redis的一些总结

使用 redis 有哪些好处

  1. 速度快:因为数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是 O (1)。
  2. 支持丰富数据类型:支持 string,list,set,sorted set,hash。
  3. 支持事务:可以一次执行多个命令。失败不会回滚,会继续执行。
  4. 丰富的特性:可用于缓存,消息,按 key 设置过期时间,过期后将会自动删除

字符串常用操作

常用的命令有9个,数据结构用的是SDS(Simple Dynamic String), 简单动态字符串 SDS 与 C 字符串的区别: sds记录了字符串的长度信息,SDS 是二进制安全的,储存二进制文件(如图片、音频,视频等文件的二进制数据) ,C 字符串容易缓冲区溢出,而 SDS 的空间分配策略则不会。当需要操作字符串时,会先检测空间大小,如果不满足,则需要对空间做扩展

List操作

常用的命令有7个,可以模拟队列和栈结构 列表在 3.2 之前的版本是使用 ziplist 和 linkedlist 进行实现的。在 3.2 之后的版本就是引入了 quicklist。 linkedlist 和 quicklist 的底层实现是采用链表进行实现,Redis 实现了自己的链表结构。

Hash 哈希表

常用的命令有9个,增删查改,获取长度,HLEN Hash 对象的实现方式有两种分别是 ziplist、hashtable。 字典类型的底层就是 hashtable 实现的,明白了字典的底层实现原理也就是明白了 hashtable 的实现原理

Set 集合

常用的命令有8个,和list一样有pop操作,增删查改的元素都是有的、获取range元素这些基本操作都是有的,还有自己单独的求交集、并集、差集的操作,就那三个单词啊 Set 的底层实现是 HashTable 和 intset,Redis 中列表和集合都可以用来存储字符串,但是 Set 是不可重复的集合,而 List 列表可以存储相同的字符串。

ZSet 有序集合

常用的命令有7个,z开头,既然是有序的,那肯定可以通过正序倒叙来获取了REVRANGE和range,还有操作分数的命令score ziplist和skiplist都有可能 在通过 ZADD 命令添加第一个元素时, 通过检查输入的第一个元素来决定该创建什么编码的有序集

底层数据结构就这三种:

sds hashtable(intset) ziplist(linkedlist/skiplist)

命令总结:

string和List命令没有前缀,hash/set/zset有前缀; 都有通用的增删查改,删除有del/rem/pop,增加有set/add,获取有get/push,汇总有card/members/len/range

Redis 缓存三大问题

  1. 缓存穿透: 指查询一条数据库和缓存都没有的一条数据,就会一直查询数据库,对数据库的访问压力就会增大 缓存穿透的解决方案:缓存空对象(代码维护较简单,但是效果不好)、布隆过滤器(代码维护复杂,效果很好)。 布隆过滤器是一种基于概率的数据结构,主要用来判断某个元素是否在集合内,一个非常大的二进制位数组 (数组里只有 0 和 1),空间效率和查询效率高,若干个哈希函数
  2. 缓存击穿 大并发集中对这一个 key 进行访问,当这个 key 在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,瞬间对数据库的访问压力增大。 缓存击穿这里强调的是并发,造成缓存击穿的原因有以下两个: 该数据没有人查询过 ,第一次就大并发的访问。(冷门数据)添加到了缓存, reids 有设置数据失效的时间,这条数据刚好失效,大并发访问(热点数据) 对于缓存击穿的解决方案就是加锁,普遍做法是根据 key 获取 value 值为空时锁上,从数据库中 load 数据后再释放锁。若其它线程获取锁失败,则等待一段时间后重试;
  3. 缓存雪崩# 缓存雪崩是指在某一个时间段,缓存集中过期失效。此刻无数的请求直接绕开缓存,直接请求数据库。 造成缓存雪崩的原因,有两种:reids 宕机、大部分数据失效 解决方案是:避免缓存集中过期,过期时间上加个随机字符串
  4. 总结:都是大并发的时候没有查到缓存,造成数据库压力。缓存击穿和缓存雪崩都是key过期了,不同之处是单个和多个;

Redis 数据的5种淘汰策略

voltile-lru 在设置了过期时间的数据中,淘汰最近最少使用的数据 voltile-ttl 淘汰将设置了过期时间的数据,ttl 大的优先淘汰 (即最接近过期的) voltile-random 随机淘汰设置了过期时间的数据 allkeys-lru 淘汰最近最少使用的数据 allkeys-random 任意选择淘汰 no-eviction 禁止淘汰,当内存不足时写入数据,会返回错误(系统默认)

LRU和LFU

LRU(Least recently used,最近最少使用) LFU(Least Frequently Used,最近被访问的频率) LRU的优点:LRU相比于 LFU 而言性能更好一些,因为它算法相对比较简单,不需要记录访问频次,可以更好的应对突发流量。

Redis key 的过期策略

定时过期:每个设置了过期时间的 key 都创建一个定时器,到期自动清除。(内存友好,CPU 不友好) 惰性过期:只有在访问一个 key 时,才判断 key 是否已经过期。(CPU 友好,内存不友好) 定期过期:每隔一定时间,扫描一定数量的设置了过期时间的数据集,然后清除 文档

redis的为啥这么快总结:

纯内存操作、单线程无上下文切换的损耗、io多路复用、内核多数据结构做了优化

Redis是典型的事件驱动程序,时间处理流程很重要;事件分为两大类:文件事件和时间事件;

redis 中 list 和 set 的区别?

list:有序,元素可重复,可用作队列 set:无序,元素唯一不可重复,可用于去重 这个问题可以去掉 “ redis 中”,类似于 LinkedList 和 HastSet 的区别。

redis所有操作总结:

  1. 分布式锁(悲观)、watch(乐观) 一把锁被两个客户端同时持有,不安全性由此产生,这种不安全也仅仅是在主从发生 failover 的情况下才会产生。用Redlock 队列:list、延时队列set,PubSub、stream 位图
  2. HyperLogLog、布隆过滤器(bf.add,bf.exists) #空间上还能节省 90% 以上,只是稍微有那么点不精确; 布隆过滤器的应用:2、在爬虫系统中,对 URL 进行去重,2、新闻客户端推荐系统如何实现推送去重

简单限流,zset的value存毫秒时间戳(保证唯一性即可),score 来圈出这个时间窗口来; GeoHash(geoadd) 4、事务(multi/exec/discard),Pipeline Redis 的事务不能算「原子性」,只满足了事务的「隔离性」,当前执行的事务有着不被其它事务打断的权利; 在执行事务时都会结合 pipeline 一起使用,这样可以将多次 IO 操作压缩为单次 IO 操作

运维相关(11):

Scan、info、线程io模型、通信协议、持久化、命令行工具、主从同步、Sentinel、集群、保护redis服务、Redis安全通信 Redis是单线程,Node.js 和 Nginx 也是; Redis 单线程处理那么多的并发客户端连接的原因:多路复用, select 系列的事件轮询 API,非阻塞 IO。

跳跃表

新节点和各层索引节点逐一比较,确定原链表的插入位置。O(logN) 把索引插入到原链表。O(1) 利用抛硬币的随机方式,决定新节点是否提升为上一级索引。结果为“正”则提升并继续抛硬币,结果为“负”则停止。O(logN)

自上而下,查找第一次出现节点的索引,并逐层找到每一层对应的节点。O(logN) 删除每一层查找到的节点,如果该层只剩下1个节点,删除整个一层(原链表除外)。O(logN) 总体上,跳跃表删除操作的时间复杂度是O(logN)

总体上,跳跃表插入操作的时间复杂度是O(logN),而这种数据结构所占空间是2N,既空间复杂度是 O(N)。 Redis当中的Sorted-set

ziplist(压缩列表)

是一组连续内存块组成的顺序的数据结构,能够节省空间(所以称为压缩列表)。压缩列表中使用多个节点来存储数据(header/nodes/mark)

字典dict:

Redis 的数据库使用字典作为底层实现的,通过 key 和 value 的键值对形式,代表了数据库中全部数据。而且,所有对数据库的增删查改的命令都是建立在对字典的操作上。 dict 结构内部包含两个 hashtable,通常情况下只有一个 hashtable 是有值的。但是在 dict 扩容缩容时会用到旧的 hashtable; hash 冲突时:通过不同的 key 通过计算得到同一个 index,就会形成单向链表(链地址法) rehash:当 hash 表中的存放的键值对不断的增加或者减少时,需要对 hash 表进行一个扩展或者收缩; 渐进式 rehash:如果 hash 结构很大,一次完整 rehash 的过程就会耗时很长。采用了渐进式 rehash ,它会同时保留两个新旧 hash 结构,在后续的定时任务以及 hash 结构的读写指令中将旧结构的元素逐渐迁移到新的结构中。这样就可以避免因扩容导致的线程卡顿现象。

版权

本作品采用 CC BY-NC-ND 4.0 授权,转载必须注明作者和本文链接。