华为架构师整理Redis数据结构的大厂最佳实践

1 概述数据结构和内部编码

华为架构师整理Redis数据结构的大厂最佳实践

文章插图
 
无传统关系型数据库的 Table 模型schema 所对应的db仅以编号区分 。同一 db 内,key 作为顶层模型,它的值是扁平化的 。即 db 就是key的命名空间 。
key的定义通常以 : 分隔,如:Article:Count:1
常用的redis数据类型有:string、list、set、map、sorted-set
华为架构师整理Redis数据结构的大厂最佳实践

文章插图
 
redisObject通用结构Redis中的所有value 都是以object 的形式存在的,其通用结构如下
华为架构师整理Redis数据结构的大厂最佳实践

文章插图
 
  • type 数据类型
    指 string、list 等类型
  • encoding 编码方式
    指的是这些结构化类型具体的实现方式,同一个类型可以有多种实现 。e.g. string 可以用int 来实现,也可以使用char[] 来实现;list 可以用ziplist 或者链表来实现
  • lru
    本对象的空转时长,用于有限内存下长时间不访问的对象清理
  • refcount
    对象引用计数,用于GC
  • ptr 数据指针
    指向以 encoding 方式实现这个对象实际实现者的地址 。如:string 对象对应的SDS地址(string的数据结构/简单动态字符串)
单线程
华为架构师整理Redis数据结构的大厂最佳实践

文章插图
 
单线程为何这么快?
  • 纯内存
  • 非阻塞I/O
  • 避免线程切换和竞态消耗
  • 一次只运行一条命令
  • 拒绝长(慢)命令
    keys, flushall, flushdb, slow lua script, mutil/exec, operate big value(collection)
  • 其实不是单线程
    fysnc file descriptorclose file descriptor
2 stringRedis中的 string 可表示很多语义
  • 字节串(bits)
  • 整数
  • 浮点数
redis会根据具体的场景完成自动转换,并根据需要选取底层的实现方式 。
例如整数可以由32-bit/64-bit、有符号/无符号承载,以适应不同场景对值域的要求 。
  • 字符串键值结构,也能是 JSON 串或 XML 结构
内存结构在Redis内部,string的内部以 int、SDS(简单动态字符串 simple dynamic string)作为存储结构
  • int 用来存放整型
  • SDS 用来存放字节/字符和浮点型SDS结构
SDStypedef struct sdshdr {// buf中已经占用的字符长度unsigned int len;// buf中剩余可用的字符长度unsigned int free;// 数据空间char buf[];}
  • 结构图
    存储的内容为“Redis”,Redis采用类似C语言的存储方法,使用’’结尾(仅是定界符) 。SDS的free 空间大小为0,当free > 0时,buf中的free 区域的引入提升了SDS对字符串的处理性能,可以减少处理过程中的内存申请和释放次数 。
buf 的扩容与缩容当对SDS 进行操作时,如果超出了容量 。SDS会对其进行扩容,触发条件如下:
  • 字节串初始化时,buf的大小 = len + 1,即加上定界符’’刚好用完所有空间
  • 当对串的操作后小于1M时,扩容后的buf 大小 = 业务串预期长度 * 2 + 1,也就是扩大2倍 。
  • 对于大小 > 1M的长串,buf总是留出 1M的 free空间,即2倍扩容,但是free最大为 1M 。
字节串与字符串SDS中存储的内容可以是ASCII 字符串,也可以是字节串 。由于SDS通过len 字段来确定业务串的长度,因此业务串可以存储非文本内容 。对于字符串的场景,buf[len] 作为业务串结尾的’’ 又可以复用C的已有字符串函数 。
SDS编码的优化value 在内存中有2个部分:redisObject和ptr指向的字节串部分 。
在创建时,通常要分别为2个部分申请内存,但是对于小字节串,可以一次性申请 。
incr userid:pageview (单线程:无竞争) 。缓存视频的基本信息(数据源在MySQL)
华为架构师整理Redis数据结构的大厂最佳实践

文章插图
 
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)):} }}
华为架构师整理Redis数据结构的大厂最佳实践

文章插图
 


推荐阅读