安装
主从
复制延时
master挂掉需要人工干预升级从节点为主节点,每个包含完整数据
哨兵
哨兵是一个特殊服务不参与数据操作,可以部署Sentinel为一个集群,一个哨兵发现Master故障,会与集群中其他Sentinel确认,确认过半则下线Master。哨兵只监控主节点。会执行switch-master操作。下线的节点同步完成后会重现上线。
集群
数据分区,支持主从复制、故障转移
每个master会带一个salve
主节点:负责读写请i去和集群信息维护
从节点:只进行主节点数据和状态信息的复制
cluster-enable yes
环形hash槽位 16384
数据结构
String
key - string
get
set key value 单值缓存
set user:1 ["name":"roy", "balance":"1999" ] // 对象缓存
mget
mset user:1:name roy user:1:balance 1888 能保证原子性
append
incr:自增,可以用来分配key保证不冲突
分布式锁
SETNX product:10001 true // 返回1代表获取锁成功,返回0代表失败,TTL
DEL product:10001 // 多次获取删一次就可以
TTL product:10001
EXPIRE product:10001 10 // 设置过期时间防止阻塞
getset k1 v6 // 获取并重新设置
Hash
key - {field, value}
HSET h1 f1 v1 f2 v2 f3 v3
HGET h1 f1
HSETNX h1 f4 v4
购物车
-
以用户id为key
-
商品id为field
-
商品数量为value
购物车操作:
-
添加商品 setcart:1001 10088 1
-
增加数量:hincriby cart:1001 10088 1
-
商品总数:hlen cart:1001
-
删除商品:hdel cart:1001 10088
-
获取购物车所有商品 hgetall:cart:1001
有点:
-
同类数据归类存储,方便管理
-
相比string操作消耗内存 cpu更小
-
相比string存储更省空间
缺点:
-
过期功能不用用在field上 只能用在key
-
集群架构不适合大规模使用,可能有大key问题
List
key - [a][b][c][d]
数据结构
Stack 栈 = LPUSH + LPOP
Queue = LPUSH + RPOP
Blocking MQ = LPUSH + BRPOP
使用场景
视频列表、签到列表
排队机
简化版MQ
Action
LPUSH //左推
RPUSH //右推
LPOP
RPOP
LRANGE
BLPOP
BRPOP
一个list容量时2的32次方-1个元素,大约40亿,注意大key的问题
list底层时双端列表,对双端操作效率高,不适合对下标操作
Set
key - (a,b,c,d) 无顺序 无重复
SADD
SREM 删
SCARD 数量
SISMEMBERS 查元素
SRANDMEMBER 随机查看一个 不会拿出
SPOP
Set 运算
SINTER 交集
SINTERSTORE
SUNION 并集
SDIFF 差集
SUNIONSTORE
使用场景
1 加入抽奖资格
SADD key {userID}
2 查看有哪些用户
SMEMBERS key
3 抽取count名中奖者
SPOP key [count]
ZSet
有排序Set,每个元素增加score
key - ([score,value],[score2,v2])
ZSCORE z1 c 查看分数
ZINCRBY z1 5 a 给a加5分
ZCOUNT z1 70 90 查看70-90分的元素
ZRANGE z1
使用场景
热点新闻
ZINCRBY hotNews:20180819 1 守护香港
展示当日排行前十
ZREVRANGE hotNews:20190813 0 ( WITHSCORES
七日搜索榜单计算
ZUNIONSTORE 后台News:20190813-20190819 7
Bitmap
key - [0][1][0][0][0]
SETBIT b1 10 1
SETBIT b1 5 1
BITCOUNT b1
BITCOUNT b1 0 9 bit
可以用于操作String
GETBIT k1 5
使用场景
每日签到
SETBIT dailycheck:1 100 1 用户1第100天签到
BITCOUNT dailycheck:1 统计用户1的签到次数
BITPOS dailycheck:1 统计用户1第一天签到的时间
优点
快速、高效、节省空间
Hyperloglog
统计一个集合中不重复的元素个数.大集合 会有错误率0.8%
pfadd h1 a b c d e
PFCOUNT h1
使用场景
统计UV 不同的独立访客
PFADD visitlog 192.168.1.1 xxx
PFCOUNT visitlog
Geo
地理空间
Stream
Redis版的MQ -- 阻塞队列 + pub/sub
Redis是单线程还是多线程
客户端多线程,服务端单线程
如何保证原子性
- 使用复合指令:
GETSET、SETNX、SETEX
- 使用事务:
-
MULTI 开启事务
-
set k1 1
-
incr k1
-
get k1
-
EXEC 执行事务
-
WATCH key 某个key被修改则事务执行会失败
-
报错、有问题不会回滚。需要事务前备份才能回滚
- 管道:
--pipe
redis-cli -a 123qqq --pipe | cat cmd.txt
- lua
Lua
eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
redis.call('get',KEYS[1])
redis.call('set',KEYS[1], b)
redis.register_function()
SCRIPT load 将脚本存到服务器(不建议)
EVALSHA asdad213sd21we123 0
BigKey问题
指哪些占用空间非常大的key,容易造成Redis的服务阻塞。200w个元素或存了一篇文章
redis-cli
--bigkeys
--memkeys
总结
Redis的瓶颈在于内存而不是cpu所以核心部分以单线程为主。
Redis压测
redis-benchmark -a 123qweasd -t set -n 1000000 -c 20 一百万数量20个客户端压测
吞吐量10w qps
Redis持久化
-
完全关闭持久化
-
RDB 一定时间快照,快,紧凑,不能实时,可能会丢数据,占内存,fork子线程刷盘,
-
AOF append only file: 文件大,性能慢,记录的是执行操作可以用来恢复,
建议RDB + AOF
RDB
RDB核心配置save操作:定时,定次,
save会阻塞
bgsave后台执行
LASTSAVE 显示上一次save的时间
AOF
appendonly yes开启
appendfilename 文件名
appenddirname 文件路径
appendfsync everysec 每秒记录
生成文件
appendonly.aof.2.base.rdb 全量备份
appendonly.aof.2.incr.aof 增量操作
appendonly.aof.mainfest 前两个文件的元数据
Replica
配从不配主,对主节点没有影响。
info replicatin 查看信息
从节点默认只读,可以修改为可写。可以禁用配置rename-command CONFIG ""
SLAVEOF no one, 断开从节点,
set k2 v2
SLAVEOF192.168.1.1 6379 重新配置成6379的从节点
数据出现不一致,会自动将slave数据覆盖。
主从复制工作流程
-
Slave启动后,向master发送一个sync,建立成功后slave会删除自己的数据日志,等待主节点同步。
-
master接到sync请求后会触发一次RDB。同时收集所有修改数据的指令。然后master将RDB和操作指令全量同步给Slave
-
主从关系建立后,master会定期向slave发送心跳包,确认状态默认10s
-
master会持续将指令传递给slave并记录offset
5.如果slave短暂不回复master的心跳,master就会停止向slave同步数据,知道slave重新上线,master从offset开始继续向slave同步数据。
优缺点
-
同步有延迟
-
主节点挂掉,从节点不会自动切换
哨兵集群
主节点挂掉会自动选择从节点升级为主节点
replica-priority 100 优先选级别更小的为主节点
集群模式

数据倾斜:
user:k1 -> user{k1} 计算时会根据{}内容计算hash分配
mset需要所有key必须在要给slot上
mset user:{1}:name rror user:{1}:password 123 这样就可以将用户1的数据都放到同一个节点,能保证一致性
gossip协议:ping,pong,meet,fail,集群内同步协议,不保证强一致,默认使用服务端口+10000
总结 数据安全方案
单机 RDB-AOF
Replica 主从
Sentinel 监控
cluster 分担压力
Redis作为分布式锁
案例 秒杀减库存场景
- 并发问题
1)
-
setIfAbsent(lockKey,"xx"); // 获取锁,异常情况下,可能锁无法释放
-
expire(lockKey, 10, TimeUnit.SECONDS) // 设置超时时间
2)
- setIfAbsent(lockKey, "xx", 10, TimeUnit.SECONDS); // 高并发依然有超卖问题,可能被别人解锁
3)
-
setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS);
-
finally{
clientId.equals(stringRedisTemplate.opsForValue().get(lockKey)){
string.RedisTemplate.delete(lockKey); // 判断与释放不是原子性操作,如果锁刚好在判断后过期,则会删掉别人的锁
}
}
4)可以锁续命(redisson、jedis等框架提供额外操作)
RLock redissonLock = redisson.getLock(lockKey);
redissonLock.lock()
redissonLock.unLock()
- 用lua脚本中原子实现比较和set减库存
等待线程会订阅channel
释放锁的时候发一个消息
6)集群架构下分布式锁
主刚刚设置锁挂掉没同步到从节点
zk过半选举
redission重入锁:加两次也要减两次
Redis缓存架构
-
缓存要设置过期时间,查到的数据做缓存超时的延期,冷门数据自动过期删除。
-
缓存失效(击穿):大批量缓存在同一时间失效,导致大量请求打到数据库。解决办法:超时时间随机。
-
缓存穿透:
-
将商品全部误删,缓存没有,数据库也没有,请求对缓存和数据库压力都很大。
-
使用不存在的商品id发送请求进行攻击。
解决办法:当数据库查询为空时,给缓存也设置一个空缓存。空缓存也设置过期时间,否则会大量空缓存。
布隆过滤器:注意布隆过滤器不能删数据 只能重新初始化
- 突发数据热点
-
分布式锁DCL
-
查缓存
-
加锁
-
查缓存
-
查数据库更新缓存
- 双写不一致
-
加分布式锁,写缓存的时候不允许其他人读
-
延迟双删,删除后sleep一点时间后再删除一次,防止被其他线程错误的更新缓存
-
使用canal通过监听数据库binlog来更新缓存,不需要业务代码更新缓存
- 读多写少
-
读写锁(写写有并发,写读有并发,读读没并发)
-
tryLock 大量线程重建缓存,重建也比较快,用tryLock没有锁竞争。但如果限定时间内没有重建完成,则失败。
- 缓存雪崩------明星热点事件
-
大量请求到redis,redis扛不住。
-
限流
-
多级缓存,自己维护一个Map做缓存防止Redis崩溃时无法服务。
设计规范
- key名设计
业务名为前缀防止冲突,用冒号分隔
trade:order:1
不包含空格、换行、引号
- value设计
拒绝bigkey 字符串大于512MB,value集合不超过5000个元素
非字符串不要使用del删除,使用hscan\sscan\zscan,删除也是耗时操作
网络堵塞,
过期删除,过期异步删除
产生bingkey的原因:粉丝列表,统计类用户集合,缓存类数据库load出来都放到redis
如何优化:拆,不要一次取出所有db数据,删除也不要一次删除所有hmget代替hgetall
-
控制key的生命周期
-
线上禁止使用keys, flushall, flushdb, 通过redis rename机制禁掉,或者使用scan的方式渐进处理
-
使用批量操作mget, mset,使用pipeline提高效率
连接池优化
maxTotal:
Redis并发量
不超过redis的maxclient默认10000,nodes(应用个数) * maxTotal不超过 maxclient
资源开销:maxTotal=maxIdle
连接池预热,redis连接池初始化时没有任何链接。循环创建连接并ping()注意在循环外close()确保能够创建链接。
- Key的删除策略
-
被动删除,key过期时触发惰性删除策略,可能get的时候检查是否过期
-
主动删除,定期淘汰过期的key
-
当前内存超过maxmemory,触发主动清除策略
主动清除策略
a) 过期时间的key
-
volatile-ttl: 根据过期时间
-
volatile-random
-
volatile-lru:最近最少使用 Least Recently Used
-
volatile-lfu:最不经常使用 Least Frequently Used 热点数据使用LFU可能更好
b) 对所有key做处理(没空间了):
-
allkeys-random:
-
allkeys-lru
-
allkeys-lfu
c) 不处理
- noevication: 不会提出任何数据但拒绝写入 OOM command not allow
JDHotkey
多级缓存一般时本地一个HashMap做缓存
优点
减少网络请求,提高性能
分布式系统中,天然分布式缓存
减少远程缓存的读压力
缺点
进程空间大小有限,不支持大数据量
重启程序会丢失
分布式场景下,系统之间不一致
和远程缓存不一致
什么场景使用多级缓存
热点商品详情页
热搜
热点帖子
热门用户主页
热点探测服务原理
什么是热点:有限时间,流量高聚集
有预期的热点,提前进行扩容、降级、缓存
无预期的热点,攻击、爬虫、突发新闻
根据滑动窗口计算key的热点次数