这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记
redis缓存与数据库的不一致问题
在大多数业务场景下,我们会把Redis作为只读缓存使用。针对只读缓存来说,我们既可以先删除缓存值再更新数据库,也可以先更新数据库再删除缓存。
建议:优先使用先更新数据库再删除缓存的方法,原因主要有两个:
- 先删除缓存值再更新数据库,有可能导致请求因缓存缺失而访问数据库,给数据库带来压力;
- 如果业务应用中读取数据库和写缓存的时间不好估算,那么,延迟双删中的等待时间就不好设置。
备注:延迟双删
redis.delKey(x)
db.update(x) // 可能失败
Thread.sleep(N)
redis.delKey(x) // 延迟删除,保证其它线程可以从数据库得到最新值
缓存雪崩、缓存击穿、缓存穿透
从问题成因来看,缓存雪崩和击穿主要是因为数据不在缓存中了,而缓存穿透则是因为数据既不在缓存中,也不在数据库中。所以,缓存雪崩或击穿时,一旦数据库中的数据被再次写入到缓存后,应用又可以在缓存中快速访问数据了,数据库的压力也会相应地降低下来,而缓存穿透发生时,Redis 缓存和数据库会同时持续承受请求压力。
但是服务降级可能会让部分数据请求到错误数据(因为对于非核心数据返回的是预定义值或者是错误信息);服务熔断,会暂停整个缓存服务;请求限流,会降低系统的吞吐量。
所以,我给你的建议是,尽量使用预防式方案:
- 针对缓存雪崩,合理地设置数据过期时间,以及搭建高可靠缓存集群;
- 针对缓存击穿,在缓存访问非常频繁的热点数据时,不要设置过期时间;
- 针对缓存穿透,提前在入口前端实现恶意请求检测,或者规范数据库的数据删除操作,避免误删除。
Redis无锁的原子操作
Redis处理并发操作并没有加锁,而是使用原子操作,包括两种:
-
把多个操作写在Redis中实现为一个操作,即单命令操作。
如INCR/DECR,把读数据,数据增减,存数据三个命令变成一个。
-
多个操作写到Lua脚本,以原子性方式执行。Redis在执行Lua脚本时候,命令是原子性的。
redis-cli --eval lua.scripts keys args
Redis实现分布式锁
-
基于单节点(只有一个redis节点)
- 保证加锁和解锁的原子操作(SETNX、Lua脚本)
- 设置锁的过期时间,防止客户端崩溃导致锁无法去除。
- 给不同的客户端设置唯一标示,使得锁只能被加锁的客户端解锁。
-
基于多节点(Redis自身的Redlock算法)
- 客户端按一定顺序向不同的客户端请求加锁,当获得半数以上的锁之后,则认为其获得了分布式锁。(一定顺序减少了客户端加锁的竞态)。
Redis消息队列的实现方式
消息队列在存取消息时,必须满足3个条件:消息保序、处理重复的消息、保证消息的可靠性。
Redis可以用于实现消息队列。
Redis性能变慢的checklist
- 获取Redis实例在当前环境下的基线性能。
- 是否用了慢查询命令?如果是的话,就使用其他命令替代慢查询命令,或者把聚合计算命令放在客户端做。
- 是否对过期key设置了相同的过期时间?对于批量删除的key,可以在每个key的过期时间上加一个随机数,避免同时删除。
- 是否存在bigkey?对于bigkey的删除操作,如果你的Redis是4.0及以上的版本,可以直接利用异步线程机制减少主线程阻塞;如果是Redis 4.0以前的版本,可以使用SCAN命令迭代删除;对于bigkey的集合查询和聚合操作,可以使用SCAN命令在客户端完成。
- Redis AOF配置级别是什么?业务层面是否的确需要这一可靠性级别?如果我们需要高性能,同时也允许数据丢失,可以将配置项no-appendfsync-on-rewrite 设置为yes,避免AOF重写和fsync竞争磁盘IO资源,导致Redis延迟增加。当然,如果既 需要高性能又需要高可靠性,最好使用高速固态盘作为AOF日志的写入盘。
- Redis实例的内存使用是否过大?发生swap了吗?如果是的话,就增加机器内存,或者是使用Redis集群,分摊单机Redis的键值对数量和内存压力。同时,要避免出现Redis和其他内存需求大的应用共享机器的情况。
- 在Redis实例的运行环境中,是否启用了透明大页机制?如果是的话,直接关闭内存大页机制就行了。
- 是否运行了Redis 主从集群?如果是的话,把主库实例的数据量大小控制在2~4GB,以免主从复制时,从库因加载大的RDB文件而阻塞。
- 是否使用了多核CPU或NUMA架构的机器运行Redis实例?使用多核CPU时,可以给Redis实例绑定物理核;使用NUMA架构时,注意把Redis实例和网络中断处理程序运行在同一个CPU Socket上。
HyperLogLog
HyperLogLog常用于大数据量的统计,比如页面访问量统计或者用户访问量统计
场景:统计一个页面的访问量UV(用户多次访问算一次),数据量较小的时候可以使用SET(具有去重功能,但是需要频繁创建SET对象)。HyperLogLog可以优化统计计算。
bitmap
位图。占用空间少。
场景:用户在线状态;用户活跃状态;用户签到;