redis其实不是单线程,有如下几个线程
redis-server
: 命令处理,网络事件的监听;bio_close_file
:异步关闭大文件;bio_aof_fsync
:异步aof刷盘bio_lazy_free
:异步清理大块内存io_thd_*
:io多线程jemalloc_bg_thd
:jemalloc后台线程redis单线程的含义是指命令处理在一个单独的线程中。
redis是kv数据库,v支持丰富的数据结构(string、list、hash、set、zset),加锁复杂,锁粒度不好控制;
多线程频繁的cpu上下文切换,抵消多线程的优势。
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2]; //ht[0]是原来的hashtable,ht[1]是扩容或缩容后的hashtable
long rehashidx;
unsigned long iterators;
}dict;
typedef struct dictht {
dictEntry **table;
unsigned long size; //数组长度,必须为 2^n
unsigned long sizemask; //数组长度-1,用于将hash(key) % size优化为 hash(key) & sizemask
unsigned long used; //实际存储的元素个数
} dictht;
负载因子 = u s e d / s i z e used / size used/size, 负载因子越大冲突越大,复杂度可能由 O ( 1 ) O(1) O(1)变为 O ( n ) O(n) O(n)
扩容
缩容
扩容和缩容会导致 rehash
缩容条件没有设置为负载因子 < 1是为什么?
防止频繁缩容,会造成内存操作的频繁,会造成io密集。
scan cursor [MATCH pattern] [COUNT count] [TYPE type]
使用方法:
_dictRehashStep()
dictRehashMilliseconds(dict *d, int ms)
定时处理跳表(多层级有序链表)结构用来实现有序集合;
鉴于 redis 需要实现 zrange
以及 zrevrange
功能;需要节点间最好能直接相连并且增删改操作后结构依然有序;
B+ 树时间复杂度为 h ∗ O ( log 2 n ) h * O(\log_2n) h∗O(log2n),由于其分裂操作导致
时间复杂度:有序数组通过二分查找能有 O ( log 2 n ) O(\log_2n) O(log2n)时间复杂度;平衡二叉树也能获得 O ( log 2 n ) O(\log_2n) O(log2n)时间复杂度
每隔一个节点生成一个层级节点;模拟二叉树结构,以此达到搜索时间复杂度为
O
(
log
2
n
)
O(\log_2n)
O(log2n) ;
这是一个空间换时间的结构;
但是如果对理想跳表结构进行删除增加操作,很有可能改变跳表结构;如果重构理想结构,将是巨大的运算;考虑用概率的方法来进行优化;从每一个节点出发,每增加一个节点都有 1 2 \frac{1}{2} 21的概率增加一个层级, 1 4 \frac{1}{4} 41 的概率增加两个层级, 1 8 \frac{1}{8} 81的概率增加3 个层级,以此类推;经过证明,当数据量足够大(256)时,通过概率构造的跳表趋向于理想跳表,并且此时如果删除节点,无需重构跳表结构,此时依然趋向于理想跳表;此时时间复杂度为 ( 1 − 1 n c ) O ( log 2 n ) (1-\frac{1}{n^c})O(\log_2n) (1−nc1)O(log2n);
个人理解:跳表通过增加层级,让链表也能够执行二分查找。
#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough
for 2^64 elements */
#define ZSKIPLIST_P 0.25 /* Skiplist P = 1/4
*/
/* ZSETs use a specialized version of Skiplists
*/
typedef struct zskiplistNode {
sds ele;
double score; // WRN: score 只能是浮点数
struct zskiplistNode *backward;
struct zskiplistLevel {
struct zskiplistNode *forward;
unsigned long span; // 用于 zrank
} level[]; //柔性数组,不算空间大小,free时不需要考虑level
} zskiplistNode;
typedef struct zskiplist {
struct zskiplistNode *header, *tail;
unsigned long length; // zcard
int level; // 最高层
} zskiplist;
typedef struct zset {
dict *dict; // 帮助快速索引到节点
zskiplist *zsl;
} zset;
方便范围查询,通过 O ( log 2 n ) O(\log_2n) O(log2n)的时间复杂度快速找到边界,然后在最底层找到范围内所有元素)
用于优化网络io密集的问题
send
和encode
的步骤read
和decode
send
和encode
compute
)是在主线程中完成的。