10-redis
Redis:
1、数据结构:string, hash, list, set, sorted set
2、memcache对比:
1)数据类型更丰富
2)可以持久化
问题:单线程性能优势(IO多路复用)
1)直接操作内存
2)单线程,无需上下文切换及锁开销
3)基于IO多路复用
3、持久化与性能
1)Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
2)如果数据比较重要,某个Slave开启AOF备份数据,策略设置为Always
3)为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
4)尽量避免在压力很大的主库上增加从库
5)主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3...
这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
4、集群方案
Redis 集群的键空间被分割为 16384 个槽(slot), 集群的最大节点数量也是 16384 个
主从方案
哨兵方案
集群方案
5、场景问题
5.1 缓存与数据库一致性
https://tech.imdada.cn/2017/06/30/daojia-redis/
http://ifeve.com/concurrency-cache-cross/
更新顺序:失效:从缓存中读取,没得到则从数据库取数据,成功后放到缓存;
命中:从缓存中读取,取到直接返回;
更新:先将数据保存至数据库,再让缓存失效
同步:单独同步服务,分析读取mysql binLog + 消息队列 + 处理后更新到缓存
强一致性:缓存一般不应用于强一致性场景,如金融业务
5.2 缓存穿透
1、若缓存不存在,查询数据库使用分布式锁,获取锁时再次判断缓存中是否存在
2、查询数据库,数据库中若不存在该数据,则将NULL放入缓存中(失效时间短)
3、将缓存失效时间分散开,可以在原有的失效时间基础上增加一个随机值
5.3 如果有大量的key需要设置同一时间过期,一般需要注意什么
将缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,
这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件
5.4 如何保证redis都是热点数据(考察缓存失效策略)
LRU:从已设置过期时间和从所有数据中,选择最近最少使用淘汰
Random:从已设置过期时间和从所有数据中,随机选择淘汰
即将过期:从已设置过期时间的数据中,选择将要过期的淘汰
6、应用场景
分布式session,分布式锁,队列、排行榜、计数器
7、分布式锁:
使用jedis.setnx()命令实现加锁,其中key是锁,value是锁的过期时间。
执行过程:
1. 通过setnx()方法尝试加锁,如果当前锁不存在,返回加锁成功。
2. 如果锁已经存在则获取锁的过期时间,和当前时间比较,如果锁已经过期,则设置新的过期时间,返回加锁成功。代码如下:
publicstaticbooleanwrongGetLock2(Jedis jedis, String lockKey, intexpireTime) {
longexpires = System.currentTimeMillis() + expireTime;
String expiresStr = String.valueOf(expires);
// 如果当前锁不存在,返回加锁成功
if(jedis.setnx(lockKey, expiresStr) == 1) {
returntrue;
}
// 如果锁存在,获取锁的过期时间
String currentValueStr = jedis.get(lockKey);
if(currentValueStr != null&& Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
// 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间
String oldValueStr = jedis.getSet(lockKey, expiresStr);
if(oldValueStr != null&& oldValueStr.equals(currentValueStr)) {
// 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才有权利加锁
return true;
}
}
// 其他情况,一律返回加锁失败
return false;
}
那么这段代码问题在哪里?
1. 由于是客户端自己生成过期时间,所以需要强制要求分布式下每个客户端的时间必须同步。 2. 当锁过期的时候,如果多个客户端同时执行jedis.getSet()方法,那么虽然最终只有一个客户端可以加锁,但是这个客户端的锁的过期时间可能被其他客户端覆盖。3. 锁不具备拥有者标识,即任何客户端都可以解锁。