13518219792

建站动态

根据您的个性需求进行定制 先人一步 抢占小程序红利时代

大厂面试!我和面试官之间关于Redis的一场对弈!

今天,我不自量力的面试了某大厂的java开发岗位,迎面走来一位风尘仆仆的中年男子,手里拿着屏幕还亮着的mac,他冲着我礼貌的笑了笑,然后说了句“不好意思,让你久等了”,然后示意我坐下,说:“我们开始吧。看了你的简历,觉得你对redis应该掌握的不错,我们今天就来讨论下redis......”。我想:“来就来,兵来将挡水来土掩”。

10年积累的网站设计、成都网站设计经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站制作后付款的网站建设流程,更有平桥免费网站建设让你可以放心的选择与我们合作。

Redis是什么

五种数据类型

数据类型应用场景总结

类型 简介 特性 场景
string(字符串) 二进制安全 可以包含任何数据,比如jpg图片或者序列化对象 ---
Hash(字典) 键值对集合,即编程语言中的map类型 适合存储对象,并且可以像数据库中的update一个属性一样只修改某一项属性值 存储、读取、修改用户属性
List(列表) 链表(双向链表) 增删快,提供了操作某一元素的api 最新消息排行;消息队列
set(集合) hash表实现,元素不重复 添加、删除、查找的复杂度都是O(1),提供了求交集、并集、差集的操作 共同好友;利用唯一性,统计访问网站的所有Ip
sorted set(有序集合) 将set中的元素增加一个权重参数score,元素按score有序排列 数据插入集合时,已经进行了天然排序 排行榜;带权重的消息队列

Redis缓存

 
 
 
 
  1.  
  2.      
  3.         org.springframework.boot 
  4.         spring-boot-starter-data-redis 
  5.      
  6.      
  7.         org.apache.commons 
  8.         commons-pool2 
  9.      
  10.      
  11.         org.springframework.boot 
  12.         spring-boot-starter-web 
  13.      
  14.      
  15.         org.springframework.session 
  16.         spring-session-data-redis 
  17.      
  18.      
  19.         org.projectlombok 
  20.         lombok 
  21.         true 
  22.      
  23.      
  24.         org.springframework.boot 
  25.         spring-boot-starter-test 
  26.         test 
  27.      
 
 
 
 
  1. server: 
  2.   port: 8082 
  3.   servlet: 
  4.     session: 
  5.       timeout: 30ms 
  6. spring: 
  7.   cache: 
  8.     type: redis 
  9.   redis: 
  10.     host: 127.0.0.1 
  11.     port: 6379 
  12.     password: 
  13.     # redis默认情况下有16个分片,这里配置具体使用的分片,默认为0 
  14.     database: 0 
  15.     lettuce: 
  16.       pool: 
  17.         # 连接池最大连接数(使用负数表示没有限制),默认8 
  18.         max-active: 100

创建实体类User.java

 
 
 
 
  1. public class User implements Serializable{ 
  2.     private static final long serialVersionUID = 662692455422902539L; 
  3.     private Integer id; 
  4.     private String name; 
  5.     private Integer age; 
  6.     public User() { 
  7.     } 
  8.     public User(Integer id, String name, Integer age) { 
  9.         this.id = id; 
  10.         this.name = name; 
  11.         this.age = age; 
  12.     } 
  13.     public Integer getId() { 
  14.         return id; 
  15.     } 
  16.     public void setId(Integer id) { 
  17.         this.id = id; 
  18.     } 
  19.     public String getName() { 
  20.         return name; 
  21.     } 
  22.     public void setName(String name) { 
  23.         this.name = name; 
  24.     } 
  25.     public Integer getAge() { 
  26.         return age; 
  27.     } 
  28.     public void setAge(Integer age) { 
  29.         this.age = age; 
  30.     } 
  31.     @Override 
  32.     public String toString() { 
  33.         return "User{" + 
  34.                 "id=" + id + 
  35.                 ", name='" + name + '\'' + 
  36.                 ", age=" + age +
  37.                  '}'; 
  38.     } 
  39. }

RedisTemplate的使用方式

默认情况下的模板只能支持RedisTemplate,也就是只能存入字符串,所以自定义模板很有必要。添加配置类RedisCacheConfig.java

 
 
 
 
  1. @Configuration 
  2. @AutoConfigureAfter(RedisAutoConfiguration.class) 
  3. public class RedisCacheConfig { 
  4.     @Bean 
  5.     public RedisTemplate redisCacheTemplate(LettuceConnectionFactory connectionFactory) { 
  6.         RedisTemplate template = new RedisTemplate<>(); 
  7.         template.setKeySerializer(new StringRedisSerializer()); 
  8.         template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); 
  9.         template.setConnectionFactory(connectionFactory); 
  10.         return template; 
  11.     } 
  12. }

测试类

 
 
 
 
  1. @RestController 
  2. @RequestMapping("/user") 
  3. public class UserController { 
  4.     public static Logger logger = LogManager.getLogger(UserController.class); 
  5.     @Autowired 
  6.     private StringRedisTemplate stringRedisTemplate; 
  7.     @Autowired 
  8.     private RedisTemplate redisCacheTemplate;
  9.     @RequestMapping("/test") 
  10.     public void test() { 
  11.         redisCacheTemplate.opsForValue().set("userkey", new User(1, "张三", 25)); 
  12.         User user = (User) redisCacheTemplate.opsForValue().get("userkey"); 
  13.         logger.info("当前获取对象:{}", user.toString()); 
  14.     }

然后在浏览器访问,观察后台日志 http://localhost:8082/user/test

使用spring cache集成redis

spring cache具备很好的灵活性,不仅能够使用SPEL(spring expression language)来定义缓存的key和各种condition,还提供了开箱即用的缓存临时存储方案,也支持和主流的专业缓存如EhCache、Redis、Guava的集成。定义接口UserService.java

 
 
 
 
  1. public interface UserService { 
  2.     User save(User user); 
  3.     void delete(int id); 
  4.     User get(Integer id); 
  5. }

接口实现类UserServiceImpl.java

 
 
 
 
  1. @Service 
  2. public class UserServiceImpl implements UserService{ 
  3.     public static Logger logger = LogManager.getLogger(UserServiceImpl.class); 
  4.     private static Map userMap = new HashMap<>(); 
  5.     static { 
  6.         userMap.put(1, new User(1, "肖战", 25)); 
  7.         userMap.put(2, new User(2, "王一博", 26)); 
  8.         userMap.put(3, new User(3, "杨紫", 24)); 
  9.     } 
  10.     @CachePut(value ="user", key = "#user.id") 
  11.     @Override 
  12.     public User save(User user) { 
  13.         userMap.put(user.getId(), user); 
  14.         logger.info("进入save方法,当前存储对象:{}", user.toString()); 
  15.         return user; 
  16.     } 
  17.     @CacheEvict(value="user", key = "#id") 
  18.     @Override 
  19.     public void delete(int id) { 
  20.         userMap.remove(id); 
  21.         logger.info("进入delete方法,删除成功"); 
  22.     }
  23.     @Cacheable(value = "user", key = "#id") 
  24.     @Override 
  25.     public User get(Integer id) { 
  26.         logger.info("进入get方法,当前获取对象:{}", userMap.get(id)==null?null:userMap.get(id).toString()); 
  27.         return userMap.get(id); 
  28.     } 
  29. }

为了方便演示数据库的操作,这里直接定义了一个Map userMap,这里的核心是三个注解@Cachable、@CachePut和@CacheEvict。测试类:UserController

 
 
 
 
  1. @RestController 
  2. @RequestMapping("/user") 
  3. public class UserController { 
  4.     public static Logger logger = LogManager.getLogger(UserController.class); 
  5.     @Autowired 
  6.     private StringRedisTemplate stringRedisTemplate; 
  7.     @Autowired 
  8.     private RedisTemplate redisCacheTemplate; 
  9.     @Autowired 
  10.     private UserService userService; 
  11.     @RequestMapping("/test") 
  12.     public void test() { 
  13.         redisCacheTemplate.opsForValue().set("userkey", new User(1, "张三", 25)); 
  14.         User user = (User) redisCacheTemplate.opsForValue().get("userkey"); 
  15.         logger.info("当前获取对象:{}", user.toString()); 
  16.     }  
  17.     @RequestMapping("/add") 
  18.     public void add() { 
  19.         User user = userService.save(new User(4, "李现", 30)); 
  20.         logger.info("添加的用户信息:{}",user.toString()); 
  21.     } 
  22.     @RequestMapping("/delete") 
  23.     public void delete() { 
  24.         userService.delete(4); 
  25.     } 
  26.     @RequestMapping("/get/{id}") 
  27.     public void get(@PathVariable("id") String idStr) throws Exception{ 
  28.         if (StringUtils.isBlank(idStr)) { 
  29.             throw new Exception("id为空"); 
  30.         } 
  31.         Integer id = Integer.parseInt(idStr); 
  32.         User user = userService.get(id); 
  33.         logger.info("获取的用户信息:{}",user.toString()); 
  34.     } 
  35. }

用缓存要注意,启动类要加上一个注解开启缓存

 
 
 
 
  1. @SpringBootApplication(exclude=DataSourceAutoConfiguration.class) 
  2. @EnableCaching 
  3. public class Application { 
  4.     public static void main(String[] args) { 
  5.         SpringApplication.run(Application.class, args); 
  6.     } 
  7. }

1、先调用添加接口:http://localhost:8082/user/add

2、再调用查询接口,查询id=4的用户信息:

可以看出,这里已经从缓存中获取数据了,因为上一步add方法已经把id=4的用户数据放入了redis缓存 3、调用删除方法,删除id=4的用户信息,同时清除缓存

3、调用删除方法,删除id=4的用户信息,同时清除缓存

4、再次调用查询接口,查询id=4的用户信息:

没有了缓存,所以进入了get方法,从userMap中获取。

缓存注解

1、@Cacheable 根据方法的请求参数对其结果进行缓存

2、@CachePut 根据方法的请求参数对其结果进行缓存,和@Cacheable不同的是,它每次都会触发真实方法的调用。参数描述见上。

3、@CacheEvict 根据条件对缓存进行清空

缓存问题

 
 
 
 
  1. setRedis(key, value, time+Math.random()*10000);

如果Redis是集群部署,将热点数据均匀分布在不同的Redis库中也能避免全部失效。或者设置热点数据永不过期,有更新操作就更新缓存就好了(比如运维更新了首页商品,那你刷下缓存就好了,不要设置过期时间),电商首页的数据也可以用这个操作,保险。

 
 
 
 
  1. public static String getData(String key) throws InterruptedException { 
  2.         //从Redis查询数据 
  3.         String result = getDataByKV(key); 
  4.         //参数校验 
  5.         if (StringUtils.isBlank(result)) { 
  6.             try { 
  7.                 //获得锁 
  8.                 if (reenLock.tryLock()) { 
  9.                     //去数据库查询 
  10.                     result = getDataByDB(key); 
  11.                     //校验 
  12.                     if (StringUtils.isNotBlank(result)) { 
  13.                         //插进缓存 
  14.                         setDataToKV(key, result); 
  15.                     } 
  16.                 } else { 
  17.                     //睡一会再拿 
  18.                     Thread.sleep(100L); 
  19.                     result = getData(key); 
  20.                 } 
  21.             } finally { 
  22.                 //释放锁 
  23.                 reenLock.unlock(); 
  24.             } 
  25.         } 
  26.         return result;
  27.      }

Redis为何这么快

Redis和Memcached的区别

淘汰策略

策略 描述
volatile-lru 从已设置过期时间的KV集中优先对最近最少使用(less recently used)的数据淘汰
volitile-ttl 从已设置过期时间的KV集中优先对剩余时间短(time to live)的数据淘汰
volitile-random 从已设置过期时间的KV集中随机选择数据淘汰
allkeys-lru 从所有KV集中优先对最近最少使用(less recently used)的数据淘汰
allKeys-random 从所有KV集中随机选择数据淘汰
noeviction 不淘汰策略,若超过最大内存,返回错误信息

补充一下:Redis4.0加入了LFU(least frequency use)淘汰策略,包括volatile-lfu和allkeys-lfu,通过统计访问频率,将访问频率最少,即最不经常使用的KV淘汰。

持久化

 
 
 
 
  1. appendfsync yes 
  2. appendfsync always     #每次有数据修改发生时都会写入AOF文件。 
  3. appendfsync everysec   #每秒钟同步一次,该策略为AOF的缺省策略。

AOF可以做到全程持久化,只需要在配置中开启 appendonly yes。这样redis每执行一个修改数据的命令,都会把它添加到AOF文件中,当redis重启时,将会读取AOF文件进行重放,恢复到redis关闭前的最后时刻。

主从复制

其他资讯

让你的专属顾问为你服务