精通Redis之路(一)数据结构——String原理介绍及使用场景

为什么编撰这个系列

1.在无数的简历中见过“精通Redis”的技能描述,当问其一些原理性的东西时候都是磕磕绊绊毫无头绪及思路,
  大部分面试者停留在使用的阶段,可是若是单纯的使用,不知其原理,
  在某些逼死面试者的面试场景中就会真的被逼死~
3.巩固自身姿式~
复制代码

接下来将直入主题html

Redis 数据结构

1、String

能够简单的认为底层实现就是SDS(Simple Dynamic String)。Redis中,默认以SDS做为本身的字符串表示,
只有在一些字符串不可能出现变化的地方使用C字符串。
复制代码
SDS 与 C字符串 比较
SDS C字符串
获取字符串长度的复杂度为O(1) 获取字符串长度的复杂度为O(N)
API是安全的,不会形成缓冲区溢出 API是不安全的,可能会形成缓冲区溢出
修改字符串长度N次最多须要执行N次内存重分配 修改字符串长度N次必然须要执行N次内存重分配
能够保存文本或者二进制数据 只能保存文本数据
可使用一部分库的函数 可使用全部库中的函数

接下来是比较结果的原理介绍面试

SDS定义以下:
struct sdshdr {    
  // 用于记录buf数组中使用的字节的数目
  // 和SDS存储的字符串的长度相等 
	int len;    
  // 用于记录buf数组中没有使用的字节的数目 
	int free;    
  // 字节数组,用于储存字符串
	char buf[];   //buf的大小等于len+free+1,其中多余的1个字节是用来存储’\0’的。
};
复制代码
  • SDS除了用来保存数据库中的字符串以外,SDS还被用做缓冲区(buffer),如AOF模块中的AOF缓冲区,以及客户端状态中的输入缓冲区
SDS存储示例:

SDS 存储示例

1.获取字符串长度的复杂度redis

C语言中: 字符串只是简单的字符的数组,当使用strlen获取字符串长度的时候,
         内部实际上是直接顺序遍历数组的内容,找到对应的’\0’对应的字符,从而计算出字符串的长度。
         即O(N)
S D S : 只须要访问SDS的len属性就能获得字符串的长度,复杂度为O(1)。
复制代码

2.杜绝缓冲区溢出数据库

Redis是C语言编写的,并无方便的数据类型来进行内存的分配和释放(C++ STL String),
必须手动进行内存分配和释放。
对于字符串的拼接、复制等操做,C语言开发者必须确保目标字符串的空间足够大,否则就会出现溢出的状况。
当使用SDS的API对字符串进行修改的时候,

1. API内部第一步会检测字符串的大小是否知足。
    若是空间已经知足要求,那么就像C语言同样操做便可。若是不知足,则拓展buf的空间
2. 以后再进行操做。每次操做以后,len和free的值会作相应的修改。

扩展buf空间策略:
1. 修改以后总长度len<1MB: 总空间为2*len+1;
2. 修改以后总长度len>=1MB: 总空间为len+1MB+1。
换句话说,预分配的空间上限是1MB,尽可能为len。
复制代码

3.减小修改字符串时带来的内存重分配次数segmentfault

Redis主要经过如下两种策略来处理内存问题。
    字符串长度增长操做时,进行空间预分配
    字符串长度减小操做时,惰性空间释放
        当执行字符串长度缩短的操做的时候,SDS并不直接从新分配多出来的字节,
        而是修改len和free的值(len相应减少,free相应增大,buf的空间大小不变化),避免内存重分配。
        SDS也提供直接释放未使用空间的API,在须要的时候,也能真正的释放掉多余的空间。
复制代码

4.二进制安全数组

C字符串除了末尾以外不能出现空字符,不然会被程序认为是字符串的结尾。
这就使得C字符串只能存储文本数据,而不能保存图像,音频等二进制数据。
使用SDS就不须要依赖控制符,而是用len来指定存储数据的大小,
全部的SDS API都会以处理二进制的方式来处理SDS的buf的数据。
程序不会对buf的数据作任何限制、过滤或假设,数据写入的时候是什么,读取的时候依然不变。
复制代码

5.兼容部分C字符串函数缓存

SDS的buf的定义(字符串末尾为’\0’)和C字符串彻底相同,所以不少的C字符串的操做都是适用于SDS->buf的。
好比当buf里面存的是文本字符串的时候,大多数经过调用C语言的函数就能够。
复制代码
参考

喵耳朵 redis基本操做-string原理 (原文地址404)安全

SDS底层结构 segmentfault.com/p/121000000…session

使用场景:

1.缓存
string类型为基本简单类型也是最经常使用的缓存数据类型,字符串单key长度最长为512MB,
一般作法是能够将简单对象序列化后存入。但建议根据业务及业务数据选择最优的数据结构,
不建议全部数据均序列化存入string,固然这样开发很快,可是有一点点low。智者见智。
复制代码
2.计数器
当string结构中存储的是数字时,可进行incr、decr操做,可是注意:此时string内部存储的便再也不是SDS了,同时string方法中setBit及getRange的实现也会有所不一样(TODO)
复制代码
3.共享session
用户从新刷新一次界面,可能须要访问一下数据进行从新登陆,或者访问页面缓存Cookie,
可是能够利用Redis将用户的Session集中管理,在这种模式只须要保证Redis的高可用,
每次用户Session的更新和获取均可以快速完成。大大提升效率。(这段是抄的)
复制代码

相关命令整理:

copy from www.cnblogs.com/DswCnblog/p…数据结构

命令原型 时间复杂度 命令描述
APPEND O(1) 若是该Key已经存在,APPEND命令将参数Value的数据追加到已存在Value的末尾。若是该Key不存在,APPEND命令将会建立一个新的Key/Value。
DECR O(1) 将指定Key的Value原子性的递减1。若是该Key不存在,其初始值为0,在decr以后其值为-1。若是Value的值不能转换为整型值,如Hello,该操做将执行失败并返回相应的错误信息。注意:该操做的取值范围是64位有符号整型。
相关文章
相关标签/搜索