前言
Redis是一种K/V形式的存在于内存中数据存储,可用作数据库,缓存和消息代理。Redis的关键命令的执行是单线程的,所以通常我们将Redis认为是单线程模型。
特点:
- 丰富的数据结构
- 一秒十万的QPS,贼猛
- 分布式的,很方便的进行水平扩容
- 通过Redis Sentinel和Redis Cluster自动分区提供高可用性
- 事物、不同的磁盘持久化策略、Lua脚本
一 数据结构
redis命令大全:redisdoc.com/
1.1 String
由上图可知,dictEntry里面存放kv,v又存放在redisObject里面,里面又存放编码、长度、内容等。
- String存储的数据类型int、float、string。
- String是一个二进制安全字符串。
- SDS(简单动态字符串),redis并没有使用char原因有二。其一,在char中存储的字符串是以/0结尾。字符串可以是任意的数据,当存储字节数据时,/0是不能轻易满足的。其二,char不能高效的支持长度计算和追加操作,在redis中这又是相当频繁的。
存储编码:
- int,存储八个字节的长整型
- enbstr,存贮占用比较小的字符串,只读,分配的内存空间连续,查找更加方便。但是由于扩容不方便,所以当enbstr编码的字符串被修改时,会变成raw编码。
- raw,分配的内存空间不连续,需要分配两次。redisObject和SDS各一次。
三种编码可以进行向上转型。虽然存在三种编码,但使用较多的还是raw编码。
应用场景:热点数据、分布式session、锁、incr 全局id、incr 计数器、incr 限流、位操作。
1.2 Hash
两种数据结构:ziplist、hashtable
ziplist:一种特殊的双向链表,而是存贮的上一个元素的长度。在key小于512或者value小于64kb时,默认使用此数据结构。
hashtable:数组+链表的数据结构,链表用来处理hash碰撞的问题。
扩容:当链表长度达到5,redis则会选择扩容。当然这个值可以根据业务进行调配
扩容方式:单线程渐进式扩容,也就是一个线程进行扩容,其他线程依旧可以进行并发读写。定义两个hash表分开操作。扩容过程中通过锁保证数据一致性。
1、扩容时读操作:先从旧表里面找,然后从新表里面找。
2、扩容时写操作:直接写入新表。
缩容:当存储key的数量小于容量的10%时,进行缩容。
应用场景:聚合场景
1.3 List
数据结构:quicklist(快速链表)
quicklist是双向链表和压缩列表的结合体。在默认情况下不进行压缩,每个节点默认存储8kb的值。
quicklist是一种对时间和空间同时妥协的设计方案。双向链表,存放相邻节点的的地址,空间不连续,内存花销比较大,对内存精打细算的redis显然不适合这种方案。压缩列表,存放节点的长度,占用内存跟小,一段连续的存储空间,新增节点内存花销小,但是修改会重新分配内存,当链表很长时,重新分配内存相当不划算。
quicklist采用在双向链表里存放压缩链表,通过控制压缩链表的长度,来调节时间和空间之间的平衡。
1.4 set和zset
set是一个无序的集合,如果存放的值是整数,则使用intset结构存储,否则使用hashtable存放。如果存放的值过多也会使用hashtable存放。intset实质就是一个数组,由于数组的不可变性,过长的数组非常损耗性能
zset是一个有序的集合,每个元素都有一个score,用来进行排序,数据过多之后,采用skipList(跳表)进行存储。