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字段。空间预分配,惰性空间释放
问题
-
大key问题
集群分片不均匀, 删除,自动过期,造成卡顿 合理设计数据结构 被动触发清理,避免出现大key
- 查找 用scan,不用keys。keys N的复杂度可能导致卡顿
- 战国上线关于腾讯云redis的问题 不支持redis订阅和发布,导致redis客服端消息错乱
配置
maxmemory-policy noeviction #提供多种数据淘汰策略
集群
redis cluster 不用consistent hashing。而是用hash slot。
redis是异步同步write到replication,所以可能会lose write。redis cluster 提高了高可靠和弱的一致性。