1 概述
数据结构和内部编码
无传统关系型数据库的 Table 模型
schema 所对应的db仅以编号区分。同一 db 内,key 作为顶层模型,它的值是扁平化的。即 db 就是key的命名空间。
key的定义通常以:分隔,如:Article:Count:1
常用的Redis数据类型有:string、list、set、map、sorted-set
redisObject通用结构
Redis中的所有value 都是以object 的形式存在的,其通用结构如下
- type 数据类型
指 string、list 等类型 - encoding 编码方式
指的是这些结构化类型具体的实现方式,同一个类型可以有多种实现。e.g. string 可以用int 来实现,也可以使用char[] 来实现;list 可以用ziplist 或者链表来实现 - lru
本对象的空转时长,用于有限内存下长时间不访问的对象清理 - refcount
对象引用计数,用于GC - ptr 数据指针
指向以 encoding 方式实现这个对象实际实现者的地址。如:string 对象对应的SDS地址(string的数据结构/简单动态字符串)
单线程
单线程为何这么快?
- 纯内存
- 非阻塞I/O
- 避免线程切换和竞态消耗
- 一次只运行一条命令
- 拒绝长(慢)命令
keys, flushall, flushdb, slow lua script, mutil/exec, operate big value(collection) - 其实不是单线程
fysnc file descriptor
close file descriptor
2 string
Redis中的 string 可表示很多语义
- 字节串(bits)
- 整数
- 浮点数
redis会根据具体的场景完成自动转换,并根据需要选取底层的实现方式。
例如整数可以由32-bit/64-bit、有符号/无符号承载,以适应不同场景对值域的要求。
- 字符串键值结构,也能是 JSON 串或 XML 结构
内存结构
在Redis内部,string的内部以 int、SDS(简单动态字符串 simple dynamic string)作为存储结构
- int 用来存放整型
- SDS 用来存放字节/字符和浮点型SDS结构
SDS
typedef struct sdshdr { // buf中已经占用的字符长度 unsigned int len; // buf中剩余可用的字符长度 unsigned int free; // 数据空间 char buf[]; }
- 结构图
存储的内容为“Redis”,Redis采用类似C语言的存储方法,使用’\0’结尾(仅是定界符)。
SDS的free 空间大小为0,当free > 0时,buf中的free 区域的引入提升了SDS对字符串的处理性能,可以减少处理过程中的内存申请和释放次数。
buf 的扩容与缩容
- 当对SDS 进行操作时,如果超出了容量。SDS会对其进行扩容,触发条件如下:
- 字节串初始化时,buf的大小 = len + 1,即加上定界符’\0’刚好用完所有空间
- 当对串的操作后小于1M时,扩容后的buf 大小 = 业务串预期长度 * 2 + 1,也就是扩大2倍。
- 对于大小 > 1M的长串,buf总是留出 1M的 free空间,即2倍扩容,但是free最大为 1M。
字节串与字符串
SDS中存储的内容可以是ASCII 字符串,也可以是字节串。由于SDS通过len 字段来确定业务串的长度,因此业务串可以存储非文本内容。对于字符串的场景,buf[len] 作为业务串结尾的’\0’ 又可以复用C的已有字符串函数。
SDS编码的优化
value 在内存中有2个部分:redisObject和ptr指向的字节串部分。
在创建时,通常要分别为2个部分申请内存,但是对于小字节串,可以一次性申请。
incr userid:pageview (单线程:无竞争)。缓存视频的基本信息(数据源在MySQL)
public VideoInfo get(Long id) { String redisKey = redisPrefix + id; VideoInfo videoInfo e redis.get(redisKey); if (videoInfo == null) { videoInfo = mysql.get(id); if (videoInfo != null) { // 序列化 redis.set(redisKey serialize(videoInfo)): } } }
除此之外,string 类型的value还有一些CAS的原子操作,如:get、set、set value nx(如果不存在就设置)、set value xx(如果存在就设置)。
String 类型是二进制安全的,也就是说在Redis中String类型可以包含各种数据,比如一张JPEG图片或者是一个序列化的Ruby对象。一个String类型的值最大长度可以是512M。
在Redis中String有很多有趣的用法
- 把String当做原子计数器,这可以使用INCR家族中的命令来实现:INCR, DECR, INCRBY。
- 使用APPEND命令来给一个String追加内容。
- 把String当做一个随机访问的向量(Vector),这可以使用GETRANGE和 SETRANGE命令来实现
- 使用GETBIT 和SETBIT方法,在一个很小的空间中编码大量的数据,或者创建一个基于Redis的Bloom Filter 算法。