redis数据类型--String

/*
* 操作命令?
* 问题1:为什么要用String类型?
* 问题2:String它能存什么?
* 问题3:String存储实现原理是什么?
* 问题4:应用场景?
*
* */
操作命令:
打开客户端:bin目录下输入 ./redis-cli 回车
select xxx (选择库)
flushdb(清除当前库)
flushall(清除所有)
redis默认是16个db,第一个是0最后一个是15
set aaa 111
mset bbb:1 ccc:2 (设置多个key和多个值,一个key存一个值比如:bbb=1,ccc=2)
mget bbb
mget ccc
mset student:1:sno:qinghuadaxue student:1:name:zhangsan student:1:company:tengxun(设置一个key对应多个值,类似于表,解释:学生表student中id是1的学号是qinghuadaxue 姓名是zhangsan依次类推后面的)
mget student:1:sno student:1:sname student:1:company(取值...)
mset和mget的key是比较长的,比较费内存。
setnx zhangsan(这个命令可以用来做锁操作,一般配合expire使用,给锁加一个过期时间防止死锁)
incr aaa (只是用整数,做递增,场景:统计,全局id,限流)

1、为什么用String?
原因:Redis是有C语音编写的,在C语音中本身是没有String的而是用Char[]。使用Char[]数组有违背redis的存储理念。
说一下Char[]存储的不好之处:
1):Char[]在存储之前必须要指定字符数组的大小,若果指定过大则导致内存浪费,过小则导致内存溢出。
2):对Char[]字符数组的长度进行变更的时候需要重新分配内存。
3):获取字符数组的长度的时候需要对字符数组进行遍历,时间复杂度为O(n)。
4):结束符以\0为标准,像音频、视频等2进制的文件,它极有可能会出行数据不安全,会出现和预想的不一致的情况。
基于上面的一些问题,Redis有自己的一套String的实现,它采用的就是SDS,若问SDS是什么,那它就是Redis中String字符串的实现。
SDS存储字符串好处:
1):不需要担心内存溢出的问题,若果有需要会自动扩容。
2):String字符串长度变更不需要重新分配内存,可以通过"空间预分配"和"惰性空间释放"防止重新分配内存。
3):获取字符串的长度的时候有自己的len属性来获取,() -> 时间复杂度为O(1)。
4):String字符串读取的结尾有len的长度决定,避免出现数据丢失的问题。

Redis中的SDS空间预分配和惰性空间释放?
预分配:当SDS中的内存不够存储的时候比如当前SDS的存储小于1MB,若修改之后内存是13字节则他的存储空间是13+13+1(第二个13是预分配出来未使用的空间,最后一个字节是用来保存空字符的)
若果SDS的长度修改之后大于等于1MB,分配之后len属性的长度是30MB,那么SDS=30MB+1MB+1byte,1Mb就是预分配出来未使用的空间
惰性释放:如果SDS中的len长度变小则不会立刻释放空间,而是将缩短后多出来的空间放到free属性中,等待将来使用
总结:空间预分配和惰性释放可以减少内存分配的次数。



2、String能存储什么?
Stirng字符串、int、float和一些JSON结构的字符串。
说一下如果存/取JSON字符串的话,Redis首先是会进行序列化和反序列话的,这样难免会对序列化和反序列化进行额外的内存开销。

3、存储实现原理:
由于Redis是KV的形式进行存储的,所以他是通过hashtable来存储数据的,我们把它叫做外层的hash,redis中每个KV都是一个dictEntry(dict:字典;entry:实体),
而每一个dictEntry里面有他的属性next指向下一个dictEntry。看下图:




可以看到dictEntry中的key是以SDS来存储的,也就是说key只能是字符串类型的。value是通过RedisObject来存储的,然后通过RedisObject来指向他的SDS,
这里有个疑问既然是通过RedisObject类指向SDS,那为什么不直接用SDS呢来存呢?其实还是为了节约内存。
另一个就是String的存储大小限制:int长整形的2^63-1的时候用int存储,小于44个字符(一个英文一个字符)用embstr存储,大于44用raw存储。embstr是只读的,一点进行修改就会变成raw类型存储。

顺便说一下embstr和raw的区别:
好处:embstr内存只需要分配一次(RedisObject和SDS是连续的),raw需要两次(redisObject和SDS);embstr在创建时少分配一次空间,删除时少释放一次空间,
并且对象所有的数据是连续的方便寻找。
坏处:embstr存储的数据很小并且不能修改,一旦修改就会变成raw类型,这是因为embstr字符串长度增加是要重新分配内存的,RedisObject和SDS都会重新分配空间,因此是只读的。

4、应用场景:
缓存、计数器incr命令、限流(incr设置界限达到界限限制访问)、分布式数据共享(redis session)、全局id(incr命令,因为是自增的)、分布式锁(setnx锁+expire过期时间)

posted on 2019-10-28 17:33  冰龙之剑  阅读(284)  评论(0编辑  收藏  举报

导航