一、String
redis使用自己构建的名为简单动态字符串(SDS)的抽象类型作为默认字符串表示。
数据结构为:
struct sdshdr{
int len;//buf数组已使用的字节,即SDS保存的字符串长度
int free;//未使用的字节长度
char buf[];//保存字符串的字节数组
}
如要保存redis
这几个字符,则其SDS结构为:
buf[]: 'r','e','d','i','s','\0'
len=5;
free=0;
SDS像C的字符串一样最后保存一个额外的空字符。
1.1 SDS特性
-
常数复杂度获取字符串长度
因为使用len记录了自身的长度,可以不像C字符串一样从头遍历进行计数获取。
-
避免缓冲区溢出
SDS在需要修改时,会先检查空间是否满足大小,如果不满足,则先扩展至所需大小再进行修改操作。
-
减少修改字符串的重分配次数
-
空间预分配
如果SDS需要扩展,程序在给SDS分配所需空间后,还会分配额外的未使用空间。
-
如果修改后len长度小于1MB
则给SDS分配所需的空间后,再分配同样大小的未使用空间。
如len长度为1字节,修改后为3字节,则分配2字节空间保证需求,再分配3字节未使用空间进行预分配,下次再进行修改时,如果预分配的3字节够,就不会再进行空间分配操作,而是直接使用,
此时
buf[]
的长度为3+3+1,对应的len=3
,free=3
,buf[]={‘1’,‘2’,‘3’,‘\0’,‘’,‘’,‘’}
,最后就多了3个预分配的空间。 -
如果修改后len长度大于1MB
分配满足需求的空间后再额外分配1MB预分配空间。
如果当前长度为800,修改后的len为1100>1024=1MB,则分配300字节空间满足需求,再分配1MB空间作为预分配空间。此时
len=1100+1024+1
,free=1024
-
-
惰性释放
如果需要缩短字符串,不会立即回收多余的空间,而是用free记录剩余的空间,以备下次扩展时直接使用,避免再次分配。
如有字符串redis,
buf[]={'r','e','d','i','s','\0'}
,如果只想要red
三个字符,则buf结构为:buf[]={‘r’,‘e’,‘d’, ‘ ’ , ‘ ’, ‘\0’ }
,多余的两个长度的空间不会进行回收,此时使用free=2
记录空闲空间。如果再变成redis字符串,则直接在多的空间上进行修改,不会进行空间分配操作。
-
-
二进制安全
可以包含任何数据,包括字符串、序列化对象、图片等,但字符串大小上限为512MB。
二、Key相关命令
因为redis都是以键值对的方式存储,那么和Key有关的命令有哪些呢?
2.1 set
设置键值对,如果key存在则覆盖旧值。
setnx
这个命令和set有所不同,如果key存在,什么都不做,如果key不存在,则设置key-value
> set name wml
OK
> setnx name wml2
(integer) 0
> get name
"wml"
key存在,不会覆盖旧值
> setnx haha heihei
(integer) 1
> get haha
"heihei"
key不存在,则设置key-value。
可以用于分布式锁。
2.1 查询key
- keys 查询所有的键
- exists key 查询目标key是否存在
type key
键的类型
2.2 查看值
> get name
"wml"
get + key
-
查看并重置
> getset name wml2 "wml" > get name "wml2"
-
查看指定区间的值
> getrange name 0 1 "wm"
可以看到,区间为闭区间
2.3 删除key
> del name
(integer) 1
> keys *
(empty list or set)
2.4 设置查看和清除过期时间
expire和pexpire
直接使用
set name wml ex 50
进行设置,或者分开操作:
> set name wml
OK
> expire name 60 //设置过期时间为60
(integer) 1
> ttl name //查看还有多久过期(s)
(integer) 56
> pttl name //查看还有多久过期(ms)
51696
> persist name //清除过期时间
(integer) 1
> pexpire name2 60000 //设置过期时间(ms),不支持运算
(integer) 1
另外还可以使用setex
(s)和psetex
(ms)设置过期时间
setex和psetex
> setex address 120 jiangsu
OK
> ttl address
(integer) 108
> psetex address 60000 haha
OK
> ttl address
(integer) 53
-
setex
设置过期时间,单位s,同时必须指定key和value值
-
psetex
设置过期时间,单位ms
2.5 追加赋值append
> append age 1
1
> append age 2
2
> get age
"12"
key不存在则直接以追加的值创建新的字符串,如果存在,则将新的字符串追加到旧的后面。
2.6 自增自减
> incr age
(integer) 13
> incr name
(error) ERR value is not an integer or out of range
> decr age
(integer) 12
只能针对整数进行自增1和自减1,否则报错
> incrby age 10
(integer) 22
> decrby age 10
(integer) 12
使用incrby
和decrby
对指定值为整数的key进行增加和减少指定值。
浮点数处理
如果是浮点类型的,则需要使用incrfloat key value
,必须指定增长多少,value可为整数也可以是浮点数,为正是加,为负是减。
> incrbyfloat age2 1
2.2
> incrbyfloat age2 -1
1.2
2.7 批量处理
> mset name1 wml1 name2 wml2 name3 wml3
OK
> mget name1 name2 name4
1) "wml1"
2) "wml2"
3) (nil)
如果key不存在,显示nil
同时可以使用msetnx,和setnx结合,只有在key不存在时才设置键值,但如果批量处理的key中,有一个key存在,则所有的键值对都无法设置成功。(原子性)
> msetnx name1 wml1 name2 wml2 name3 wml3
(integer) 0
> mget name1 name2 name3
1) "wml1"
2) "wml2"
3) "wml3"
所有key都不存在,则批量设置成功。
如果再对存在的key进行msetnx,则都不会设置成功,这里name1和name2存在,因此name4设置失败。
> msetnx name1 wml2 name2 wml4 name4 wml6
(integer) 0
> mget name1 name2 name3
1) "wml1"
2) "wml2"
3) "wml3"
> mget name1 name2 name4 //name4并没有设置成功
1) "wml1"
2) "wml2"
3) (nil)
2.8 修改部分值
> set redis redis
OK
> setrange redis 3 haha
7 //返回新的长度len
> get redis
"redhaha"
可以看到,从下标3开始的位置替换为新的值,此时设置的新的值haha比下标3往后的字符is
长,因此全部覆盖。
> setrange redis 10 heihei
16
> get redis
"redhaha\u0000\u0000\u0000heihei"
如果超过了字符串长度,则中间用0填充。
如果修改的长度比剩余的长度小,即只修改第3个位置的字符,后面的会不会消失呢?
> set redis redis
OK
> setrange redis 3 h
5
> get redis
"redhs"
可以看到,如果新的值h
小于目标位置后面剩余的字符长度,则只修改目标位置的值。