Redis支持的数据结构

数据结构

首先,redis并不是一个简单的内存型k-v存储, 它支持的数据结构很多,可以说是一台数据结构服务器。

字符串Strings

是redis支持的最简单类型, redis的字符串结构可以理解为将C字符串封装了一层,通过加入的属性字段降低字符串操作的复杂度,提高安全性
1. 可以存储的最大string为512M
2. 二级制安全,也就是说可以存储任意类型的数据,比如图片、序列化后的对象等。 C字符串是ASCII编码的,以’\0’来判断字符串是否结束,如果数据中包含’\0’字符便会将数据截断,当做两个字符串,因此仅限于保存文本数据。redis字符串通过len属性来获取数据而不是通过遍历寻找结束标志,因此可以存储任意内容的二进制数据。
3. 兼容C语言的操作。默认在字符串最后加’\0‘,使用C库字符串的部分操作。
4. 如果是integer数据类型的string表示,可以使用原子操作命令using commands in the INCR family: INCR, DECR, INCRBY
5. 可以避免缓冲区溢出。其实原理就是根据redis字符串的特点重写了C可能造成内存溢出的函数,如strcat拼接字符串函数,C语言中需人为保证目标字符串的空间足够大,否会造成溢出,而redis字符串通过free属性来自动判断是否可容纳源字符串,否则会扩展自身内存再拼接。
6. 空间和效率的问题。redis采用的是牺牲空间来换取效率的提升,避免多次的申请或释放内存。redis是kv型数据库,要求速度快,数据也多为频繁修改,因此牺牲一部分空间是可取的。
它提供了两种策略,空间预分配和惰性释放。预分配指的是一个字符串修改后会同时分配一定大小的空间给free预留,避免再次修改分配内存。惰性释放是字符串缩短后不是放空间而是给free记录,避免释放空间。
预分配和惰性释放在memcache中也使用了,不过它是从内存管理的角度出发的。通过在初始化时对整个内存进行预分配,减少动态申请内存的操作带来的消耗,使用的内存都是连续的,避免了内存碎片,内存也不会回收而是通过内个slab的slot指针数组来保存

链表Lists

redis的链表基于双端无环链表,支持push、pop等操作

集合Sets

Set 存储非重复数据,Redis的Set有一个非常好的特性, 可以快速求解集合之间的 并集、交集、差集

字典 Hashes

Redis Hashes are maps between string fields and string values, key-value都只支持string
字典结构
dict结构中,type表示是dicttype类型的指针,表示操作特定类型的键值对的函数,privdata是传给特定函数的可选参数。ht是包含两个哈希表的数组,ht[0]表示正常存放数据的hash表,而ht[1]用于rehash,rehashidx是记录了rehash的进度,正常情况下为-1。
dictht是哈希表,table表示哈希表数组,指针数组,size表示哈希表的大小,used表示已有节点的数目,used/size为负载因子,决定何时进行rehash。sizemask用于计算索引值
每个哈希表元素是dictentry结构,key为键,value可以是一个指针或是一个整数。,next为指向下一个的指针。是传统的hash表。
rehash:
redis使用murmurhash算法计算hash值,能有很好的随机分布性,速度也很快。
当负载因子大于3/2时进行扩容操作,扩容大小为第一个大于ht[0].used*2的2^n,当负载因子小于0.1进行缩容,为第一个大于ht[0].used的2^n。
redis采用渐进式rehash,将rehash操作分摊到每次增删改查,具体过程见这里

有序集合 Sorted sets

Sorted Set 在Set的基础上加了Score,set中的元素按照score排序
Redis的 Sorted Set 是基于跳表实现的,参见维基百科 https://en.wikipedia.org/wiki/Skip_list
参见我的另一条博客。。。。

应用场景

发布-订阅(pub-sub) 系统

**几个重要命令举例:**
发布:PUBLISH  first   Hello1
    PUBLISH  second  Hello2
订阅:SUBSCRIBE first  second
模式订阅:Pub/Sub实现支持模式匹配。客户端可以订阅指定模式
取消订阅:UNSUBSCRIBE

LRU缓存

redis基于LRU策略管理数据
Maxmemory 设置最大使用内存:maxmemory 100mb 这个命令可以放在 redis.conf 文件中
当内存不足时,可以通过maxmemory-policy 命令指定几种淘汰策略:

1、noeviction:达到内存限额后返回错误,客户尝试可以导致更多内存使用的命令(大部分写命令,但DEL和一些例外)
2、allkeys-lru:为了给新增加的数据腾出空间,驱逐键先试图移除一部分最近使用较少的(LRC)。
3、volatile-lru:为了给新增加的数据腾出空间,驱逐键先试图移除一部分最近使用较少的(LRC),但只限于过期设置键。
4、allkeys-random: 为了给新增加的数据腾出空间,驱逐任意键。
5、volatile-random: 为了给新增加的数据腾出空间,驱逐任意键,但只限于有过期设置的驱逐键。
6、volatile-ttl: 为了给新增加的数据腾出空间,驱逐键只有秘钥过期设置,并且首先尝试缩短存活时间的驱逐键。
如果没有键满足回收的前提条件的话,策略volatile-lru, volatile-random以及volatile-ttl就和noeviction 差不多了
选择正确的回收策略是非常重要的,这取决于你的应用的访问模式,不过你可以在运行时进行相关的策略调整,并且监控缓存命中率和没命中的次数,通过RedisINFO命令输出以便调优。
一般的经验规则:
使用allkeys-lru策略:当你希望你的请求符合一个幂定律分布,也就是说,你希望部分的子集元素将比其它其它元素被访问的更多。
使用allkeys-random:如果是循环访问,所有的键被连续的扫描,或者你希望请求分布正常(所有元素被访问的概率都差不多)
使用volatile-ttl:如果你想要通过创建缓存对象时设置TTL值,来决定哪些对象应该被过期

volatile-ttl 会消耗一些多余的内存;

Redis的LRU实现不是一个精确完整的实现,是基于采样的近似的LRU算法。 通过对少量Key 进行取样,回收一个最合适的(最后一次被访问时间最早的) 。 从redis 3.0开始已经改进为基于回收键的候选池 (这是什么意思呢。。。) ,使其更加接近LRU的行为。
Redis LRU有一个非常重要的特性,就是可以指定回收时检查的采样数量,可以通过每次回收时检查的采样数量调整算法的精度,采样数量越多消耗的CPU越高,命令示例如下
maxmemory-samples 10
Redis为什么不使用真实的LRU? 是因为需要太多的内存。 看redis官网的介绍,redis 3.0版本在采样数量为10的情况下已经比较接近LRU的理论情况了,可以看到三种点在图片中, 形成了三种带.
* 浅灰色带是已经被回收的对象
* 灰色带是没有被回收的对象
* 绿色带是被添加的对象
在LRU实现的理论中,我们希望的是,在旧键中的第一半将会过期。Redis的LRU算法则是概率的过期旧的键。
也可以看到,Redis 2.8 版本比3.0版本的分布情况要好
Redis LRU 内存使用分析

事务

分布式锁

参考:
http://www.redis.cn/topics
https://redis.io/documentation
https://redis.io/topics/data-types
http://wangjixiang.iteye.com/blog/2230169

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页