| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 大数据 -> Redis线程模型和五种数据类型详解 -> 正文阅读 |
|
[大数据]Redis线程模型和五种数据类型详解 |
redis简介redis是一个基于内存的NoSQL数据库,因为数据是存在内存中 redis的存储方式是Key-Value的。 redis线程模型redis采用的是单线程模型,为什么会采用单线程呢? 因为redis是基于内存的数据库,只会操作内存,不牵扯到IO,属于CPU计算密集型任务,如果贸然引入多线程的话,线程之间的切换只会白白浪费资源,不如单线程的执行效率。 除此之外,单线程不会造成并发问题,保证了线程安全问题。 单线程怎么同时处理多个连接呢? redis采用的epoll,也就是多路IO复用。epoll详解可以看我之前写的文章linux中TCP连接详解 我在这里简单的说一下: 然后就可以新建一个线程调用epoll_wait()方法来获取对应的数据,如果event_poll中的就绪列表不为空,就取出数据,拿出对应的socket对象,就可以读取数据了。 2、 Linux中每个连接都会对应一个socket对象,socket中维护了一个接收队列,用来存放数据包;socket中同时维护了一个等待队列,在epoll模式下,等待队列中存放的是event_poll_callback()和epitem信息。当内核将数据包挂到socket的接收队列中的时候,会唤醒对应的event_poll_callback(),并将epitem作为参数传入。 3、 event_poll_callback()首先会将epitem放到对应的event_poll对象的就绪队列中,然后唤醒线程来读取数据了。 就是通过一个就绪列表就可以只利用一个线程来读取多个socket信息了。 redis五种数据类型redis中能够存储五种数据类型 1、stringstring有两种存储方式:
sds有以下优点: Redis中规定 string的大小不能超过512MB 2、listlist就是列表,对应两种实现方式: 当list的长度小于512,并且list中元素的大小都小于64字节的时候,会使用ziplist存储。 从名字就可以看出,ziplist的目的是为了减少内存使用。 ziplist使用的是数组,而不是链表。 我们都知道链表的好处是不要求存储空间连续,但是坏处就是需要指针来保存对应的后续节点信息,64位操作系统,就需要8个字节来存储一个指针了。。。 如果list中元素小于8字节的话,指针都比数据占用空间高了,属实有点大头儿子了。。 所以ziplist利用的数组,数组因为地址是连续的,直接加上对应的偏移就可以得到对应的地址了。 zlbytes zltail zllen 2、entry 采用动态长度编码,当前一个entry的长度小于254字节的时候,就用1个字节记录大小,当前一个entry的长度大于254的字节的时候,用5个字节表示大小。 怎么实现的动态编码呢? encoding content 3、zlend 2、linkedlist linkedlist就是一个双向链表 3、setset对应的是元素不重复的集合,也有两种存储方式
intset适用于元素为整数,并且set中元素个数小于512的情况,还是采用数组的方式来存储,使用连续内存,减少内存使用。 intset中是按照元素的大小顺序来排列。 encoding 当整数的范围在int_16的时候,就将整数按照16位来存储,减少内存的使用。 length contents 插入元素 如果插入的元素在encoding的表示范围,就按照二分法找到对应的插入位置,将在插入位置之后的元素往后移动,最后把新元素插入到对应的位置。 dictht
dict中含有两个dictht,ht[0]用于存放数据,ht[1]用于扩容和收缩。 扩容详情 因为当持久化的时候,会往磁盘中写入对应的数据,尽量要避免扩容,所以负载因子会变成5。 1、 首先将哈希表的数组大小增加到 第一个 大于等于 ht.used * 2的 2n。比如当前ht.used = 100,那么新的哈希表大小就为 128。 2、 对ht[1]分配对应的哈希表空间,不会直接将ht[0]的数据重新hash然后搬运到ht[1]上,而是采用渐近扩容 渐近扩容 如果哈希表中存在很多数量的键值对,一次性的将键值对搬运到ht[1]的话,会占用redis的大量时间,造成用户服务变慢。所以采用渐近扩容。 ht中维护了一个rehash变量,在渐近扩容的时候,会将rehashindex = 0,当操作了一次ht[0]中的元素的时候,会将rehashindex ++,当rehashindex = ht[0].used的时候,就说明渐近扩容结束了,将rehash = -1,代表扩容结束。 增加: 如果不存在,就将其加入到ht[1]中。 查询: 删除: 更新: 如果不存在,就查询ht[0],如果存在,就将新值插入到ht[1]中,并且将旧值从ht[0]中删除,rehashindex+1. 收缩详情 当负载因子小于 0.1的时候,会触发收缩 ht[1]的大小变为第一个大于等于 ht[0].used 的 2n 然后继续开始渐近收缩,和渐近扩容一样。 当redis线程空闲的时候,也会帮助扩容或者收缩。 4、hashhash是用来存储字典的,也有两种方式存储
2、dictht 5、zsetzset存储的是有序的set,也有两种存储格式: 2、skiplist skiplist是一个概率型结构,通过概率来决定新插入的节点插入到哪一层。 为什么不用红黑树或者B+树? 1、 红黑树只支持固定key的查询,不支持区间查询。 skiplist作者的原话在这里: 1)、 They are not very memory intensive. It’s up to you basically. Changing parameters about the probability of a node to have a given number of levels will make then less memory intensive than btrees. 2、 A sorted set is often target of many ZRANGE or ZREVRANGE operations, that is, traversing the skip list as a linked list. With this operation the cache locality of skip lists is at least as good as with other kind of balanced trees. 3、 They are simpler to implement, debug, and so forth. For instance thanks to the skip list simplicity I received a patch (already in Redis master) with augmented skip lists implementing ZRANK in O(log(N)). It required little changes to the code. 使用场景
|
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 21:49:40- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |