Redis相关知识汇总(一)

首先普及一下前置知识:

  1. 常用的数据库有3种,
    • 关系型 如 Oracle,MySQL,Microsoft SQL Server,PostgreSQL
    • 内存型 如 Redis, Memcached
    • document 如 MongoDB
  2. 内存存储和磁盘存储的区别, 都知道内存比磁盘快, 那么具体快多少, 下面给出对比资料(大体的对比)

    寻址时间:启动位置到达所要求的读/写位置所经历的全部时间。
    带宽: 传输数据的时候数据流的速度

    • 寻址时间单位
      • 普通硬盘 ms
      • 固态硬盘 us
      • 内存 ns
    • 传输带宽
      • 机械硬盘 100多Mb/s
      • 固态硬盘 500Mb左右/s
      • 主流ddr3 内存6Gb/s
    • 存储方式
      • 磁盘: 链式存储
      • 内存:线性存储
  3. 为什么会出现缓存
    1. 世界上有全部使用内存的数据库引擎SAP HANA, 但是成本特别大
    2. 使用磁盘存储, 受限于io, 速度比较慢
    3. 即使使用全内存数据库, 也有网络IO的限制
    4. 使用缓存是一种折中的体现
    5. 如果能解决磁盘IO和网络IO的情况问题, 那可能改变现在计算机环境
    PS:目前的计算机环境
    1. 冯诺依曼体系的硬件(未来是量子计算机)
    2. 以太网,tcp/ip的网络

redis基础整理
  1. 安装
  2. 请求处理, 优先使用epoll, 降级select
  3. 存储结构

在这里插入图片描述
如图:
1. redis默认16个db
2. 每个db指向一个dist对象
3. 每个dict对象维护2个dictht, 第一个是使用的, 第二个是重构的
4. dictht 对象维护N个 dictEntry 对象
5. dictEntry 对象维护 redis的key和value

dictEntry 的*key 指向了 键值的key; *v 指向了键值的value, next指向了下一个 dictEntry

/*
* 哈希表节点
*/
typedef struct dictEntry {
   // 键
   void *key;
   // 值
   union {
       void *val;
       uint64_t u64;
       int64_t s64;
   } v;
   // 指向下个哈希表节点,形成链表
   struct dictEntry *next;

} dictEntry;

dictht 采用 (MurmurHash)[http://calvin1978.blogcn.com/articles/murmur.html]和链表 维护了dictEntry,假设 dictht.table 创建了一个长度为4的dictEntry, 如图(演示了维护的过程):
在这里插入图片描述

/*
* 哈希表
*
* 每个字典都使用两个哈希表,从而实现渐进式 rehash 。
*/
typedef struct dictht {
   
   // 哈希表数组
   dictEntry **table;
   // 哈希表大小
   unsigned long size;
   // 哈希表大小掩码,用于计算索引值
   // 总是等于 size - 1
   unsigned long sizemask;
   // 该哈希表已有节点的数量
   unsigned long used;

} dictht;

当数据越来越多,size只有4个,但是dictEntry 有几万个或者更多的时候,很显然上面那样子查询会变得很慢, 所以dict 就开始维护 dictht对象, 维护方式如图:

/*
* 字典
*/
typedef struct dict {

   // 类型特定函数
   dictType *type;
   // 私有数据
   void *privdata;
   // 哈希表
   dictht ht[2];
   // rehash 索引
   // 当 rehash 不在进行时,值为 -1
   int rehashidx; /* rehashing not in progress if rehashidx == -1 */
   // 目前正在运行的安全迭代器的数量
   int iterators; /* number of iterators currently running */

} dict;

下面是一些默认的函数, 有兴趣可以看下

//initServer
server.db[j].dict = dictCreate(&dbDictType,NULL);
server.db[j].expires = dictCreate(&keyptrDictType,NULL);
server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL);
server.db[j].ready_keys = dictCreate(&objectKeyPointerValueDictType,NULL);
server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);

dictType dbDictType = {
    dictSdsHash,                /* hash function */
    NULL,                       /* key dup */
    NULL,                       /* val dup */
    dictSdsKeyCompare,          /* key compare */
    dictSdsDestructor,          /* key destructor */
    dictObjectDestructor   /* val destructor */
};

dictType keyptrDictType = {
    dictSdsHash,                /* hash function */
    NULL,                       /* key dup */
    NULL,                       /* val dup */
    dictSdsKeyCompare,          /* key compare */
    NULL,                       /* key destructor */
    NULL                        /* val destructor */
};

dictType keylistDictType = {
    dictObjHash,                /* hash function */
    NULL,                       /* key dup */
    NULL,                       /* val dup */
    dictObjKeyCompare,          /* key compare */
    dictObjectDestructor,       /* key destructor */
    dictListDestructor          /* val destructor */
};

dictType objectKeyPointerValueDictType = {
    dictEncObjHash,            /* hash function */
    NULL,                      /* key dup */
    NULL,                      /* val dup */
    dictEncObjKeyCompare,      /* key compare */
    dictObjectDestructor,      /* key destructor */
    NULL                       /* val destructor */
};

在这里插入图片描述

上图只是简略的介绍了这个过程, 实际情况更复杂一些

  1. ht[0]=ht[1] 这个叫做 rehash
  2. rehash 的条件是 负载因子((ht[0].userd / ht[0].size) >= 1或者5) 或者 负载因子<0.1
    • 服务器目前没有执行bgsave或bgrewriteaof命令,并且哈希表的负载因子>=1
    • 服务器目前正在执行bgsave或bgrewriteaof命令,并且哈希表的负载因子>=5
  3. 扩展和收缩计算
    • 若是扩展操作,那么ht[1]的大小为>=ht[0].used*2的2^n
    • 若是收缩操作,那么ht[1]的大小为>=ht[0].used的2^n
  4. rehash 会带来额外的 io负载, 所以redis采用了渐进式rehash, 简单解释:就是先建立好 ht[1], 然后dictEntry的迁移是 在 对ht[0]的增删查改的过程中逐渐迁移到 ht[1]。
  5. 网 传 k e y 越 大 , 然 后 查 询 越 慢 , 其 实 从 上 面 的 结 构 就 可 以 看 出 来 , 这 句 话 要 这 么 理 解 \color{red} 网传key越大,然后查询越慢, 其实从上面的结构就可以看出来,这句话要这么理解 key
    k e y 的 大 小 主 要 影 响 查 询 的 主 要 问 题 是 h a s h 的 计 算 , k e y 越 大 , h a s h 越 慢 , 得 到 h a s h 后 的 查 询 是 不 慢 的 \color{red} key的大小主要影响查询的主要问题是hash的计算,key越大,hash越慢,得到hash后的查询是不慢的 keyhashkeyhashhash
  6. 想了解更多, 请看《Redis设计与实现(第二版)》
  7. 美团针对Redis Rehash机制的探索和实践
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值