zoukankan      html  css  js  c++  java
  • Redis基础篇(一)数据类型与数据结构(转载)

    原文地址

    https://www.cnblogs.com/liang24/p/13876986.html

    我们先从Redis支持的数据类型学起,了解不同的数据类型的差异和底层实现的数据结构。

    Redis的数据类型

    一般的键值对数据库只支持String一种数据类型,例如Memcached,而Redis支持的数据类型非常丰富,一共有5种,分别是String(字符串)、List(列表)、Hash(字典)、Set(集合)、SortSet(有序集合)。除String外,其余四种数据类型是集合类型。

    Redis数据类型和底层数据结构的对应关系

    String

    String是最简单的数据类型了,它是由简单动态字符串实现的。

    List

    List支持存储一组数据,有两种实现方式:压缩列表(ziplist)和双向循环链表。

    Hash

    Hash用来存储一组数据对,每个数据对又包含键和值两部分。Hash也有两种实现方式:压缩列表和哈希表。

    Set

    Set这种数据类型用来存储一组不重复的数据。有两种实现方式:有序数组和哈希表。

    SortedSet

    SortedSet用来存储一组数据,并且每个数据会附带一个得分。通过得分的大小,我们将数据组织成跳表这种数据结构,以支持快速地按照得分值、得分区间获得数据。

    SortedSet也有两种实现方式:跳表和压缩列表。

    Redis的数据结构

    Redis底层一共有六种数据结构,分别是简单动态字符串、双向链表、压缩列表、哈希表、跳表、整数数组。

    简单动态字符串(SDS)

    与字符串本身不同,简单动态字符串(SDS)保留字符串长度信息,只需要O(1)就能得到字符串长度。

    image

    • len:记录buf数据中已使用字节的数量
    • free:记录buf数组中未使用字节的数量
    • buf:字节数组,用于保存字符串

    双向链表

    双向链表是节点保存前置和后置节点的指针的链表结构。

    image

    压缩列表

    类似于数组,但与数组不同的是,压缩列表在表头有三个字段(zlbytes、zltail、zllen)和表名有一个字段(zlend)。

    查找第一个元素和最后一个元素只需要O(1),其他O(n)。

    image

    • zlbytes:列表长度
    • zltail:列表层的偏移量
    • zllen:列表中的entry个数
    • entry list:列表数组
    • zlend:表示列表结束

    哈希表

    一个哈希表,其实就是一个数组,数组的每个元素称为一个哈希桶。

    哈希表最大的好处是查找元素只需要O(1)时间复杂度,但随着哈希表写入大量数据后,就可能出现操作变慢的情况,这是因为哈希表的冲突问题和rehash可能带来的操作阻塞。

    为什么哈希表操作变慢了?

    当哈希表中写入大量数据时,哈希冲突是不可避免的。这里的哈希冲突指的是两个key的哈希值相同,落在同一个哈希桶中。

    Redis解决哈希冲突的方式是:链式哈希,即同一个哈希桶中的多个元素用一个链表来保存。

    但这里还存在一个问题,哈希冲突链上查找元素的时间复杂度是O(N),当哈希冲突链过长,查找元素耗时长,效率降低。这对Redis来说是不能接受的。

    所以Redis会对哈希表做rehash操作。rehash就是增加现有的哈希桶数量,即扩容。

    Redis执行rehash有三步:

    1. 给哈希表2分配更大的空间,例如是当前哈希表1大小的两倍;
    2. 把哈希表1中的数据重新映射并拷贝到哈希表2中;
    3. 释放哈希表1的空间。

    这个过程看似简单,但是步骤2涉及大量的数据拷贝,如果一次性把数据都迁移完,会造成Redis线程阻塞,无法处理其他请求。

    为了避免这个问题,Redis采用了渐进式rehash

    简单来说,就是把步骤2拷贝数据这一次性开销分摊到多次请求里,例如请求key1,会把key1所在的哈希桶从哈希表1迁移到哈希表2里,直到所有哈希桶都迁移到哈希表2里。在这个过程里,哈希表1要一直保留,等到迁移完成后才释放。

    除了键值对请求操作进行数据迁移,Redis本身还会有一个定时任务来执行rehash。

    跳表

    有序链表只能逐一查找元素,导致操作非常缓慢,于是出现了跳表。具体来说,跳表是在链表的基础上,增加了多级索引,通过索引位置的几个跳转,实现数据的快速定位。如下图所示:

    image

    可以看到,这个查找过程就是在多级索引上跳来跳去,最后定位到元素。跳表的查找时间复杂度是O(logN)。

    整数数组

    就是一个数组,元素类型是整数,并且是有序的。

    image

    • encoding:编码方式
    • length:数组包含的元素数量
    • contents:保存元素的数组

    集合类型的时间复杂度

    数据结构的时间复杂度

    总结

    1. Redis能够快速操作键值对,一方面是因为使用了O(1)的哈希表,另一方面是也采用了O(logN)的跳表。
    2. 集合类型的范围操作的时间复杂度通常是O(N)。建议用其他命令替换,例如SCAN。
    3. List的复杂度较高,要因地制宜使用List。

    拓展

    整数数组和压缩列表在查找时间复杂度方面没有很大的优势,Redis为什么还会把它们作为底层数据结构?

    两方面原因:

    1. 数组和压缩列表可以提升内存利用率,因为他们的数据结构紧凑
    2. 数组对CPU高速缓存支持友好,当数据元素超过阈值时,会转为hash和跳表,保证查询效率

    参考资料

     
  • 相关阅读:
    docker logs-查看docker容器日志
    Linux Python3 的一些坑
    系统安装-007 CentOS7yum源添加、删除及其yum优化
    老司机使用 docker-pan 一键搭建可离线磁力种子的私有云盘,可在线播放预览文件
    syncthing安卓客户端怎么使用
    syncthing搭建私人网盘分享
    黑群晖6.1.x虚拟化安装方法
    Swoole跟thinkphp5结合开发WebSocket在线聊天通讯系统教程
    phpmailer 生产环境发送邮件发送失败Failed to connect to server的解决办法
    aliyun RDS不支持MYIAM
  • 原文地址:https://www.cnblogs.com/zhboke/p/14233616.html
Copyright © 2011-2022 走看看