Redis
# Redis
# 数据结构
# 1.String
# 字符串类型
- 单值存储
- SET key value
- GET key
- 对象存储
- SET user:1 value(json数据)
- MSET user:1:name zhangsan user:1:age 20
- MGET user:1:name user:1:age
- 分布式锁
- 计数器
- Web集群session共享
- spring session + redis实现session共享
- 分布式系统序列号
- INCRBY orderId 1000,redis批量生成序列号提升性能
- 拿批量到jvm,宕机浪费id,无所谓
# 2.Hash
# 哈希类型,存放键值对
- key-(field-value)
- 对象缓存,避免大key,可以分段
- HMSET user 1:name zhangsan 1:age 20
- HMGET user 1:name 1:age
- 电商购物车
- 以用户id为key,商品id为field,商品数量为value
- 添加商品:hset cart:1001 10088 1
- 增加数量:hincrby cart:1001 10088 1
- 商品总数:hlen cart:1001
- 删除商品:hdel cart:1001 10088
- 获取购物车所有商品:hgetall cart:1001
- 以用户id为key,商品id为field,商品数量为value
- 优缺点
- 优点:
- 同类归类整合存储,方便管理
- 相比string消耗内存和cpu小
- 缺点:
- 过期功能不能使用在field,只能使用在key
- Redis集群架构不适合大规模使用
- 优点:
# 3.List
# 列表类型,有序集合
- 栈(Stack)
- LPUSH + LPOP
- 队列(Queue)
- LPUSH + RPOP
- 阻塞队列(BlockingQueue)
- LPUSH + BRPOP
# 4.Set
# 无序集合类型,通常用来去重,交集,并集
- 集合操作
- 交集----SINTER set1 set2
- 并集----SUNION set1 set2
- 差集----SDIFF set1 set2
- 实现关注模型
- 诸葛关注的人,zhugeSet->{guojia,xushu}
- 杨过关注的人,yangguoSet->{zhuge,baiqi,guojia,xushu}
- 郭嘉关注的人,guojiaSet->{zhuge,yangguo,baiqi,xushu,xunyu}
- 诸葛和杨过共同关注,SINTER zhugeSet yangguoSet
- 关注诸葛的人也关注杨过,SISMEMBER guojiaSet yangguo
- 可能认识的人,SDIFF yangguoSet zhugeSet->{zhuge,baiqi}
# 微信抽奖小程序
- 点击参与抽奖加入集合,SADD key userid
- 查看参与抽奖所有用户,SMEMBERS key
- 抽取count名中奖者, SRANDOMKEY key count元素还在,SPOP key count会删元素
# 微信微博点赞,收藏,标签
- 用户点赞
- SADD like:{消息id} {用户id}
- 取消点赞
- SREM like:{消息id} {用户id}
- 检查用户是否点赞
- SISMEMBER like:{消息id} {用户id}
- 获取点赞用户列表
- SMEMBERS like:{消息id}
- 获取点赞用户数量
- SCARD like:{消息id}
# 5.ZSet
# 有序集合类型,有序集合,可以关联一个分数,通常用来做排行榜
- 点击新闻
- ZINCRBY hotNews:20190819 1 守护香港
- 展示当日排行前十
- ZREVRANGE hotNews:20190819 0 9 WITHSCORES
- 七日搜索榜单计算
- ZUNIONSOURT hotNews:20190813-20190819 7
- 展示七日排行前十
- ZREVRANGE hotNews:20190813-20190819 0 9 WITHSCORES
# 6.BitMap
布隆过滤器
# 7.Geo
基于经纬度计算距离
# 8.HyperLogLog
去重,统计UV
# Redis 为什么快
# 1.单线程,避免上下文切换
# Redis为什么早期选择单线程
官方解释:https://redis.io/topics/faq 官方FAQ表示,因为Redis是基于内存的操作,CPU成为瓶颈的情况很少,Redis的瓶颈最有可能是内存的大小或网络限制。
如果想要最大程度利用cpu,可以再一台机器上启动多个Redis实例。
# Redis的多线程是怎么回事
Redis6.0之前,Redis是单线程的,6.0之后,Redis引入了多线程。 Redis的多线程是用多线程来处理数据的读写和协议解析的,但是Redis执行命令还是单线程的。

这样做的目的是因为Redis的性能瓶颈在于网络IO,而不是CPU,使用多线程能提升IO读写效率,从而整体提高Redis的性能。
# 2.基于内存操作,避免磁盘IO
# 3.使用多路I/O复用机制,非阻塞IO
# I/O多路复用
知乎高赞回答,假如你是一个老师,让30个学生解答一道题目,然后检查学生做的是否正确,你有下面几个选择:
- 1.按顺序逐个检查,先检查A,然后是B,之后是C,D。这中间如果有一个学生卡住,全班都会被耽误。这种模式就好比,你用循环挨个处理socket,根本不具备并发能力。
- 2.你创建30个分身,每个分身检查一个学生的答案是否正确。这种类似于为每一个用户创建一个进程或者线程处理连接。
- 3.你站在讲台上等,谁先解答完谁举手。这时C,D举手,表示他们解答完毕,你下去一次检查C,D的答案,然后继续回到讲台上等,此时E,A又举手,然后处理E和A。
第一种就是阻塞IO,第三种就是I/O复用。
Linux中,select,poll,epoll都是I/O复用模型。
# 4.高效的数据结构
# 数据持久化
# 1.RDB:Redis DataBase
- 默认开启
- 指定时间fork一个子进程,将内存数据dump到rdb文件中
- save 60 10000,60秒内有10000修改,写dump.rdb
- 优缺点
- 优点
- 只包含一个dump.rdb文件,方便持久化
- 容灾性好,方便备份
- 性能最大化,子进程处理写操作,主进程继续接收请求
- 数据集大时候,比AOF效率高
- 缺点
- 数据安全性低,可能丢失数据
- fork子进程,性能消耗,数据大时服务器会变慢
- 优点
# 2.AOF:Append Only File
- 日志形式记录写,删操作
- 优缺点
- 优点
- 安全性高,数据完整
- 即使中途dump机,不会破坏已同步内容,可用redis-check-aof解决一致性问题
- AOF的rewrite机制,定期对AOF文件更新,以达到压缩目的
- 缺点
- AOF比RDF体积大,恢复速度慢
- 数据集大时,比RDB效率低
- 运行效率没有RDB高
- 优点
# 3.RDB和AOF同时开启
# key过期策略
# 1.定时删除
- key过期同时设置一个定时器,过期时间到后删除
- 定时扫描,对内存友好,对cpu不友好
# 2.惰性删除
- 访问key的时候,若过期,删除,对内存不友好,对cpu友好
# 3.定期删除
- 隔一段时间,扫描一定数量的key,平衡内存和cpu
# Redis同时使用惰性和定期删除是比较好的策略
# 高可用
# 1.主从


# 2.哨兵


# 3.集群
- 16384hash槽
- 高可用,分片存储
# 缓存
# 双写不一致问题
# 现象

# 解决方案
- √加分布式锁
- 加canal,监听数据库binlog,及时修改
- 延迟双删,删一次,sleep,再删一次
# 缓存穿透
# 现象
- 自身业务代码或者数据出问题
- 缓存中没有的数据,数据库也没有,可能是恶意攻击或者误操作,造成数据库压力过大,查询不应该存在
# 解决方案
- 校验
- 缓存空值(空缓存)(过期),返回空对象(空id,而不是null)
- bitmap布隆过滤器,存在的数据hash到bitmap,不存在的则被拦截;某个值存在时,这个值可能不存在,当它说不存在,那就肯定不存在
# 缓存击穿(失效)(只穿缓存,数据库还有数据)
# 现象
- 热点key,缓存没有数据库有,一般是过期,数据库单条记录瞬时压力大
- 缓存大批同时失效
# 解决方案
- 互斥锁(DCL)
- 加随机失效时间
- 热点数据永不过期
# 缓存雪崩(同时穿缓存,数据库没有数据)
# 现象
- 娱乐热点事件
- 缓存大面积失效
# 解决方案
- 互斥锁(DCL)
- 限流,Sentinel或Hystrix或队列
- 多级缓存架构
- √ Guava
- √ EhCache
# 分布式锁
# 普通锁
- 1.SETNX
- 抛异常会执行不了,死锁
- 2.SETNX+trycath
- 宕机,死锁
- 3.SETNX+expire+trycath
- 设置key和expire原子性,系统宕机,死锁
- 4.SETNX+expire+trycath
- 原子性问题,锁超时失效,超高并发场景可能超卖
- 5.SETNX+expire+trycath+uuid
- 解锁原子性问题,在临界值删除别的线程加的锁
- 6.SETNX+expire+trycath+uuid+lua
# Redisson
# Lock
- redisson.getLock(key)
- redisson.lock()
# RedLock
客户端要收到半数以上的响应才能加锁成功
编辑 (opens new window)
上次更新: 2024/06/01, 23:33:02