redis理解

redis,作为简单,灵活,高效的内存数据库,被广泛使用。通过tcp直接存取,优势是速度快,并发高,缺点是数据类型有限,查询功能不强,一般用作缓存。经常用来缓存数据,存储全局共享的数据,处理排行榜等。

特点

  • 单线程,支持分布式
  • 五种数据结构(string ,hashes, list, sets, sorted sets)
  • value不提供查询,如果想查询,用一个hash来记录索引。
  • 渐进hash的hashmap(消除了大的hashmap在rehash时,需要长时等待)

数据库

默认会创建15个数据库。

typedef struct redisDb {
    dict *dict;                 /* The keyspace for this DB */
    dict *expires;              /* Timeout of keys with a timeout set */
    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP)*/
    dict *ready_keys;           /* Blocked keys that received a PUSH */
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */
    int id;                     /* Database ID */
    long long avg_ttl;          /* Average TTL, just for stats */
    list *defrag_later;         /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;

dict负责保存所有数据。

数据结构

redis提供给我们的数据结构,每种结构可能底层有多种实现方式。

  • 列表可以被编码为 ziplist 或 linkedlist,quicklist 。 ziplist 是为节约大小较小的列表空间而作的特殊表示。
  • 集合可以被编码为 intset 或者 hashtable 。 intset 是只储存数字的小集合的特殊表示。
  • 哈希表可以编码为 ziplist,zipmap 或者 hashtable 。 zipmap 是小哈希表的特殊表示。
  • 有序集合可以被编码为 ziplist 或者 skiplist 格式。 ziplist 用于表示小的有序集合,而 skiplist 则用于表示任何大小的有序集合。

ziplist 是列表和哈希。

intset(整数集合)

对象系统

在执行```set msg “hello”时,会创建两个redis对象,一个key,一个value。对象类型对应了redis提供的五种数据结构。

typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */
    int refcount;
    void *ptr;
} robj;

type表示是五种数据结构的哪种,encoding代表底层用哪种数据结构表示。每种数据结构可以有不同的底层实现,可以根据不同场景做优化。

比如type为string的对象。可以对应3中不同的底层编码实现。

set msg "hello"                         encoding为embstr,也是sds,但是和redisobject放在一起,避免一次内存分配
set msg "abcdefghijklmnopqrstuw"        超过32字符,encoding为raw,对应sds
set msg 1                               encoding为int

redis的落地方式

1. RDB 快照

全量备份。fork进程,把进程dump到磁盘中。在调用save命令,或者配置文件中指定触发方式。

2. AOF (append only file)

把所有操作保存在磁盘中。备份开销小,可以每秒或者每次同步。

实现

用c语言实现,核心就是实现一个高效的hashtable。其value是个union。提供redis的五种数据结构。union对数字的有单独区分,提供更高效的效率。

union {
    void *val;
    uint64_t u64;
    int64_t s64;
    double d;
} v;

高效的hashmap

用两个hashmap。当需要扩容的时候,扩容map2,将map1慢慢rehash到map2中。如果在rehashing。在增删改查的过程中,都会rehash一次,在服务器空闲的时候,会rehash一段时间。以此实现渐进的rehash过程。

线程模型

单线程的模式,基于event loop。网络IO和逻辑处理都在主线程处理。另外有3个BIO线程,在bio.c中定义,做持久化相关的操作。

在linux系统中,用epoll来实现异步io。用sds来做动态内存分配。

SDS(simple dynamic string)

对比c字符串的优点。

  • 不用\0作为字符串的结尾,可以存储任意二进制。
  • len字段,常量时间获取字符串长度。
  • 未使用free字段。空间预分配,惰性空间释放

问题

  1. 大key问题

    集群分片不均匀, 删除,自动过期,造成卡顿 合理设计数据结构 被动触发清理,避免出现大key

  2. 查找 用scan,不用keys。keys N的复杂度可能导致卡顿
  3. 战国上线关于腾讯云redis的问题 不支持redis订阅和发布,导致redis客服端消息错乱

配置

maxmemory-policy noeviction  #提供多种数据淘汰策略

集群

redis cluster 不用consistent hashing。而是用hash slot。

redis是异步同步write到replication,所以可能会lose write。redis cluster 提高了高可靠和弱的一致性。

Updated: