Redis作为缓存中间件,被广泛应用在各类系统,用来提升系统性能和吞吐,下面总结几点开发人员在使用Redis时需要考虑的几个关键点:
一. key的设计
1. key命名规范:为了避免不必要的麻烦,我们要给系统定义一套key的设计规范。通俗点举个例子,我们在电脑上写好了一篇文章,需要保存起来,这时候我们会找个合适目录并且取个合适的文件名,以便后续要找它的时候,能想起它的名字并找到它,key的命名就好比给你要保存的文件命名和选目录,好的命名,能让你很容易想起它、找到它。大多缓存场景,是将需高频读取低频变更的数据从数据库中加载到redis,比较常用的key命名规范是:表名:主键名:主键值:存储列名,存储列名可根据下面第2点提到的粒度问题来自行定义。比如,缓存用户信息表user,set user:id:1:name 张三,缓存了用户id为1的用户的名字叫张三。
2. 粒度的把握:需要根据不同应用场景来设定。粒度越大,操作越简单,通用性好,但空间占用大,重建缓存需要的资源也越多;粒度越小,控制越复杂,通用性差,但空间占用小,重建缓存时需要的资源就越少。缓存粒度设计不当,可能会造成很多无用空间的浪费、网络带宽的浪费,也可能会造成代码通用性较差等情况,如何权衡缓存的粒度控制,需要根据实际业务提前设计好。
二. 缓存更新策略
缓存中的数据会和数据源有一段时间窗口的不一致,需要利用某些策略更新,下面介绍几种主要的缓存更新策略。
1. LRU/LFU/FIFO:剔除算法通常用于,当缓存使用量超过预设的最大值,如何对现有的数据进行剔除。
2. 超时剔除:给缓存数据设置过期时间,过期后自动删除,例如Redis提供的expire命令。如果业务可以容忍一段时间内,缓存层数据和存储层数据不一致,那么可以为其设置过期时间。在数据过期后,再从真实数据源获取数据,重新放到缓存并设置过期时间。
3. 主动更新:对数据一致性要求高,当源数据更新后,立即更新缓存,可通过MQ来实现触发。
三. 缓存穿透
缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不会命中。解决缓存穿透,一般的方法有:
1. 缓存空对象。即当某个key从缓存和db都查不到时,为这个key缓存一个空对象,这样下次来就不会击穿到db。这样做,会有2个隐患,一个是,如果被攻击,则可能造成缓存大量的空对象,导致占用大量内存,可以设置比较短的过期时间来应对。另一个隐患是,如果db新增这个key的值,就会有一段时间不一致,当然这个也是数据一致性问题,通过主动更新的策略可避免。
2. 布隆过滤器。利用布隆过滤器保存在redis中存在的key,在击穿缓存时,先查一下布隆过滤器,如果不存在,则不查db,一定程度保护了db层。
四. 热点key重建问题
这个问题是指,某个key高并发读,如果刚好碰上到期更新,会导致多个线程重建key,导致db负载过大,应用雪崩。要解决这个隐患,可以给重建key设置互斥锁,确保同一时间只有一个线程重建缓存。另外,还有一个办法就是,不设置过期时间,然后在逻辑上去控制,即逻辑上记录一个过期时间,如果到了这个过期时间,缓存还能用,只是要通知缓存重建线程去重建。
五. 查缓存要注意
1. 使用连接池来管理连接。
2. 一个业务多次查询,考虑用Pipeline,将多次查询合并为一次,虽然命令会被执行多次,但节省IO,能有效提高响应速度。
3. 多次String查询,使用mget,将多次请求合并为一次,命令也会被合并为一次,能有效提高响应速度,对于Hash内多个Field查询,使用hmget,起到和mget同样的效果。
4. Redis是单线程执行的,如果一条命令执行时间较长,其他线程在此期间会被阻塞,所以在操作Redis时要注意操作指令的涉及的数据量,尽量降低单次操作的执行耗时,比如要慎用模糊匹配。