1.redis基础篇

redis的诞生以及发展历程,这里就不用多赘述了,这篇文章从Redis 基本数据类型来讲起!并且只讲存储类型以及存储的原理

1.Redis-String

1) 存储类型 : 可以用来存储字符串、整数、浮点数。

2)String 存储(实现)原理 :

   1.数据模型:

以set hello word 为例,因为 Redis 是 KV 的数据库,它是通过 hashtable 实现的(我们把这个叫做外层的哈希)。所以每个键值对都会有一个 dictEntry(源码位置:dict.h),里面指向了 key 和 value 的指针。next 指向下一个 dictEntry。

dictEntry存储结构
存储模型

其中,key 是字符串,但是 Redis 没有直接使用 C 的字符数组,而是存储在自定义的 SDS中。value 既不是直接作为字符串存储,也不是直接存储在 SDS 中,而是存储在redisObject 中。实际上五种常用的数据类型的任何一种,都是通过 redisObject 来存储的。

2. 具体分析:  

1)redisObject:redisObject 定义在 src/server.h 文件中。

redisObject存储结构

2)内部编码

内部编码

这里注意,*ptr指向对象实际的数据结构,在这里是字符串!可以通过 object encoding key这个命令来查看!

3)字符串类型的编码:1. int,存储 8 个字节的长整型(long,2^63-1)。2. embstr, 代表 embstr 格式的 SDS(Simple Dynamic String 简单动态字符串),存储小于 44 个字节的字符串。3.raw,存储大于 44 个字节的字符串.

3.问题分析:

1)什么是 SDS:SDS是Redis 中字符串的实现。在 3.2 以后的版本中,SDS 又有多种结构(sds.h):sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64,用于存储不同的长度的字符串,分别代表 2^5=32byte,2^8=256byte,2^16=65536byte=64KB,2^32byte=4GB。

sds结构

2)为什么 Redis 要用 SDS 实现字符串:

SDS与C字符串的对比

3)embstr 和 raw 的区别:embstr 的使用只分配一次内存空间(因为 RedisObject 和 SDS 是连续的),而 raw需要分配两次内存空间(分别为 RedisObject 和 SDS 分配空间)。因此与 raw 相比,embstr 的好处在于创建时少分配一次空间,删除时少释放一次空间,以及对象的所有数据连在一起,寻找方便。而 embstr 的坏处也很明显,如果字符串的长度增加需要重新分配内存时,整个RedisObject 和 SDS 都需要重新分配空间,因此 Redis 中的 embstr 实现为只读。

4)int 和 embstr 什么时候转化为 raw:当 int 数据不再是整数,或大小超过了 long 的范围(2^63-1=9223372036854775807)时,自动转化为 embstr。

5)明明没有超过阈值,为什么修改的时候embstr变成 raw 了:对于 embstr,由于其实现是只读的,因此在对 embstr 对象进行修改时,都会先转化为 raw 再进行修改。因此,只要是修改 embstr 对象,修改后的对象一定是 raw 的,无论是否达到了 44个字节。

6)当长度小于阈值时,会还原吗:关于 Redis 内部编码的转换,都符合以下规律:编码转换在 Redis 写入数据时完成,且转换过程不可逆,只能从小内存编码向大内存编码转换(但是不包括重新 set)

7)为什么要对底层的数据结构进行一层包装呢:通过封装,可以根据对象的类型动态地选择存储结构和可以使用的命令,实现节省空间和优化查询速度。

4.应用场景

1)缓存:例如:热点数据缓存(例如报表,明星出轨),对象缓存,全页缓存。可以提升热点数据的访问速度。

2)数据共享分布式:因为 Redis 是分布式的独立服务,可以在多个应用之间共享。例如:分布式 Session

springboot中的应用

3)分布式锁:STRING 类型 setnx 方法,只有不存在时才能添加成功,返回 true。

分布式锁

4)全局 ID:INT 类型,INCRBY,利用原子性 .如incrby userid 1000

5)计数器:INT 类型,INCR 方法.例如:文章的阅读量,微博点赞数,允许一定的延迟,先写入 Redis 再定时同步到数据库。

6)限流:INT 类型,INCR 方法,以访问者的 IP 和其他信息作为 key,访问一次增加一次计数,超过次数则返回 false。

2.Redis-HASH

1)简述:

1.Hash 与 String 的主要区别:

String中value 只能是字符串,不能嵌套其他类型.而hash把所有相关的值聚集到一个 key 中,节省内存空间,只使用一个 key,减少 key 冲突,当需要批量获取值的时候,只需要使用一个命令,减少内存/IO/CPU 的消耗。

2.Hash 不适合的场景:

1)Field 不能单独设置过期时间 2)没有 bit 操作 3)需要考虑数据量分布的问题(value 值非常大的时候,无法分布到多个节点)

2)存储原理:

1.简述:

Redis 的 Hash 本身也是一个 KV 的结构,类似于 Java 中的 HashMap。外层的哈希(Redis KV 的实现)只用到了 hashtable。当存储 hash 数据类型时,我们把它叫做内层的哈希。内层的哈希底层可以使用两种数据结构实现:1)ziplist:OBJ_ENCODING_ZIPLIST(压缩列表) 2)hashtable:OBJ_ENCODING_HT(哈希表)。

2.ziplist 压缩列表

1)定义ziplist 是一个经过特殊编码的双向链表,它不存储指向上一个链表节点和指向下一个链表节点的指针,而是存储上一个节点长度和当前节点长度,通过牺牲部分读写性能,来换取高效的内存空间利用率,是一种时间换空间的思想。只用在字段个数少,字段值小的场景里面。

2)ziplist 的内部结构?

源码中的结构
编码方式--对应上图encodeing字段
存储模型图

3)什么时候使用 ziplist 存储?

当 hash 对象同时满足以下两个条件的时候,使用 ziplist 编码:1)所有的键值对的健和值的字符串长度都小于等于 64byte(一个英文字母一个字节);2)哈希对象保存的键值对数量小于 512 个。 并且一个哈希对象超过配置的阈值(键和值的长度有>64byte,键值对个数>512 个)时,会转换成哈希表(hashtable)。

3.hashtable(dict)---》内层的hash

1)定义:在 Redis 中,hashtable 被称为字典(dictionary),它是一个数组+链表的结构。

2)存储结构:从最底层到最高层 dictEntry——dictht——dict——OBJ_ENCODING_HT

存储结构

3)具体的实现源码分析:从下到上!Redis 的 KV 结构是通过一个 dictEntry 来实现的。Redis 又对 dictEntry 进行了多层的封装实现内层hash。

dictEntry
dictEntry放入 dictht(hashtable 里面)
ht 放到了 dict 里面

4)问题分析-为什么要定义两个哈希表呢?ht[2]:redis 的 hash 默认使用的是 ht[0],ht[1]不会初始化和分配空间。定义俩个哈希表是为了防止rehash操作

41)rehash 的步骤:1.为字符 ht[1]哈希表分配空间,这个哈希表的空间大小取决于要执行的操作,以及 ht[0]当前包含的键值对的数量。(扩展:ht[1]的大小为第一个大于等于 ht[0].used*2。) 2.将所有的 ht[0]上的节点 rehash 到 ht[1]上,重新计算 hash 值和索引,然后放入指定的位置。3.当 ht[0]全部迁移到了 ht[1]之后,释放 ht[0]的空间,将 ht[1]设置为 ht[0]表,并创建新的 ht[1],为下次 rehash 做准备。

42)什么时候触发扩容:

扩容条件分析

5)应用场景:比如对象或者一张表的数据,比 String 节省了更多 key 的空间,也更加便于集中管理。例如购物车

购物车实现图

3.Redis-List 列表

1)存储类型: 存储有序的字符串(从左到右),元素可以重复。可以充当队列和栈的角色。

示意图

2)存储(实现)原理

1.quicklist:

1)定义:quicklist(快速列表)是 ziplist 和 linkedlist 的结合体。

2)具体实现

quicklist定义

3)redis.conf 相关参数:

redis.conf 相关参数

4)quickList--->quicklistNode:quicklistNode 中的*zl 指向一个 ziplist,一个 ziplist 可以存放多个元素。

quicklistNode

5)总体结构:

list总体结构

3)应用场景

1.用户消息时间线 timeline----》 因为 List 是有序的,可以用来做用户时间线

时间线

2.消息队列---》List 提供了两个阻塞的弹出操作:BLPOP/BRPOP,可以设置超时时间。

1)BLPOP:BLPOP key1 timeout 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。  2)BRPOP:BRPOP key1 timeout 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

4.Redis-Set集合

1)存储类型:String 类型的无序集合,最大存储数量 2^32-1(40 亿左右)。

2)存储(实现)原理:

Redis 用 intset 或 hashtable 存储 set。如果元素都是整数类型,就用 inset 存储。如果不是整数类型,就用 hashtable(数组+链表的存来储结构)。

1.kv如何存储set元素:key 就是元素的值,value 为 null。

2.注意点:如果元素个数超过 512 个,也会用 hashtable 存储。

3)应用场景:

1.抽奖:

随机获取元素:  spop myset

2.点赞、签到、打卡

实现图
命令展示

3.商品标签

实现图

4.商品筛选-

1)获取差集:sdiff set1 set2  2)获取交集:sinter set1 set2 3)获取并集:sunion set1 set2

商品筛选-例子
商品筛选-实现

5.Redis-ZSet 有序集合

1)存储类型:

1.定义:sorted set,有序的 set,每个元素有个 score。score 相同时,按照 key 的 ASCII 码排序

2.数据结构对比:

数据结构

2).存储(实现)原理

1.原理

同时满足以下条件时使用 ziplist 编码:1)元素数量小于 128 个 2)所有 member 的长度都小于 64 字节.并且在 ziplist 的内部,按照 score 排序递增来存储。插入的时候要移动之后的数据。超过阈值之后,使用 skiplist(跳表)+dict 存储。

阀值

2. 应用场景

1)排行榜:1.id 为 6001 的新闻点击数加 1:zincrby hotNews:20190926 1 n6001 2.获取今天点击最多的 15 条:zrevrange hotNews:20190926 0 15 withscores

排行榜

6.Redis-其他数据结构

1)BitMaps

1.定义:Bitmaps 是在字符串类型上面定义的位操作。一个字节由 8 个二进制位组成。

BitMaps图

2.应用场景:用户访问统计,在线用户统计

1)支持按位与、按位或等等操作:

按位与、按位或等等操作

2)计算出 7 天都在线的用户:BITOP "AND" "7_days_both_online_users" "day_1_online_users" "day_2_online_users" ... "day_7_online_users"

7.总结:

1)数据结构总结

数据结构总结

2)编码转换总结

编码转换总结
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 转载:可能是目前最详细的Redis内存模型及应用解读 Redis是目前最火爆的内存数据库之一,通过在内存中读写数据...
    jwnba24阅读 650评论 0 4
  • 前言 Redis是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说Redis是实现网站...
    小陈阿飞阅读 829评论 0 1
  • String 字符串 存储类型 可以用来存储字符串、整数、浮点数。 操作命令 设置多个值(批量操作,原子性) 设置...
    WEIJAVA阅读 1,350评论 0 4
  • 老板娘发信息给我说,她帮我讲了许多话,才把充电器给换了个新的,叫我有空去取。这事多少还是有些欣慰,因为一年多点换...
    素颜浣溪纱阅读 351评论 2 17
  • 1.今天5月20日,不知道从什么时候起,五二零(我爱你)也成了一个节日,大家在这一天各种秀恩爱。早饭的时候我开玩笑...
    冯素芳阅读 121评论 0 0