1.1)string->int编码、raw编码、embstr编码
1.1.1)应用场景:计数
1.1.2)SDS
1.1.2.1)获取字符串长度的时间复杂度为O(1)
1.1.2.2)减少修改字符串时带来的内存重分配次数,因为SDS会预留一部分空间,来预防字符串修改
1.2) List ->linkedlist ziplist,
1.2.1)默认linkedlist,ziplist:所有字符串长度都小于64字节,二是元素数量小于512
1.2.2)应用场景:1、最新消息排行等功能(比如朋友圈的时间线) 2、消息队列
1.3)hash ->ziplist,hash
1.3.1)应用场景:购物车
1.4)set ->intset(数组),hashtable
1.4.1)集合对象使用intset编码需要满足两个条件:一是所有元素都是整数值;二是元素个数小于等于512个;
1.4.2)应用场景:
1、共同好友sinter,好友推荐时,根据tag求交集,大于某个阈值就可以推荐
2、利用唯一性,统计访问网站的所有独立ip
3、经常有业务出于安全性方面的考虑,需要设置用户黑名单、ip黑名单、设备黑名单等,set类型适合存储这些黑名单数据,sismember命令可用于判断用户、ip、设备是否处于黑名单之中。
1.5)Zset ->ziplist,skiplist
1.5.1)排行榜,取TopN操作 2、带权重的消息队列,3限流(滑动窗口)
1.5.2)zset包括 dict和zskiplist两个数据结构,其中dict的保存key/value,便于通过key(元素)获取score(分值)。zskiplist保存有序的元素列表,便于执行range之类的命令。
1.6)skiplist:一种有序数据结构,它通过在某个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。
1.6.1)有序集合对象使用ziplist编码需要满足两个条件:一是所有元素长度小于64字节;二是元素个数小于128个;不满足任意一条件将使用skiplist编码
https://www.cnblogs.com/MouseDong/p/11134039.html
2.1)持久化
2.1.1)AOF
2.1.1.1)优点
2.1.1.2) 缺点
2.1.2)RDB
2.1.2.1)优点
2.1.2.2)缺点
数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候。
2.1.3)全量复制:
slave初次连接master,master把runid,offset发给slave,并且bgsave生成rdb文件,中间的写命令放入缓冲区
bgsave完事后,把rdb文件发给slave,slave完事后然后发送缓冲区中的写命令
slave接收到rdb文件,然后抛弃原有的数据,加载rdb文件,完事后,执行缓冲区的写命令
此后master每写完一个命令,就会向slave发送一个写命令
2.1.4)增量复制:
如果中间出现闪断或者重连情况,恢复后,slave向master发送runid,offset
主节点检查runid与自身是否一致,如果没问题,判断offset是否在缓冲区,如果在增量,不在执行全量复制
2.1.5)同步故障问题:
rdb太大,导致全量超时,调大repl-timeout
积压缓冲区太多,主节点将直接关闭复制客户端连接,导致全量失败。调整:client-output-buffer-limit slave
slave全量复制时,对于无法容忍不一致的应用场景可以设置slave-server-stale-data no来关闭命令执行
2.2)主从
2.3)哨兵
2.3.1)sentinel状态持久化,可以安全的停止和重启sentinel进程。
2.3.2)sentinel工作方式:
2.3.2.1)sentinel每一秒向master,slave,其他sentinel发送Ping命令
2.3.2.2)如果一个实例在距上次ping回复超过了own-after-milliseconds,则认为该实例主观下线
2.3.2.3)如果是master被标记为主观下线,则监视该master的所有sentinel以每秒一次的频率确认master是否主观下线
2.3.2.4)如果有一定数量的sentinel在一定时间内确认master已经下线,则master被标记为客观下线
2.3.2.5)一般情况下,sentinel每10秒一次向master,slave发送Info命令
2.3.2.6)如果master被标记为客观下线,sentinel改为1秒一次的频率向master的slave实例发送INFO命令
2.3.2.7)如果没有足够的sentinel确认master主观下线或者master重新向sentinel发送有效PING回复,则主观下线被移除
2.3.3)三个定时任务
2.3.3.1)PING命令:心跳检测,是失败判定的依据
2.3.3.2)INFO命令:发现slave节点,确认主从
2.3.3.3)channel交换信息(pub/sub):与master进行信息交换(对节点的"看法"和自身的信息)
2.3.4) 为什么要选领导者?
简单来说,就是因为只能有一个sentinel节点去完成故障转移。
sentinel is-master-down-by-addr这个命令有两个作用,一是确认下线判定,二是进行领导者选举。
选举过程:
1)每个做主观下线的sentinel节点向其他sentinel节点发送上面那条命令,要求将它设置为领导者。
2)收到命令的sentinel节点如果还没有同意过其他的sentinel发送的命令(还未投过票),那么就会同意,否则拒绝。
3)如果该sentinel节点发现自己的票数已经过半且达到了quorum的值,就会成为领导者
4)如果这个过程出现多个sentinel成为领导者,则会等待一段时间重新选举。
2.4)集群
2.4.1)设计:Redis-cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点和其他所有节点连接
2.4.2)结构特点
2.4.2.1)所有节点彼此互联,内部使用二进制协议优化传输熟读和贷款
2.4.2.2)节点的fail通过集群中超过半数的节点检测失效时才生效
2.4.2.3)客户端与redis直连,不需要中间层,也不需要连接所有节点,而是连接任意节点就行
2.4.2.4)redis-cluster把所有的物理节点映射到[0-16383]slot(插槽)上,cluster 负责维护node<->slot<->value
2.4.2.5)redis集群预分配16384个桶,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。
2.4.3)新增节点分配
2.4.3.1)从其他节点拿出适量的hash槽分配到目标节点
2.4.3.2)在指定节点拿出适量的hash槽分配给目标节点
2.4.4)redis cluster主从模式
redis cluster 为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉
3.1)定时删除 消耗内存
3.2)惰性删除 消耗空间
3.3)定期删除 检查删除都是随机的
4)淘汰机制 LRU 最近最少使用
设置过期时间LRU
设置过期时间即将过期
设置过期时间-随机
所有-LRU
所有-随机
淘汰的数据的大小其实是和置换的大小来确定的,如果置换的数据量大,淘汰的肯定也多
6.1)setNX+getset过期时间,并且比较过期时间
缺点:A获取锁,然后redis master宕机,主从切换,可能会导致多个线程获取到同一个锁
6.2)RedLock:多个redis实例同时加锁,只要多数redis节点(一半以上)在使用,client就可以获取和释放锁
缺点:对于性能要求高的业务,实在太重太复杂,
对于完全正确的则不安全(ABCDE,ABC,CDE,C时钟前移,前一个线程锁c上过期,两个线程都能获取锁)
6.3)看门狗:自动续期,每30秒续期一次
7.1)限流
7.1.1)setNx
7.1.2) zset range 滑动窗口 比较两个时间戳内的请求数量
7.1.3)令牌桶 list
定时push 然后lpop
问题:空轮训--->blpop,需要捕获异常,重试
令牌桶可以用来保护自己,主要用来对调用者频率进行限流,为的是让自己不被打垮,应对流量突发
7.1.4)漏斗算法Redis Cell cl.throttle
如果保证别人的系统不被打垮,用漏桶算法
7.2)Geohash算法
将经纬度编码,将二维变一维,给地址位置分区的一种算法。
7.3)bitmap
7.3.1)用户签到 SETBIT key offset value
7.3.2)统计活跃用户 BITOP operation destkey key
7.3.3)用户在线状态
7.3.4)优势
7.3.4.1)占用空间小,基于bit实现的
7.3.4.2)设置时候时间复杂度O(1)、读取时候时间复杂度O(n),操作是非常快的。
7.3.4.3)二进制数据的存储,进行相关计算的时候非常快
7.3.4.4)方便扩容
7.4)HyperLogLog 基数计数-使用存储少,统计结果并不是完全精确,存在一定误差,采用分桶机制,从局部看全部
7.4.1)统计用户访问量
pfadd key 123 ,pfcount key 统计该key去重后的元素个数
pfmerge key1 key2 //将两个key合并,用于统计两个网站访问量的总大小
7.5)布隆过滤器:巧妙使用hash算法和bitmap位存储的方式,极大的节约空间
可以判断某个数据一定不存在,但是无法判断一定存在。
通过 bitmap 这种数据结构实现 Redisson guava
减少磁盘 IO 或者网络请求
布隆过滤器除了一个位数组,还有 K 个哈希函数。当一个元素加入布隆过滤器中的时候,会进行如下操作: •使用 K 个哈希函数对元素值进行 K 次计算,得到 K 个哈希值。 •根据得到的哈希值,在位数组中把对应下标的值置为 1。 举个例子,假设布隆过滤器有 3 个哈希函数:f1, f2, f3 和一个位数组 arr。现在要把 2333 插入布隆过滤器中: •对值进行三次哈希计算,得到三个值 n1, n2, n3。 •把位数组中三个元素 arr[n1], arr[n2], arr[3] 都置为 1。 当要判断一个值是否在布隆过滤器中,对元素进行三次哈希计算,得到值之后判断位数组中的每个元素是否都为 1, 如果值都为 1,那么说明这个值可能在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。
7.5.1)推荐系统给用户推荐新闻,避免重复推送。
8.1)缓存击穿
8.1.1)存在的key,在过期时刻,大量请求打到DB上,导致DB压力增大
8.1.2)解决方案:1)设置key不过期,2)加互斥锁,同一时间只有一个去请求db,其他在等待,3)get的时候,如果还差30秒过期,设置标志位,开一个线程续期 4)多级缓存,l1时间短,l2时间长,l1过期,一个线程去DB获取,其他线程访问l2
8.2)缓存穿透
8.2.1)不存在的key,大量访问,导致DB压力增大
8.2.2)解决方案:1)布隆过滤器,2)如果取不到,设置缓存为null,较短过期时间
8.3)缓存雪崩
8.3.1)大量Key同时过期,导致db崩掉
8.3.2)解决方案:1)缓存过期时间设置随机,2)热点数据永远不过期,3)如果是分布式,将热点数据均匀分布缓存数据库中
8.4)双写一致性
8.4.1)延时双删 先删除缓存->写数据库->过一秒再删除缓存
8.4.2)订阅binlog,同步到缓存,如果删除缓存失败,消息发送到消息队列,重试操作直到成功
8.5)热key
8.5.1)缓存不失效
8.5.2)多级缓存
8.5.3)布隆过滤器
8.5.4)读写分离
8.6)大key,字符串最大可存512M
8.6.1) bigkey命令(redis-cli --bigkeys),找到就干掉
如何解决大key:Mset 命令用于同时设置一个或多个 key-value 对,分散到各个实例中
8.6.2)Redis 4.0引入了memory usage命令和lazyfree机制,Lazyfree逻辑删除,删除操作交给BIO
8.7)集群(主从)脑裂解决方案:
min-slaves-to-write 3
min-slaves-max-lag 10
第一个参数表示连接master的slave节点数量,第二个参数表示slave连接到master的最大延迟时间
8.8)redis快的原因
8.8.1)基于内存实现
8.8.2)高效的数据结构
8.8.3)合理的编码结构
8.8.4)合适的线程模型
单线程处理读写
IO 多路复用处理IO,因为原来版本的redis瓶颈也在IO上
9)备用方案
9.1)主从+哨兵+cluster
9.2)ecache+Hystrix+降级+熔断+隔离
9.3)持久化
10)redis事务
redis通过MULT,EXEC,WATCH命令来实现事务,事务执行过程中,按照一定的顺序执行,并且事务执行过程中,不会中断,不会执行其他请求
watch机制基于CAS来实现的,被监视的key放在链表中,如果某个key被修改,那么REDIS_DIRTY_CAS标志将会被打开,这时服务器会拒绝执行事务。
11)hash扩容
11.1)redis中的hash扩容渐进式rehash过程
11.2)redis字典(hash表)底层有两个数组,还有一个rehashidx用来控制rehash,每次的增删改查,rehashidx+1,然后执行对应原hash表rehashidx索引位置的rehash
11.3)单线程渐进式hash过程
在扩容和收缩的时候,如果哈希字典中有很多元素,一次性将这些键全部rehash到ht[1]的话,可能会导致服务器在一段时间内停止服务。所以,采用渐进式rehash的方式,详细步骤如下:
11.3.1)为ht[1]分配空间,让字典同时持有ht[0]和ht[1]两个哈希表,ht[1]长度为ht[0]的两倍。
11.3.2)将rehashindex的值设置为0,表示rehash工作正式开始
11.3.3)在rehash期间,每次对字典执行增删改查操作是,程序除了执行指定的操作以外,还会顺带将ht[0]哈希表在rehashindex索引上的所有键值对rehash到ht[1],当rehash工作完成以后,rehashindex的值+1
11.3.4)随着字典操作的不断执行,最终会 在某一时间段上ht[0]的所有键值对都会被rehash到ht[1],这时将rehashindex的值设置为-1,表示rehash操作结束
将rehash的操作分摊在每一个的访问中,避免集中式rehash而带来的庞大计算量
11.4)
11.4.1)ConcurrentHashMap采用的扩容策略为: “多线程协同式rehash“。
扩容时大致过程如下:
线程A在扩容把数据从oldTable搬到到newTable,这时其他线程
进行get操作:这个线程知道数据存放在oldTable或是newTable中,直接取即可。
进行写操作:如果要写的桶位,已经被线程A搬运到了newTable。
那么这个线程知道正在扩容,它也一起帮着扩容,扩容完成后才进行put操作。
进行删除操作:与写一致。
12)大热Key优化方案
将整存整取的大对象,分拆为多个小对象。可以尝试将对象分拆成几个key-value, 使用multiGet获取值,这样分拆的意义在于分拆单次操作的压力,将操作压力平摊到多个redis实例中,降低对单个redis的IO影响;
13)Redis事务
13.1)Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现
13.2)
13.3)Redis 事务其他实现
基于Lua脚本,Redis可以保证脚本内的命令一次性、按顺序地执行,
| 留言与评论(共有 0 条评论) “” |