redis学习系列(三-1)--redis基础类型初探(字符串)

来源:互联网 发布:centos debian 稳定性 编辑:程序博客网 时间:2024/06/05 02:23

博主从事java 只有1年多,越学习越觉得自己知道的很少,期间有时候还觉得自己java好像不错的样子,现在来看,扯淡,哈哈。

套用人生几大状态:其实很多时候人都是处于第一阶段,以为自己懂的很多,当然这是针对大多人的,少部分人又天赋,智商又高,搞起编程得心应手,而我们这种不聪明又没天赋的,只能尽量努力提高自己。时刻警醒,要做2阶段的人。求知探索。

1、不知道自己不知道(自以为是);
2、知道自己不知道(求知探索);
3、知道自己知道(探索规律);
4、不知道自己知道(空杯心态)。

此篇日志参考于如下资料:,自己写一遍,去redis环境测试一遍,能够加深不少印象,光看,还是不行的。

http://redisbook.com/ 

redis的对象 

redis使用对象来表示键值对,也就是说新建一个redis键值对时,会创建2个对象,一个是键对象,一个是值对象。每个对象都是由redisObject结构表示:

typedef struct redisObject {
    // 类型
    unsigned type:4;
    // 编码
    unsigned encoding:4;
    // 指向底层实现数据结构的指针
    void *ptr;
    // ...
} robj;

这个结构先不去关心,只需要知道就行,有C基础应该就能看懂,C中的结构体就是用struct表示的。

类型

对象的type属性记录的是对象的类型,这个值可以是下面的一种:对于键值对的键来说的话,其实一般都是字符串对象,而值可能有多种多样的类型

类型常量                 对象的名称
REDIS_STRING 字符串对象
REDIS_LIST        列表对象
REDIS_HASH        哈希对象
REDIS_SET        集合对象
REDIS_ZSET        有序集合对象

实践

这个时候其实我对于命令还只是停留在set,get阶段,不过不要紧,先有个感性的认识,理性的认识慢慢来。

#String 类型

127.0.0.1:6379> set abc haha
OK
127.0.0.1:6379> get abc
"haha"
127.0.0.1:6379> type abc
string
127.0.0.1:6379> 

#list 类型

127.0.0.1:6379> type abclist
list
127.0.0.1:6379> 

#hash类型

127.0.0.1:6379> hmset srudent name tom age 15 address nanjing
OK

127.0.0.1:6379> hmget srudent name age address
1) "tom"
2) "15"
3) "nanjing"
127.0.0.1:6379> type srudent
hash

另外还有集合和有序集合这两个基本的类型,分别是set和zset,不去列举了。几个类型都在下面。OBJECT ENCODING命令可以查看编码方式

对象对象 type 属性的值TYPE 命令的输出字符串对象REDIS_STRING"string"列表对象REDIS_LIST"list"哈希对象REDIS_HASH"hash"集合对象REDIS_SET"set"有序集合对象REDIS_ZSET"zset"

编码和底层实现

这一段我也不清楚,暂时先记录下,作为对比,以后深入学习之后再说:

编码常量编码所对应的底层数据结构REDIS_ENCODING_INTlong 类型的整数REDIS_ENCODING_EMBSTRembstr 编码的简单动态字符串REDIS_ENCODING_RAW简单动态字符串REDIS_ENCODING_HT字典REDIS_ENCODING_LINKEDLIST双端链表REDIS_ENCODING_ZIPLIST压缩列表REDIS_ENCODING_INTSET整数集合REDIS_ENCODING_SKIPLIST跳跃表和字典


类型编码对象REDIS_STRINGREDIS_ENCODING_INT使用整数值实现的字符串对象。REDIS_STRINGREDIS_ENCODING_EMBSTR使用 embstr 编码的简单动态字符串实现的字符串对象。REDIS_STRINGREDIS_ENCODING_RAW使用简单动态字符串实现的字符串对象。REDIS_LISTREDIS_ENCODING_ZIPLIST使用压缩列表实现的列表对象。REDIS_LISTREDIS_ENCODING_LINKEDLIST使用双端链表实现的列表对象。REDIS_HASHREDIS_ENCODING_ZIPLIST使用压缩列表实现的哈希对象。REDIS_HASHREDIS_ENCODING_HT使用字典实现的哈希对象。REDIS_SETREDIS_ENCODING_INTSET使用整数集合实现的集合对象。REDIS_SETREDIS_ENCODING_HT使用字典实现的集合对象。REDIS_ZSETREDIS_ENCODING_ZIPLIST使用压缩列表实现的有序集合对象。REDIS_ZSETREDIS_ENCODING_SKIPLIST使用跳跃表和字典实现的有序集合对象。

字符串对象

字符串对象的编码可以是 int ,raw,embstr

1.设置的是数字型的键值对,编码就是int

127.0.0.1:6379> set num 10010
OK
127.0.0.1:6379> type num
string
127.0.0.1:6379> object encoding num
"int"
127.0.0.1:6379> 

底层结构如下:图直接copy下来,可以看见ptr指针直接指向 这个值

digraph {    label = "\n 图 8-1    int 编码的字符串对象";    rankdir = LR;    node [shape = record];    redisObject [label = " redisObject | type \n REDIS_STRING | encoding \n REDIS_ENCODING_INT | <ptr> ptr | ... "];    node [shape = plaintext];    number [label = "10086"]    redisObject:ptr -> number;}

2.设置的是字符串键值对,这其中根据字符串长度的不同,会有不同的表现

2.1 长度小于39的话,会使用embstr的编码

127.0.0.1:6379> set key1 abc
OK
127.0.0.1:6379> type key1
string
127.0.0.1:6379> object encoding key1
"embstr"
127.0.0.1:6379> 

embstr是专门针对短字符串的一种优化,和raw一样,都使用redisObject和sdshdr结构表示,但是embstr只调用一次内存分配函数来分配一块连续的空间

digraph {    label = "\n 图 8-3    embstr 编码创建的内存块结构";    node [shape = record];    embstr [ label = " { redisObject | { type | encoding | <ptr> ptr | ... } } |  { sdshdr | { free | len | <buf> buf }} " ];}

因此2.1中set的 key1就如下结构:图是直接copy的,可以看见sdshdr中有free,len和buf,buf中存放的是字符串,以\0结尾,和C一致

digraph {    label = "\n 图 8-4    embstr 编码的字符串对象";    node [shape = record];    embstr [ label = " { redisObject | { type \n REDIS_STRING | encoding \n REDIS_ENCODING_EMBSTR | <ptr> ptr | ... } } |  { sdshdr | { free \n 0 | len \n 5 | { buf | { <buf> 'h' | 'e' | 'l' | 'l' | 'o' | '\\0'}} }} " ];    embstr:ptr -> embstr:buf;}

2.2 长度超过 39,使用的是 raw编码方式

127.0.0.1:6379> set key2 "abc     cdrfdsertg      kgjfhdurhfg     hff"
OK
127.0.0.1:6379> type key1
string
127.0.0.1:6379> type key2
string
127.0.0.1:6379> object encoding key2
"raw"
127.0.0.1:6379> 

raw方式的不同点在于调用两次内存分配函数创建redisObject和sdshdr结构,也就是这种方式的存放地址不是连续的,但是内部都是一样的

digraph {    label = "\n 图 8-2    raw 编码的字符串对象";    rankdir = LR;    node [shape = record];    redisObject [label = " redisObject | type \n REDIS_STRING | encoding \n REDIS_ENCODING_RAW | <ptr> ptr | ... "];    sdshdr [label = " <head> sdshdr | free \n 0 | len \n 43 | <buf> buf"];    buf [label = " { 'L' | 'o' | 'n' | 'g' | ... | 'k' | 'i' | 'n' | 'g' | ' ' | '.' | '.' | '.' | '\\0' } " ];    //    redisObject:ptr -> sdshdr:head;    sdshdr:buf -> buf;}

2.3浮点数也是一样可以保存的,但是会转换成字符串

127.0.0.1:6379> set key3 3.14
OK
127.0.0.1:6379> type key3
string
127.0.0.1:6379> object encoding key3
"embstr"
127.0.0.1:6379> 

总结下:

值编码可以用 long 类型保存的整数。int可以用 long double 类型保存的浮点数。embstr 或者 raw字符串值, 或者因为长度太大而没办法用 long 类型表示的整数, 又或者因为长度太大而没办法用 long double 类型表示的浮点数。embstr 或者 raw

编码的转换

int 和embstr子啊特定条件会向raw转换,不难理解,如果int 拼接字符串或者字符串长度太长,会转换

比如1:

127.0.0.1:6379> get num
"10010"
127.0.0.1:6379> append num "is a num"
(integer) 13
127.0.0.1:6379> get num
"10010is a num"
127.0.0.1:6379> object encoding num
"raw"
127.0.0.1:6379> 

比如2: redis中没有对embstr编码的字符串提供修改程序,所以embstr都是只读的,因此任何对embstr做的修改,都会先将embstr转换成raw,再执行修改。

127.0.0.1:6379> set key5 abc 
OK
127.0.0.1:6379> object encoding abc
"embstr"
127.0.0.1:6379> append key5 " is a english"
(integer) 16
127.0.0.1:6379> obejct encoding key5
(error) ERR unknown command 'obejct'
127.0.0.1:6379> object encoding key5
"raw"
127.0.0.1:6379> 

字符串命令:

命令int 编码的实现方法embstr 编码的实现方法raw 编码的实现方法SET使用 int 编码保存值。使用 embstr 编码保存值。使用 raw 编码保存值。GET拷贝对象所保存的整数值, 将这个拷贝转换成字符串值, 然后向客户端返回这个字符串值。直接向客户端返回字符串值。直接向客户端返回字符串值。APPEND将对象转换成 raw 编码, 然后按 raw编码的方式执行此操作。将对象转换成 raw 编码, 然后按 raw编码的方式执行此操作。调用 sdscatlen 函数, 将给定字符串追加到现有字符串的末尾。INCRBYFLOAT取出整数值并将其转换成 longdouble 类型的浮点数, 对这个浮点数进行加法计算, 然后将得出的浮点数结果保存起来。取出字符串值并尝试将其转换成long double 类型的浮点数, 对这个浮点数进行加法计算, 然后将得出的浮点数结果保存起来。 如果字符串值不能被转换成浮点数, 那么向客户端返回一个错误。取出字符串值并尝试将其转换成 longdouble 类型的浮点数, 对这个浮点数进行加法计算, 然后将得出的浮点数结果保存起来。 如果字符串值不能被转换成浮点数, 那么向客户端返回一个错误。INCRBY对整数值进行加法计算, 得出的计算结果会作为整数被保存起来。embstr 编码不能执行此命令, 向客户端返回一个错误。raw 编码不能执行此命令, 向客户端返回一个错误。DECRBY对整数值进行减法计算, 得出的计算结果会作为整数被保存起来。embstr 编码不能执行此命令, 向客户端返回一个错误。raw 编码不能执行此命令, 向客户端返回一个错误。STRLEN拷贝对象所保存的整数值, 将这个拷贝转换成字符串值, 计算并返回这个字符串值的长度。调用 sdslen 函数, 返回字符串的长度。调用 sdslen 函数, 返回字符串的长度。SETRANGE将对象转换成 raw 编码, 然后按 raw编码的方式执行此命令。将对象转换成 raw 编码, 然后按 raw编码的方式执行此命令。将字符串特定索引上的值设置为给定的字符。GETRANGE拷贝对象所保存的整数值, 将这个拷贝转换成字符串值, 然后取出并返回字符串指定索引上的字符。直接取出并返回字符串指定索引上的字符。直接取出并返回字符串指定索引上的字符。












原创粉丝点击