版本升级
- 3.0开始支持cluster集群模式;
- 4.0开发的lazyfree和PSYNC2解决了Redis长久的大key删除阻塞问题(详见lazyfree)及同步中断无法续传的问题;
- 5.0新增了stream数据结构使Redis具备功能完整的轻量级消息队列能力;
- 6.0更是发布了诸多企业级特性如threaded-io、TLS和ACL等,大幅提升了Redis的性能和安全性。
Value类型
我们都知道 Redis 提供了丰富的数据类型,常见的有五种:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合) 。
随着 Redis 版本的更新,后面又支持了四种数据类型: BitMap(2.2 版新增)、HyperLogLog(2.8 版新增)、GEO(3.2 版新增)、Stream(5.0 版新增) 。
Redis集群部署模式
- 主从模式
- 哨兵模式: 一主多从+多个哨兵 关键配置项:slaveof主redis的ip,readonly从redis设置,主从切换需要n个哨兵认为master失效(客观下线),然后进行主从切换(选出领头哨兵;通过优先级、同步偏移量、run id选出主数据库A;升级A为主数据库,并通知其他哨兵更新主数据库。)
- 分布式存储
- Redis Cluster模式 采用hash slot分片(16384个slot,每个Key通过CRC16校验后对16384取模),P2P模式(至少3个节点,推荐3主3从),完全去中心化
- 豌豆荚的Codis(与Redis Cluster模式类似)
- Twitter的Twemproxy
- 客户端分片(不易维护)
Redis事务和lua脚本
要解决的问题
缓存穿透
数据不存在
数据不存在,无法缓存到Redis(可能是黑客攻击),一直访问数据库,造成数据库负载过高。 解决办法:
正常访问
- 对空值给null缓存,设置短过期时间(一般不超过5分钟)
网络攻击
- 设置白名单:使用bitmaps包含正常的用户id,非正常用户id则过滤
- 布隆过滤器:类似白名单,效率更高,但命中率不准确
- 实时监控:找到命中率特别低的ip,设置黑名单过滤
缓存击穿
数据存在,但因缓存过程比较长,缓存过期后,刚好有大量访问,此时都访问不到缓存,出现大量访问都去数据库取数据,造成数据库负载过高。
防止大量Key同时过期
- 根据热度动态调整过期时间
- 分页缓存减少加载时间(一般用户不会一直翻页)
- 爬虫搞鬼:识别爬虫禁止(会影响SEO和推广)或监控应对
防止热数据突然大量访问
- 热数据提前加载,增加过期时长
- 使用分布式锁,同一时间只有 1 个请求可以访问到数据库:第1个检测到值没有缓存的加锁,其他请求检测到锁后等待一段时间后重复调用。
缓存雪崩
- 使用分布式锁(可能不适合高并发)
- 后台更新:将无缓存数据的缓存过程变为消息通知的形式,后台在缓存前对重复缓存进行过滤
- 在缓存过期前,异步触发主动更新缓存
- 设置随机失效时间,避免大量key在同一时间失效
- 多级缓存(缺点比较大)
缓存热点
明星微博可能某天短时间上千万用户访问,可缓存多份,通过在key里加上分区号,将用户进行分区访问某一份缓存。
锁的误释放
设置锁时加了超时自动解锁,等程序反应过来后可能会解了别人的锁(锁的key相同) 解决办法: set lockkey server-uuid nx ex 10 程序先检测锁是自己的uuid,再解锁。这里存在操作原子性问题,所以应改成在redis的lua脚本中检测+删除。
其他问题
持久化
redis本身就是缓存,一般不需要完全的持久化,即使丢一部分数据,通过mysql读过来也没有太大问题,所以一般AOF的appendfsync选择everysec或no(no依赖系统策略,可能是30秒)
| 类型 | 优点 | 缺点 |
|---|---|---|
| RDB | 磁盘占用小,恢复速度快 | 最后一段时间的数据可能丢失 |
| AOF | 磁盘占用大,恢复速度慢 | 数据不丢(appendfsync 3种模式:everysec,always,no) |
| RDB-AOF混合(v4.0开始) | 恢复速度比RDB稍慢,但有限 | 数据不丢 |
Value类型
| 类型 | 操作 | 特殊操作 | 使用场景 |
|---|---|---|---|
| String | set、get、exists、strlen、del、mset(批量设置)、incr、decr、incrby(增加指定值) | expire、ttl、setex(同时设置值和过期时间)、setnx(不存在就插入) | 缓存对象(整个和分离2种)、常规计数、分布式锁(使用setnx加锁、lua解锁)、共享Session信息(支持42亿条) |
| List(双链) | lpush/rpush/lpop/rpop/lrange | blpop/brpop | 消息队列,但有严重缺陷 |
| Hash(KV集合) | hset/hget/hmset/hmget/hdel/hlen/hgetall/hincrby | --- | 缓存对象、购物车 |
| Set(无序并唯一的KV集合) | sadd/srem(删除)/smenbers(获取整个集合)/scard(获取集合元素个数)/sismenber(元素是否存在集合中)/srandmenber(从集合中随机选出n个元素)/spop | sinter(交集)/sinterstore(交集并存到新集合中)/sunion/sunionstore/sdiff/sdiffstore | 可存42亿条、点赞、共同关注、抽奖活动 |
| Zset | --- | 排行榜、电话姓名排序 | |
| GEO | GEOPOS(从给定的key里返回所有指定名称的位置)/GEODIST(返回两个给定位置之间的距离)/GEORADIUS(根据给定坐标获取指定范围的地理位置集合) | --- | 查找附近的地理位置 |
| BigMap | --- | --- | 统计签到、判断用户登录态(5kw用户只需要6M)、连续签到用户数 |
| HyperLogLog | --- | --- | 百万级网页UV计数(误差标准差是0.81%) |
| Stream | --- | --- | 同一消息可以被多个组消费。缺点:1、但持久化和主从复制都是异步的可能会丢数据 2、因Redis使用的是内存,消息堆积量有限 |