一、数据结构与内部编码

1.1 全局命令

Redis有5种数据类型,它们是键值对中的值,对于键来说有一些通用的命令。我们可以通过 dis.cn/commands.html 查看redis的命令。

查看当前库所有key,时间复杂度是O(n)

keys *

键总数,时间复杂度是O(1)

dbsize

检查键是否存在,如果键存在则返回1,不存在则返回0

exists key
127.0.0.1:6379> exists java
(integer) 1
127.0.0.1:6379> exists not_exist_key
(integer) 0

删除键

del key [key ...]
unlink key  #非阻塞,可用版本>= 4.0.0

键过期

expire key seconds
ttl key  #查看key还有多少秒过期,-1表示没设置过期时间,-2表示不存在

键的数据结构类型

type key

其他命令

select 1  #切换数据库
flushdb  #清空当前库
flushall  #清空全部库
rename key newkey  #键重命名
randomkey  #随机返回一个键

1.2 内部编码

type命令实际返回的就是当前键的数据结构类型,它们分别是:string(字符串)、hash(哈希)、list (列表)、set(集合)、zset(有序集 合),但这些只是Redis对外的数据结构。

查看内部编码

127.0.0.1:6379> object encoding hello
"embstr"
127.0.0.1:6379> object encoding mylist
"ziplist"

这样设计的优势:

  • 改进内部编码,对外的数据结构和命令没有影响

  • 多种内部编码实现可以在不同场景下发挥各自的优势

1.3 内存模型

1、dictEntry,每个键值对都对应一个dictEntry,里面存储了指向Key和Value的指针,next指向下一个dictEntry,与本Key-Value无关。

2、Key:左上角可见,Key(“hello”)并不是直接以字符串存储,而是存储在SDS结构中,但是这个SDS是一个指定长度的,没有空闲空间的sds。

3、redisObject:Value(“world”)既不是直接以字符串存储,也不是像Key一样直接存储在SDS中,而是存储在redisObject中。实际上,不论Value是5种类型的哪一种,都是通过RedisObject来存储的。

redisObject数据结构:

typedef struct redisObject{
    //用于表示存储的对象类型
    unsigned type:4;
    //数据结构的编码
    unsigned encoding:4;
    //LRU策略
    unsigned lru:LRU_BITS;
    //引用次数
    int refcount;
    //指针,指向具体的数据类型
    void *ptr;
}

二、常用五大数据类型

string,hash,list,set,zset

2.1 String

  • String类型是Redis最基本的数据类型

  • value可以是字符串、数字、二进制

  • 值最大不能超过512MB

常用命令-单个键值

设置的单个键值

set key value [ex seconds] [px milliseconds] [nx|xx]
  • ex seconds:为键设置秒级过期时间。

  • px milliseconds:为键设置毫秒级过期时间。

  • nx:键必须不存在,才可以设置成功,用于添加。

  • xx:与nx相反,键必须存在,才可以设置成功,用于更新

setex 和 setnx

setex key seconds value
setnx key value
127.0.0.1:6379> setnx hello redis # 因为键redis已存在,所以setnx失败,返回结果为0
(integer) 0

获取值

get key
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> get aaa
(nil)

计数

incr key

  • 值不是整数,返回错误。

  • 值是整数,返回自增后的结果。

  • 键不存在,按照值为0自增,返回结果为1

除了incr命令,Redis提供了decr(自减)、incrby(自增指定数字)、 decrby(自减指定数字)、 incrbyfloat(自增浮点数)

  • decr key

  • incrby key increment

  • decrby key decrement

  • incrbyfloat key increment

示例

127.0.0.1:6379> get counter
"2"
127.0.0.1:6379> incr counter
(integer) 3
127.0.0.1:6379> get counter
"3"
127.0.0.1:6379> incrby counter 100
(integer) 103
127.0.0.1:6379> get counter
"103"
127.0.0.1:6379> decr counter
(integer) 102

其他命令

(1)追加值

append <key> <value>  #将给定的<value>追加到原值的末尾
127.0.0.1:6379> get key
"redis"
127.0.0.1:6379> append key world
(integer) 10
127.0.0.1:6379> get key
"redisworld"

(2)字符串长度

strlen <key>  #获得值的长度

(3)设置并返回原值

getset key value

(4)设置指定位置的字符

setrange key offeset value
#下面操作将值由pest变为了best:
127.0.0.1:6379> set redis pest
OK
127.0.0.1:6379> setrange redis 0 b
(integer) 4
127.0.0.1:6379> get redis
"best"

(5)获取部分字符串

getrange key start end
127.0.0.1:6379> getrange redis 0 1
"be"

字符串类型命令的时间复杂度,如下图所示:/ 结合自身业务需求和数据大小选择适合的命令 /

Redis 原子性操作

所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。

1、在单线程中,能够在单条指令中完成的操作都可以认为是“原子操作”,因为中断只能发生于指令之间。

2、在多线程中,不能被其它进程(线程)打断的操作就叫原子操作。Redis单命令的原子性主要得益于Redis的单线程。即使6.0以后变成了多线程,由于只有主线程执行命令,所以命令的原子性还是可以保证的。

多个键值

批量设置值

mset key value [key value ...]
msetnx key value [key value ...]
127.0.0.1:6379> mset a 1 b 2 c 3 d 4
OK

批量获取值

mget key [key ...]

数据结构

String的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。具有以下特点:

1、长度可变:SDS内部使用一个len属性记录当前字符串的长度,因此可以动态地扩展和缩小字符串的大小,从而满足各种需求。

2、二进制安全:SDS中的内容不以任何形式被视为C字符串,因此可以存储任何类型的数据,包括二进制数据、图片、音频等等。

3、空间预分配:在SDS中,每个字符串都会预分配一定的额外空间,避免了多次扩容和内存重新分配的开销,提高了存储和读取数据的效率。

4、缓冲区复用:在SDS中,当一个字符串被修改后,原来的缓冲区不会被立即释放,而是被保留在SDS中,以便以后可以被复用。

5、O(1)复杂度的长度计算:SDS通过使用len属性来存储字符串长度,使得计算字符串长度的复杂度为O(1)。

字符串类型的内部编码有3种:

  • int:8个字节的长整型。

  • embstr:小于等于44个字节的字符串(一次空间申请,空间紧凑)

  • raw:大于44个字节的字符串。

示例

127.0.0.1:6379> set key22 "one string greater than 44 byte............."
OK
127.0.0.1:6379> strlen key22
(integer) 44
127.0.0.1:6379> object encoding key22
"embstr"
127.0.0.1:6379> set key33 "one string greater than 45 byte.............."
OK
127.0.0.1:6379> strlen key33
(integer) 45
127.0.0.1:6379> object encoding key33
"raw"

数据结构如下:

len:已用空间

alloc:sds申请的buf总空间

flags:标志符,是sds8/sds16/sds32/sds64/sds128等

buf[]:存储字符串的字节数组

所以key的最大长度为sds128,计算完以后最大长度为512M。

应用场景

  • 缓存

  • 计数器

  • 共享session

  • 限速

2.2 hash

1、简介

Redis hash 是一个键值对集合。

Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。

字符串与哈希对比

2、常用命令

设置值

hset key field value
hmset key field value [field value ...] #批量设置field-value

查询

hget key field #获取field的值
hmget key field [field ...] #批量获取field-value
hkeys key #获取所有field
hvals key #获取所有value
hgetall key #获取所有的field-value
hlen key #计算field个数
hexists key field #判断field是否存在
hstrlen key field #计算value的字符串长度

删除field

hdel key field [field ...]
#计数
hincrby key field
hincrbyfloat key field

3、数据结构

Hash类型对应的数据结构是两种:

  • ziplist(压缩列表)

  • hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用 hashtable。

127.0.0.1:6379> hmset hashkey f1 v1 f2 v2
OK
127.0.0.1:6379> object encoding hashkey
"ziplist"
127.0.0.1:6379> hstrlen hashkey f5
(integer) 75
127.0.0.1:6379> object encoding hashkey
"hashtable"

4、应用场景

缓存对象,一个key里有多个字段,比如缓存用户登录信息,如果使用hash 存储,会比直接使用json 存储节省很多空间