中间件 - Redis - 基本原理 和 类型数据结构

redis的epoll为什么快

亲密度

一个机器如果有4颗cpu, redis的线程可以一直占用某颗cpu, 占用寄存器, l1,l2,l3里存的东西就不用经常切换, 达到最高速度

首先是BIO - 同步阻塞

为每个进来的连接请求, 都创建一个线程去服务它
在这里插入图片描述

  • 内存成本: JVM中可以配置线程栈的大小, 可以是1M, 也可以配置的更小些, 越小的话, 最大线程数量可以越多
  • cpu调度成本: 线程越多, 切换的就越频繁

每个线程会有较长时间的阻塞, 拿着资源不干活

.

多路复用NIO(select/poll) 同步非阻塞

一个线程可以处理多个连接(文件描述符FD)
在这里插入图片描述
linux支持了select / poll这两个系统调用, 用户态调用内核态的select/ poll命令时, 传了大量到文件描述符, 轮询性能差
虽然, 用户态去调用内核态的select只有一次, 但是内核态仍然需要遍历这1000个文件描述符, 看看哪些FD是需要响应的, 这里有个耗时的1000个遍历的耗时, 阻塞的时间非常长

多路复用NIO(epoll) 同步非阻塞

不需要再去遍历那1000个文件描述符了
redis单线程, 这个线程会调用epoll_create拿到一个epoll的fd, 后续进来的请求连接fd, 会调用epoll_ctl绑定到这个epoll fd上
绑定的操作是调用epoll_ctl
epoll fd : [ fd1, fd2, fd3, …, fdN ]
一旦某个fd读/写就绪了, 内核会提前放到共享空间中的红黑树里, 然后, 当用户态调用epoll_wait时, 不需要传1000个文件描述符, 内核直接返回(不需要遍历)近期在列表里搜集的未读完(水平触发)或状态发生变化(边缘触发)的fd列表给用户态.

在这里插入图片描述

Linux不支持AIO, Windows支持

Linux要实现AIO的话, 会太复杂, 所以暂不支持

Redis的单线程

Redis整个进程肯定是多线程的, 但是只有一个线程, 去处理client发来的requests
所以 , 我们才会说 -> redis是单线程的
单线程的好处是, set/remove指令, 天然的就可以按顺序执行
在这里插入图片描述
但是client自己要保证好按照顺序发送给redis, 就跟kafka的partition一样, 对同一个key的操作,要让同一个client去发送指令

redis数据存储结构

redis的内部整体的存储结构就是一个大的hashmap,内部实现是数组实现hash,冲突通过挂链表去实现,然后每个dictEntry就是一个key/value对象。dictEntry的key指向set key value命令中的key对应的对象,dictEntry的v指向set key value命令中的value对应的对象。
在这里插入图片描述

在这里插入图片描述

关于压缩列表ziplist:
  • string, set 不会用到压缩列表
  • list, zset, hash在元素个数/元素本身不够大的时候, 会先用压缩列表来存储
ziplist数据结构

ziplist作为zset的存储结构时,格式如下图,细节就不多说了,我估计大家都看得懂,紧挨着的是元素memeber和分值socore,整体数据是有序格式。
在这里插入图片描述

为什么要在集合比较小的时候, 用ziplist

  1. ziplist占用的是连续的内存空间, 减少内存碎片, 利用了磁盘顺序读取的高性能
  2. 压缩双链表节省前驱和后驱指针的空间(8b),这在小的list 上,压缩效率 是非常明显的,因为一个普通的双链表中,前驱后驱指针在 64 位机器上需分别占用 8B;
  3. 普通的双向链表,链表的每一个节点都会占用一块独立内存,各项之间使用指针连接起来,指针会占用额外的内存空间,而且会产生大量的内存碎片,ziplist则是放在连续的地址空间中,实际上是一个表(list),而不是链表
  4. ziplist 对于值的存储也采用了变长的编码方式,就是对于比较大的数就多用一些字节存储,小的数就少用一些字节

string存储结构

redis是二进制安全的: redis在内部是以byte的方式存的, 并不是字符, 但同时也存了encoding是什么
所以, 要在client端沟通好字符集, 本例中, 就是在Xshell里设置字符集

set k2 中 (此时xshell的字符集是utf-8, 中国的中字占了3个字节
set k3 中 (此时Xshell的字符集调成了GBK, 中国的中字占了2个字节
在这里插入图片描述

bitmap存储结构

在这里插入图片描述
在这里插入图片描述
面试题: 如何统计三天内去重之后的活跃用户数
假设user1在20190101和20190102登录过, user7在20190102登录过
可以用bitop对这三天对应的key做or操作, 结果放入destkey里, 然后再用bitcount统计bit 1出现过的次数
如下图:
在这里插入图片描述
类似的需求无法就是两种解决方法

  • 要么就是用户为bit, 日期为key
  • 要么就是用户为key, 日期为bit

string & bitmap的使用场景

在这里插入图片描述

List存储结构

有序
在这里插入图片描述
双向链接, 并且有头有尾head, tail指针

Set 存储结构

无序, 去重

在这里插入图片描述

Hash 存储结构

相当于一个hashmap
存储的是键值对key-value
在这里插入图片描述

zset底层存储结构 ( sorted set )

集合较小的时候, 用ziplist

zset底层的存储结构包括ziplist或skiplist,在同时满足以下两个条件的时候使用ziplist,其他时候使用skiplist,两个条件如下:

  • 有序集合保存的元素数量小于128个
  • 有序集合保存的所有元素的长度小于64字节

.

  • 当ziplist作为zset的底层存储结构时候,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个元素保存元素的分值。
  • 当skiplist作为zset的底层存储结构的时候,使用skiplist按序保存元素及分值,使用dict来保存元素和分值的映射关系。

  1. dictht 字典HashTable , 可以在O(1)时间内, 快速查到score(value)
  2. zskipNode: 跳跃表, 可以快速根据score(或者score的范围)查到dictEntry(或者dictEntryList)
    在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值