3. 可能的风险点
[toc]
雪崩
假设一个场景, 某个时刻下流量到达高峰, 一下进驻一批key, 然后设置了一个统一的过期时间, 等到了过期时刻一到, 就会有相当一批key集体过期, 此时所有的请求都会统一打到数据库上, Redis有跟没有一个样, 很容易把数据库打挂
给每个Key设置相对随机一些的过期时间, 不要扎堆过期
把Key分散到集群里的不同实例上, 分散一下压力
对于超级热点, 比如首页, 可以直接设置成不过期, 如果要更新直接重新set覆盖
给数据库带上一个限流, 即使真的没数据也不要把数据库打挂了
击穿
击穿是指对于某个超级热点数据, 访问量特别大, 失效的那一瞬间, 所有的请求都落到库上了, 一下就会把库打挂, 还是那样, 对于超级热点数据, 直接设置缓存不过期就好了
穿透
穿透有点刻意搞事的意思, 比如我请求一个id为-1的数据, 这种情况下Redis里肯定没有, 你就会去MySQL里查, 目的就达到了, 然后我找一大堆肉鸡要你去查id为-1的数据, 每个请求你都要查库, 很容易数据库就又挂了
在服务层做一个校验, 对于这种明显不符合要求的数据, 直接return
对于不存在的数据, 我们同样可以在Redis里缓存, 值为null, 这样第二次请求的时候就不会再查MySQL了
如果嫌设置一大堆kv浪费内存, 或者你也可以使用布隆过滤器, 只要过滤器说不存在那就一定不存在
不一致
我们都知道Redis跟MySQL的使用逻辑是: 读的时候先读Redis, 如果没有的话就查库, 查到了以后放在Redis里, 而写或者更新数据的时候先更新数据库, 再删缓存, (这里涉及两个问题):
为什么是删缓存而不是更新缓存: 如果你需要缓存的内容是一个简单的东西, 那没问题, 但如果你要缓存的内容是一个复杂的, 需要几次联表才能得出的数据, 大可不必现场更新, 反正你也不知道你会不会, 以及什么时候用, 用的时候再说就好了
不一致问题: 如果你更新库没问题, 但是删缓存的时候失败了, Redis里的还是老数据, 这种情况下就导致了Redis跟MySQL不一致的现象出现了. 解决问题的方法是返过来, 先删缓存再更新数据库:
如果删缓存失败, 直接整体失败
如果删缓存成功, 但是数据库更新失败, 那只会造成数据库里是老数据, 同时Redis里是空的, 并不会出现数据不一致的现象
更加复杂的不一致现象
上面的问题并不保全, 只是相对好一些. 设想一种情况, 缓存已经删了, 现在准备去更新数据库了, 此时, 另一个人想来读了, 他发现Redis里没有就直接查库, 查完Set回来了, 好了, 你白删了, 数据又不一致了
如果真的出现了这么大的竞争力, 可以尝试这么做: 你在更新完库以后有一条删除缓存的命令, 同时还有一条更新缓存的命令, 要去对着Redis执行. 出现问题的点在于顺序乱了:
使用一个redis-list, 更新缓存的操作全都从这个list里面pop出来, 或者更进一步的, 因为重复set/del两个写操作有些冗余, 如果发现了队列里有人已经在尝试写这个key, 可以直接更新成当前这个命令
使用一个redis-setnx, 对外界说明, 我现在正在尝试刷新这个key, 请不要抢
并发竞争
假设有三个人同时想要先更新库, 于是首先生成了线程1, 用于处理事务1, 随后生成了线程2, 用于处理事务2, 最后生成事务3, 用于处理事务3, 很巧的是这三个人在刷新MySQL库以后全都需要刷新缓存. 此时可能会出现#1的确是先更新库更新成功了, 但因为网络问题, 没能第一个刷新缓存, 反而是#2率先刷新了缓存
执行顺序乱序的问题: 设置分布式锁, #1在操作这个资源的时候#2不许动
更新覆盖的问题: 还是上面那个问题, #1作为最优先的线程却是第二个到访Redis的, 我们可以给MySQL表加上更新时间字段, 并把这个字段放在Redis-key下的value里, 更新的时候看一下, 如果时间戳比自己的跟新, 说明自己已经不用去设置了
写进去的数据怎么没了
因为过期了? 你设置的Key都是有过期时间的, 到期以后与想象中不同, 并不是立刻从内存里删掉, 有两种方式:
定期删除: Redis每隔100毫秒就会Poll一批key出来看看有没有过期, 过期就删
惰性删除: 过期了不管, 而是到要读的时候才掏出来看看是不是过期了
也可能没过期只是被淘汰了吧, 一旦内存快满了就要走内存淘汰机制了, 淘汰谁呢?
[all] lru: 所有Key都按照LRU的方式来淘汰
[volatile] lru: 只是带超时的key走LRU
[all] random: 从所有Key里随机掏一些出来删
[volatile] random: 从带超时的key里随机掏一些出来删
[volatile] ttl: 筛选出一批快要超时的key出来删
以上走x-LRU可以让Redis里面尽量都用来存热点数据
Last updated
Was this helpful?