当前位置:网站首页>Redis source code-String: Redis String command, Redis String storage principle, three encoding types of Redis string, Redis String SDS source code analysis, Redis String application scenarios

Redis source code-String: Redis String command, Redis String storage principle, three encoding types of Redis string, Redis String SDS source code analysis, Redis String application scenarios

2022-08-11 07:46:00 Hao Kai

Redis数据类型

RedisData types are not equivalent to data structures,数据结构是RedisThe storage principle of the data type storage structure,Also the underlying encoding for that data type.

1.String存储原理

请添加图片描述

2.Redis-String数据类型:操作命令

新增修改key

# set key value:新增修改key值
127.0.0.1:6379> set mykey 1234567
OK

# get key:获得key值
127.0.0.1:6379> get mykey
"1234"

as givenkeyand the start and end values ​​of the subscripts to output the valuevalue

# getrange key start end:as givenkeyand the start and end values ​​of the subscripts to output the valuevalue
# 下标从0开始,如果下标end>String个数,will output to the end
127.0.0.1:6379> getrange mykey 0 3
"1234"
127.0.0.1:6379> getrange mykey 3 10
"4567"

获得key的value长度

# strlen key:获得key的value长度
127.0.0.1:6379> strlen mykey
(integer) 7

拼接key值

# append mykey abc:拼接key值,Returns the length after concatenation
127.0.0.1:6379> append mykey abc
(integer) 10

key不存在,才能成功,分布式锁

# setnx key value:key不存在,才能成功,返回0或1
# 利用该特性:Can be used to implement some competing operations
# 这个命令是redis实现分布式锁的关键
127.0.0.1:6379> setnx mykey 222
(integer) 0
127.0.0.1:6379> get mykey
"1234567abc"
127.0.0.1:6379> setnx mykey2 333
(integer) 1

为key设置过期时间

# expire key seconds:为key设置过期时间,单位秒,返回0或1
# There is a possibility of failure,不建议这样操作,建议在新建keywhile giving an expiration time
127.0.0.1:6379> expire existkey 3
(integer) 0
127.0.0.1:6379> expire mykey 3
(integer) 1
127.0.0.1:6379>

# set key value [EX seconds] [PX milliseconds] [NX|XX]
# EX秒,PX毫秒,and more precise time,The type of specified time needs to be integer,成功返回ok
127.0.0.1:6379> set mykey 222 EX 60
OK

Add and modify multiplekey的值

# mset key value [key value ...]:Add and modify multiplekey的值,成功返回ok
127.0.0.1:6379> mset mykey1 111 mykey2 222 mykey3 333
OK

获取多个key的值

# mget key [key ...]:获取多个key的值
127.0.0.1:6379> mget mykey1 mykey0 mykey3
1) "111"
2) (nil)
3) "333"

对于数字类型(整型、浮点型)的增减、Increment and decrease operations

# 对于数字类型(整型、浮点型)的增减、Increment and decrease operations:Returns the value after the operation
127.0.0.1:6379> set mykey 22
OK
127.0.0.1:6379> incr mykey
(integer) 23
# incrby key increment:Increment the specified integer value
127.0.0.1:6379> incrby mykey 100
(integer) 123
127.0.0.1:6379> decr mykey
(integer) 122
127.0.0.1:6379> decrby mykey 100
(integer) 22
# incrbyfloat key increment:Increment the specified float value
127.0.0.1:6379> incrbyfloat mykey 0.5
"22.5"

查看RedisInternal data structure type

当你插入RedisWhen a string data,will be based on the value you insert,RedisThen perform internal data conversion.
You can look at this directory first:3.存储原理(底层编码)

可以用object encoding key名查看key的value再RedisThe internal data type of .

127.0.0.1:6379> set key1 11
OK
127.0.0.1:6379> set key2 dhasjkkshljashfklsahkjfjkashaskhdlsahjlvnajhjsdaldhasjkhdjashjkdhask
OK
127.0.0.1:6379> set key3 abc
OK
# 查看key在RedisThe external data type of 
127.0.0.1:6379> type key1
string
127.0.0.1:6379> type key2
string
127.0.0.1:6379> type key3
string
# 查看key在RedisThe inbound data type of 
127.0.0.1:6379> object encoding key1
"int"
127.0.0.1:6379> object encoding key2
"raw"
127.0.0.1:6379> object encoding key3
"embstr"

可以看到RedisExternal types are String(type命令查看),But it's different inside.

3.存储原理(底层编码)

Redis源码:RedisHow to view the source code to get started、Redis外部数据结构到Redis内部数据结构查看源码顺序、Redis源码查看顺序

Redis String存储结构:编码类型

Redis String存储结构、RedisThree encodings of strings

RedisThere are three internal encodings for the string storage type:

  1. int,存储8个字节的长整型(long,2^63-1)
  2. embstr,embstr格式的SDS,存储小于44个字节的字符串,A long string of letters from before,A letter is a byte
  3. raw,raw格式的SDS,存储大于44个字节的字符串
    不同编码方式1The bytes occupied by each English letter are different,具体如下:

不同编码方式1The bytes occupied by each English letter are different,具体如下
1,ASCII码:一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间.
2,UTF-8编码:一个英文字符等于一个字节,一个中文(含繁体)等于三个字节.中文标点占三个字节,英文标点占一个字节.
3,Unicode编码:一个英文等于两个字节,一个中文(含繁体)等于两个字节.中文标点占两个字节,英文标点占两个字节.

编码转换问题:int和embstr什么时候转化为raw?

  1. int数据不再是整数→raw
  2. int大小超过了long的范围(2^63-1)→embstr
  3. embstr长度超过了44个字节→raw

编码转换问题:会还原吗?

编码转换在Redis写入数据时完成,且转换过程不可逆,只能从小内存编码向大内存编码转换(不包括重新set),重新setAlthough in value it is a modification or an insertion,但在RedisIt appears to be reassignment,The encoding conversion here refers to modification such as splicing strings、auto increment,非setvalue change operation.

embstr和raw的区别

embstr:只分配一次内存空间:RedisObject和SDS连续
raw:需要分配两次内存空间

请添加图片描述

embstr优点

它将 RedisObject 对象头和 SDS 对象连续存在一起,使用 malloc 方法一次分配,Its computation will be faster.
embstrThe minimum footprint is 19(16+3),而64-19-1(结尾的\0)=44,所以empstr只能容纳44字节.

当字节数小于44时,The allocated size has always been64个字节.

一旦超过44个字节,The overall size exceeds64个字节,
在RedisLieutenant will consider it a large string,不再使用 emdstr 形式存储,The storage structure will becomeraw.

embstr的缺点

  1. 如果你的valuelength increases,那么RedisObject对象头和SDSObject needs to reallocate memory space,所以Redis设计的embstr是只读的
  2. 由于embstr是只读的,Its content cannot be modified,So once modified it will beembstrencoding directly becomesraw编码,even if the number of bytes is less than44时
127.0.0.1:6379> set key a
OK
127.0.0.1:6379> object encoding key
"embstr"
127.0.0.1:6379> append key 2
(integer) 2
# 可以看到,append后长度为2,字节数小于44,Modification will makeembstrencoding directly becomesraw编码
127.0.0.1:6379> object encoding key
"raw"

sds.h源码文件:RedisImplemented its own string structure

sds.hThe source files are unzippedsrc目录下

SDS是什么:简单动态字符串

SDS(Simple Dynamic String):简单动态字符串,Redis会根据String类型的value长度,去选择合适的sdshdr数结构来存储这个value,这样的设计可以节省内存空间.

在这里插入图片描述

# 换算:
2^10=1024byte=1kb
2^20=1MB
2^30=1GB
2^40=1TB
2^50=1PB
2^60=1EB

sdshdr5:2^5=32byte(不用)
sdshdr8:2^8=256byte
sdshdr16:2^16=65536byte=64kb
sdshdr32:2^32=4GB
sdshdr64:2^64=16EB

Redis的key是字符类型,即String,最大可以用得到是512M,那么它的value字符类型,也是String,最大也是512M.
sdshdr32,sdshdr64定义的这么大,但你未必能用得到全部,这也是Redis预留的一种思想,给你足够多的空间,未来可能就用的到这么大了.

sds.h文件的sdshdr8的源码截取

struct __attribute__ ((__packed__)) sdshdr8 {
    
    uint8_t len; /* used */
				 /* 当前字符数组的长度,有这个值使得获取字符长度的复杂度变为O(1),不用向C语言的字符数组那样从头至尾遍历O(N) */
				 
    uint8_t alloc; /* excluding the header and null terminator */
				   /* 当前字符数组总共分配的内存大小 */
	
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
						 /* 当前字符数组的属性、用来标识到底是sdshdr8还是sdshdr16等 */
						 
    char buf[];			 /* 字符串真正的值,它是用字符数组来进行存储的 */
};

Redis为什么要用SDS来实现字符串?

C语言里面也是没有字符串的,它有的是字符数组,有以下特点:

  1. 内存空间预先分配
  2. 获取字符长度(即字符数组的长度)需要从头到尾先遍历一遍,时间复杂度O(n)
  3. C语言的字符数组内存分配是固定的,长度变更引起内存重新分配
  4. 用’\0’判断字符串结束(如果你存的内容恰好包含,也会认为是结束)

所以Redis基于字符数组实现了自己的字符串SDS:简单动态字符串

C字符数组SDS
获取字符长度的复杂度O(N)获取字符长度的复杂度O(1),因为SDS中uint8_t len存储当前字符数组的长度
API是不安全的,可能会造成称缓冲区溢出API是安全的,不会造成称缓冲区溢出,简单动态字符串可以扩容,而且不需要预先分配,因此不会产生内存溢出
修改字符长度N次必然需要执行N次内存重新分配修改字符长度N次最多需要执行N次内存重新分配,SDS里面有一个特殊的设计,叫做空间的预分配和惰性的空间释放,不管是获取内存还是释放内存都不是及时的,所以性能会提升
只能保存文本数据可以保存文本数据或者二进制数据
可以使用所有<string.h>库中的函数可以使用部分<string.h>库中的函数

4.应用场景

  1. 缓存: 查询、The calculated data is put directly inRedis里面,Then apply directly from Redis中拿数据,Instead of going to the database to look up the value again,It can improve the query speed of the application,It can also play a role in protecting the database
  2. 分布式Session: 在spring和spring boot里面,Actually there is a package,叫spring session,引入之后,会直接将session写入到Redis中,Why can this function be achieved,Because it is a service based on a distributed environment,That is, everyone is connected to it,data sharing can be achieved(Session共享)
  3. 分布式锁 set NX EX: Fails if it exists,To achieve competition,Also provides an expiration time for this,To ensure that the lock will be released regularly
  4. 分布式全局ID incr: There are actually many ways to do this:比如数据库、UUID、雪花算法,而RedisWhy can,是因为它的StringTypes can store integers,并且提供了incr、incrbyThis growing design
  5. 计数器 incr: Number of page visits、The real-time nature of Weibo likes is not very high,可以先存在Redis中,Then synchronize to the database
  6. 限流 incr: 对于某一个用户、IPLimit the number of operations over a period of time
  7. 位操作: 是因为RedisThe characters inside it are 8stored in binary

StringHow to store data in table format?

使用Redis存储一张表的数据,比如下表student:

idnameage
12aaa25
13bbb36

你使用StringData structures can also be implemented:
key特殊处理一下(key前缀+“自定义分隔符”+“Unique key constraint for the table”+“自定义分隔符”+属性名),Then get the value to usemgetto get attributes in batches

# 自定义分隔符_:key名(student_12_id:key前缀+"自定义分隔符"+"Unique key constraint for the table"+"自定义分隔符"+属性名)
127.0.0.1:6379> mset student_12_id 12 student_12_name aaa student_12_age 25
OK
127.0.0.1:6379> mget student_12_id student_12_name student_12_age
1) "12"
2) "aaa"
3) "25"

Well if that works fine,还要Hash干嘛?
String这样做,keyIt has taken up a lot of storage space(The real scene you stitched this onekey很长),还有value,有点得不偿失,于是RedisProvides data types that store tabular formatsHash.

String每个keyare stored separately,利用这个特性,So it can be split more easily,所以如果一个key的数据量很大,可以考虑用String来接

Hash 哈希

存储多个无序的键值对,最大存储数量2^32-1(40亿个左右)

Redis本身就是KV存储的,This storage method isRedis外层的Hash,那么Hash数据结构存储KV结构的数据,这个叫做Redis内层的Hash.

内层的Hash中的valueIt is not possible to nest other data types again(比如list、set、hash等),它的value只能是String.

String与Hash的区别

Hash特点:

  1. 节省内存空间: Because there is only one leftkey
  2. 减少key冲突: Because there is only one leftkey
  3. 取值减少性能消耗: All properties can be retrieved at once,不需要用mget命令,或者get向RedisThe server interacts multiple times to obtain values

Hash不适合的场景:

  1. Field不能单独设置过期时间: String可以单独设置过期时间
  2. 需要考虑数据量分布的问题: Content cannot be split,大数据量的key会使RedisThe memory distribution of multiple nodes is not balanced.比如一个key有10亿个Field,但是一个key只能存储在Redison a service node,You want to split into two5亿的,Hash做不到;而String每个keyare stored separately,So it can be split more easily,所以如果一个key的数据量很大,可以考虑用String来接
原网站

版权声明
本文为[Hao Kai]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/223/202208110549474353.html