redis-----01-----redis介绍(redis安装下载、底层存储结构原理剖析)

这篇文章的一些图参考了这篇文章:你真的懂Redis的5种基本数据结构吗?这些知识点或许你还需要看看(图文并茂,浅显易懂,建议收藏)

1 Redis介绍

  • Redis 是Remote Dictionary Service 的简称;也是远程字典服务。
  • Redis 是内存数据库,KV数据库,数据结构(string, list, hash, set, zset)数据库。
  • Redis 应用非常广泛,如Twitter、暴雪娱乐、Github、Stack Overflow、腾讯、阿里巴巴、京
    东、华为、新浪微博等,很多中小型公司也在使用。

Redis命令查看:http://redis.cn/commands.html。

2 安装编译

git clone https://gitee.com/mirrors/redis.git -b 6.2 
cd redis 
make 
make test 
make install 
# 默认安装在 /usr/local/bin 
# redis-server 是服务端程序 
# redis-cli 是客户端程序

3 启动

mkdir redis-data 
# 把redis文件夹下 redis.conf 拷贝到 redis-data
cp redis.conf redis-data/

# 修改 redis.conf,在末尾添加以下内容,不修改密码,那么密码默认为空,使用cli登录时不需要添加密码。
requirepass 123456 
daemonize yes 
bind 192.168.1.9 不修改的话,那么是127.0.0.1,如果想远程的话,需要修改。

# 去到这个目录,方便使用这个临时的配置文件。
cd redis-data
# 开启redis服务
redis-server redis.conf 

# 通过 redis-cli 访问 redis-server 
#redis-cli -h 127.0.0.1 -a 123456
redis-cli -h 192.168.1.9 -a 123456

登录成功截图。
在这里插入图片描述

4 认识Redis

redis 是 KV 数据库,数据结构数据库。
Redis有5种基础数据结构,分别是:

  • 1)string(字符串)。
  • 2)list(列表)。
  • 3)hash(字典)。
  • 4)set(集合或称为无序集合)。
  • 5)zset(有序集合)。

其中list、set、hash、zset这四种数据结构是容器型数据结构,它们共享下面两条通用规则:

  • 1)create if not exists:容器不存在则创建。
  • 2)drop if no elements:如果容器中没有元素,则立即删除容器,释放内存。

上面两条规则大家可以自行在redis命令行验证,几分钟的事情,很简单的,本人已经验证过了,是正确的。

5 redis存储结构

5.1 string

string(字符串)是Redis最简单也是使用最广泛的数据结构,它的内部是一个字符数组。是一个安全的数组,即可以存在特殊字符,是依赖字符串的长度len实现。

例如下列存储方式,3个\0都是可以成功读取到的。
在这里插入图片描述

5.2 list

Redis的列表相当于C++语言中STL的deque,它是一个双向链表数据结构,支持前后顺序遍历。因为链表结构插入和删除只会在头尾,所以操作快且时间复杂度O(1)。
而查询会慢,因为需要for遍历,所以时间复杂度O(n)。
在这里插入图片描述

5.2.1 list(列表)深入理解

Redis底层存储list(列表)不是一个简单的双向链表,而是quicklist ——“快速列表”。
关于quicklist是什么,下面会简单介绍。

quicklist是多个ziplist(压缩列表)组成的双向列表。而这个ziplist(压缩列表)又是什么呢?ziplist指的是一块连续的内存存储空间即可认为是数组。
Redis底层对于list(列表)的存储,当元素个数少的时候,它会使用一块连续的内存空间来存储,这样可以减少每个元素增加prev和next指针带来的内存消耗,最重要的是可以减少内存碎片化问题。

1. 正常的链表结构示意图(即未使用ziplist)

每个node节点元素,都会持有一个prev->执行前一个node节点,和next->指向后一个node节点的指针(引用)。
这种结构虽然支持前后顺序遍历,但是也带来了不小的内存开销,如果node节点仅仅是一个int类型的值,那么可想而知,引用的内存比例将会更大。
在这里插入图片描述
2. 使用ziplist压缩后的示意图
ziplist是一块连续的内存地址,他们之间无需持有prev和next指针,能通过地址顺序寻址访问。
与上面的区别是,上面因为相邻内存地址不是连续的,所以需要prev、next索引相邻的节点。
而使用ziplist压缩后,无需再使用prev、next,因为内存地址是连续的,我们只需要计算内存的偏移值即可索引得到相邻的节点。

例如下面:
假设Node相邻的偏移地址就是1字节(C++一般通过sizeof(type)进行求出)。首地址元素内部依然会有一个指针保存尾部的节点元素(老师视频所说),然后尾部节点通过偏移值求出倒数第二个节点,以此类推,同样做到了上图的作用,即头尾能快速找到(O(1)),中间节点能够通过相邻元素与偏移地址求出(O(n)),从而去掉prev、next。
在这里插入图片描述
3 quicklist示意图
简单了解一下即可,如下图。
在这里插入图片描述

5.3 hash(字典)

  • Redis的hash(字典)相当于C++语言中的unordered_map,它是根据散列值分布的无序字典,内部的元素是通过键值对的方式存储。

  • hash(字典)的实现与C++中的unordered_map的结构也是一致的,它的数据结构也是数组+链表组成的二维结构,节点元素散列在数组上,如果发生hash碰撞则使用链表串联在数组节点上。
    在这里插入图片描述

  • hash(字典)扩容:Redis中的hash(字典)存储的value只能是字符串值。Redis考虑到其核心存取是单线程的性能问题,为了追求高性能,因而采取了渐进式rehash策略。渐进式rehash指的是并非一次性完成,它是多次完成的,因此需要保理旧的hash结构,所以Redis中的hash(字典)会存在新旧两个hash结构,在rehash结束后也就是旧hash的值全部搬迁到新hash之后,新的hash在功能上才会完全替代以前的hash。 这种扩容的内存处理,类似C++的vector。

5.4 set(集合)

Redis的set(集合)内部的键值对是无序的、唯一的。它的内部实现了一个所有value为null的特殊字典。就相当于上面的哈希,只不过value值为null。例如哈希是cmd key key value,那么set就是cmd key key,所以末尾的key当成value了。

如果set集合存储的都是整数类型,那么会使用一个数组存储,此时集合会是一个有序的集合,这样让我们能快速进行交并差运算。(老师讲的)

set(集合)由于其特殊去重复的功能,我们可以用来存储活动中中奖的用户的ID,这样可以保证一个用户不会中奖两次。

5.5 zset(有序集合)

zset(有序集合)是Redis中最常问的数据结构。这个有序集合类似C++的set容器,但是底层结构不一样,C++的底层结构是使用RB-tree(红黑树)实现的。而zset不一样,zset使用跳表实现。

zset一方面通过set来保证内部value值的唯一性,另一方面通过value的score(权重)来进行排序。这个排序的功能是通过Skip List(跳跃列表)来实现的。

利用zset的去重和有序的效果可以由很多使用场景,举两个例子:

  • 存储粉丝列表,value是粉丝的ID,score是关注时间戳,这样可以对粉丝关注进行排序
  • 存储学生成绩,value使学生的ID,score是学生的成绩,这样可以对学生的成绩排名
5.5.1 Skip List

1. Skip List简介

  • 跳表全称叫做跳跃表,简称跳表。跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。
  • Skip List(跳跃列表)这种随机的数据结构,可以看做是一个二叉树的变种,它在性能上与红黑树、AVL树很相近;但是Skip List(跳跃列表)的实现相比前两者要简单很多,目前Redis的zset实现采用了Skip List(跳跃列表)(其它还有LevelDB等也使用了跳跃列表)。

RBT红黑树与Skip List(跳跃列表)简单对比:
RBT红黑树:

  1. 插入、查询时间复杂度O(logn)。
  2. 数据天然有序。
  3. 实现复杂,设计变色、左旋右旋平衡等操作。
  4. 需要加锁。

Skip List跳跃列表:
5. 插入、查询时间复杂度O(logn)。
6. 数据天然有序。
7. 实现简单,链表结构。
8. 无需加锁。

2. Skip List特性以及演示查询、插入、删除
Skip List跳跃列表通常具有如下这些特性:

  • 1)Skip List包含多个层,每层称为一个level,level从0开始递增。
  • 2)Skip List 0层,也就是最底层,应该包含所有的元素。
  • 3)每一个level/层都是一个有序的列表。
  • 4)level小的层包含level大的层的元素。
  • 5)每个节点元素由节点key、节点value和指向当前节点所在level的指针数组组成。(这一点得去看源码理解)

2.1 Skip List查询
假设初始Skip List跳跃列表中已经存在这些元素,他们分布的结构如下所示:
在这里插入图片描述
此时查询节点88,它的查询路线如下所示:
在这里插入图片描述

  1. 从Skip List跳跃列表最顶层level3开始,往后查询到10 < 88 && 后续节点值为null && 存在下层level2。
  2. level2 10往后遍历,27 < 88 && 后续节点值为null && 存在下层level1。
  3. level1 27往后遍历,88 = 88,查询命中。

2.2 Skip List插入
假设初始结构和上面查询的一样,此时假设插入的新节点元素值为90,插入路线如下所示:
在这里插入图片描述

  1. 查询插入位置,与Skip List查询方式一致,这里需要查询的是第一个比90大的节点位置,插入在这个节点的前面, 88 < 90 < 99。
  2. 构造一个新的节点Node(90),为插入的节点Node(90)计算一个随机level,这里假设计算的是1,这个level时随机计算的,可能时1、2、3、4…均有可能,level越大的可能越小,主要看随机因子x ,层数的概率大致计算为 (1/x)^level ,如果level大于当前的最大level3,需要新增head和tail节点。
  3. 节点构造完毕后,需要将其插入列表中,插入步骤十分简单 。Node(88).next = Node(90); Node(90).prev = Node(88); Node(90).next = Node(99); Node(99).prev = Node(90)。

实际上跳表的插入就是:查找插入位置,然后确定节点的level,最后插入即可。

2.3 Skip List删除
删除应该是跳表最简单的操作了,流程就是查询到节点,然后删除,重新将删除节点左右两边的节点以链表的形式组合起来即可,这里不再画图。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值