【Redis】举例让你快速理解!Redis数据结构与命令
Redis
数据存内存,C语言实现,单线程架构
基于键值对 ,值可以为字符串、哈希、列表、集合、有序集合
键过期功能实现缓存,流水线功能减少网络开销
持久化(数据内存->磁盘)、主从复制(数据多副本)
高可用(故障发现与自动转移)、分布式
*奇数是不稳定版,如4.1,4.3等。偶数稳定,如5.2,5.4jk00…0
4.0.11,6.0.9,6.0.9去看更新文档
主要应用
- 缓存:键值过期,淘汰策略
- 排行榜:列表与集合
- 计数器:计数
- 社交网络:订阅和阻塞
启动与停止
服务器:任何目录下均可
redis-server
客户端
redis-cli -h 127.0.0.1 -p 6379
停止
redis-cli shutdown (nosave|save)
关闭前是否生成持久化文件
不要kill -9杀死redis,但可以kill+进程号
数据库级别的部分用法
dbsize输出键的总数
127.0.0.1:6379> dbsize
(integer) 4
select切换数据库
redis默认有16个数据库,不同数据库之间没有管理,可以是相同数据,但如果是使用多个数据库,最好还是用多个不同端口的redis客户端
redis-cli默认是0号数据库
select index
#切到第一个库
select 0
#切到第16个库
select 15
flushdb&flushall清除数据库
flushdb清除当前数据库
flushall清除所有数据库
键值数目多的时候可能阻塞
键级别的部分用法
set插入与get查看
一个键值对
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
一个列表型
127.0.0.1:6379> rpush mylist a b c d e f g
(integer) 7
get获取的键不存在,返回nil(空)
keys *遍历所有键
*是通配符,?匹配一个,[ ]批评你部分字符,\x做转义
会阻塞
127.0.0.1:6379> keys *
1) "java"
2) "hello"
3) "python"
4) "python"
127.0.0.1:6379> keys h?
1) "hello"
scan遍历所有键
防止阻塞。也可以用模式匹配
一次scan只扫描一个字典中的一部分键,完成keys *的效果要多次scan
scan cursor pattern num
cursor=0表示遍历结束
scan过程中如果有键的变化(增删改),新增的键可能遍历不到
也有hscan、sscan、zscan
127.0.0.1:6379> scan 0
1) "14"
2) 1) "x"
2) "a"
3) "new"
4) "usr:1"
5) "c"
6) "z"
7) "n"
8) "union"
9) "key"
10) "str"
127.0.0.1:6379> scan 14
1) "23"
2) 1) "diff"
2) "redis"
3) "num"
4) "python"
5) "inter"
6) "k"
7) "hello"
8) "y"
9) "b"
10) "wx"
127.0.0.1:6379> scan 23
1) "0"
2) 1) "hi"
2) "ms"
3) "mylist"
4) "usr:2"
5) "et"
exists判断是否存在键值对
127.0.0.1:6379> exists java
(integer) 1
127.0.0.1:6379> exists c
(integer) 0
del删除键
127.0.0.1:6379> del mylist
(integer) 1
127.0.0.1:6379> exists mylist
(integer) 0
127.0.0.1:6379> del my
(integer) 0
输出删除的个数,不存在会输出0,也可一次删多个
127.0.0.1:6379> set a 1
OK
127.0.0.1:6379> set b 2
OK
127.0.0.1:6379> set c 3
OK
127.0.0.1:6379> del a b c
(integer) 3
expire设置键过期时间&expireat
xx时间后自动过期,redis不支持二级数据结构内的过期时间,如列表内部元素
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> expire hello 10
(integer) 1
127.0.0.1:6379> ttl hello
(integer) 5
127.0.0.1:6379> ttl hello
(integer) -2
127.0.0.1:6379> ttl python
(integer) -1
expireat后面时间用时间戳
ttl查看过期时间&pttl
大于等于0是剩余,-1没设置,-2键不存在,pttl精度更高
persist取消设置过期时间&set
*set命令执行时会取消过期时间的设定
type查看格式
127.0.0.1:6379> set str a
OK
127.0.0.1:6379> type str
string
127.0.0.1:6379> set n 012
OK
127.0.0.1:6379> type n
string
127.0.0.1:6379> rpush mylist a b c
(integer) 3
127.0.0.1:6379> type mylist
list
rename重命名&renamenx重命名不覆盖
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> rename hello hi
OK
127.0.0.1:6379> get hi
"world"
127.0.0.1:6379> rename hi hi
OK
#值覆盖
127.0.0.1:6379> set hi sky
OK
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> rename hello hi
OK
127.0.0.1:6379> get hi
"world"
127.0.0.1:6379> set hi sky
OK
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> renamenx hello hi
(integer) 0
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> get hi
"sky"
*如果键值比较大,重命名可能会阻塞redis
randomkey
随机从数据库返回一个键值对
move迁移不同数据库
move key database
dump迁移不同redis
开启了两个客户端连接,非原子性
dump key键值序列化
restore key ttl value复原
migrate原子迁移不同redis
是原子性的,没有开启两个客户端直接传的
单线程
命令从客户端到服务端后,放入队列中,多个客户端同时发送命令,最终执行的顺序不一定
单线程还快的原因
- 纯内存访问
- 非阻塞I/O,I/O多路复用
- 避免线程切换和竞态产生和消耗
数据结构
字符串
包含字符串、数字、二进制(图片音频、视频等),最大不超过512MB
int:8字节
embstr:小于等于39字节
raw:大于等于39字节
用object encoding name查询内部编码
设置set&getset
set key value ex seconds px milliseconds nx|xx
ex设置秒级过期时间
px设置毫秒过期时间
nx 当键不存在时,才可以设置成功,等效于setnx
xx 当键存在时才可以设置成功能,等效于setxx
127.0.0.1:6379> getset python p
"redis-py"
getset返回上一个赋值结果
批量操作mset&mget
127.0.0.1:6379> mset a 1 b 2 c 3
OK
127.0.0.1:6379> mget a b c d
1) "1"
2) "2"
3) "3"
4) (nil)
set&get: n*time_get = n*time_web +n*time_shell
mset&mget: n*time_get = 1*time_web +n*time_shell
计数
自增incr&incrby
不存在时,按0自增,不是整数会报错
127.0.0.1:6379> exists k
(integer) 0
127.0.0.1:6379> incr k
(integer) 1
127.0.0.1:6379> incr hello
(error) ERR value is not an integer or out of range
127.0.0.1:6379> incrby k 4
(integer) 5
自减decr&decrby
by指定增减长度
127.0.0.1:6379> decr k
(integer) 4
127.0.0.1:6379> decrby k 7
(integer) -3
127.0.0.1:6379> get k
"-3"
追加append
127.0.0.1:6379> append hello hello
(integer) 10
127.0.0.1:6379> get hello
"worldhello"
长度strlen
127.0.0.1:6379> strlen k
(integer) 2
127.0.0.1:6379> get k
"-2"
127.0.0.1:6379> strlen hello
(integer) 10
修改setrange
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"
127.0.0.1:6379> setrange redis 4 !
(integer) 5
127.0.0.1:6379> get redis
"best!"
127.0.0.1:6379> setrange redis 9 b
(integer) 10
127.0.0.1:6379> get redis
"best!\x00\x00\x00\x0
注意下标从0开始,超出长度也可以
截选getrange
127.0.0.1:6379> getrange redis 0 1
"be"
左右闭区间
时间复杂度
del、mset、mget是 o(k),其余均o(1)
使用场景
MySQL做存储层——》 Redis做缓存层——》web服务,MySQL也可以直接返回到web服务层
哈希
key-value,其中value由多个field(不叫key,等效key)-value构成的集合
命令在字符串的基础上加个h,作用域是field,不是key
内部编码有ziplist(元素个数和所有值大小小于规定值,默认规定512个/ 64字节)和hashtable两种
稀疏存储
hset&hget
127.0.0.1:6379> hset usr:1 name tom
(integer) 1
127.0.0.1:6379> get usr
(nil)
127.0.0.1:6379> hget usr:1 name
"tom"
hlen
127.0.0.1:6379> hset usr:1 city bj
(integer) 1
127.0.0.1:6379> hlen usr:1
(integer) 2
hmset&hmget
127.0.0.1:6379> hmset usr:2 name jerry city sh
OK
127.0.0.1:6379> hmget usr:2 name city
1) "jerry"
2) "sh"
hexists
127.0.0.1:6379> hexists usr:1 age
(integer) 0
127.0.0.1:6379> hexists usr:1 name
(integer) 1
hkeys
127.0.0.1:6379> hkeys usr:1
1) "name"
2) "city"
127.0.0.1:6379> keys *
1) "usr:1"
2) "usr:2"
hvals
127.0.0.1:6379> hvals user:1
(empty list or set)
127.0.0.1:6379> hvals usr:1
1) "tom"
2) "bj"
*没有vals *
hgetall
127.0.0.1:6379> hgetall usr:2
1) "name"
2) "jerry"
3) "city"
4) "sh"
hincrby
*没有hincr
127.0.0.1:6379> hincrby usr:1 age 5
(integer) 5
127.0.0.1:6379> hincr usr:1 age
(error) ERR unknown command `hincr`, with args beginning with: `usr:1`, `age`,
时间复杂度
hdel、hgetall、hmget、hmset 是o(k)
hkeys、hvals 是 o(n)
其余操作是o(1)
列表
一个列表最多存储232-1个元素
两端有pop和push操作
列表元素有序,索引查找
虽然输出是从1号开始,实际下标从0开始,左右闭区间
rpush
127.0.0.1:6379> rpush listk c b a
(integer) 3
lpush
127.0.0.1:6379> lpush listk d
(integer) 4
127.0.0.1:6379> lrange listk 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
lrange
如果是大列表获取中间元素的性能会变差
127.0.0.1:6379> lrange listk 0 -1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> lrange listk 0 1
1) "c"
2) "b"
127.0.0.1:6379> lrange listk 0 4
1) "d"
2) "c"
3) "b"
4) "A"
5) "a"
127.0.0.1:6379> lrange listk -5 -1
1) "d"
2) "c"
3) "b"
4) "A"
5) "a"
linsert
127.0.0.1:6379> linsert listk before a A
(integer) 5
127.0.0.1:6379> lrange listk 0 -1
1) "d"
2) "c"
3) "b"
4) "A"
5) "a"
lindex
127.0.0.1:6379> lindex listk 1
"c"
127.0.0.1:6379> lindex listk -1
"a"
llen
127.0.0.1:6379> llen listk
(integer) 5
lpop
127.0.0.1:6379> lpop listk
"d"
127.0.0.1:6379> lrange listk 0 -1
1) "c"
2) "b"
3) "A"
4) "a"
rpop
127.0.0.1:6379> rpop listk
"a"
127.0.0.1:6379> lrange listk 0 -1
1) "c"
2) "b"
3) "A"
lrem 范围删除
lrem key count value
count = 0 都删
count>0从左删
count<0从右删
127.0.0.1:6379> lrange listk 0 -1
1) "d"
2) "d"
3) "d"
4) "d"
5) "c"
6) "b"
7) "A"
127.0.0.1:6379> lrem listk 2 d
(integer) 2
127.0.0.1:6379> lrange listk 0 -1
1) "d"
2) "d"
3) "c"
4) "b"
5) "A"
127.0.0.1:6379> lrem listk -1 d
(integer) 1
127.0.0.1:6379> lrange listk 0 -1
1) "c"
2) "d"
3) "c"
4) "b"
5) "A"
6) "a"
7) "a"
8) "a"
ltrim 截取
127.0.0.1:6379> ltrim listk 1 5
OK
127.0.0.1:6379> lrange listk 0 -1
1) "d"
2) "c"
3) "b"
4) "A"
5) "a"
lset
127.0.0.1:6379> lset listk 4 Apple
OK
127.0.0.1:6379> lrange listk 0 -1
1) "d"
2) "c"
3) "b"
4) "A"
5) "Apple"
brpop&blpop阻塞
blpop/ brpop key/ key_list timeout
- 空表阻塞timeout,timeout=0一直阻塞,timeout时间结束后返回nil
- 一旦不为空,立刻弹出返回,即使未到timeout时间,即使该元素并不是本客户端插入的
- brpop从左到右,blpop从右到左,有一个能弹出就返回
- 多个客户端对同一个键(列表)执行,让最先执行的客户端获得该值,其他客户端阻塞
127.0.0.1:6379> blpop listk 3
1) "listk"
2) "Apple"
127.0.0.1:6379> blpop listk 3
(nil)
(3.04s)
127.0.0.1:6379>
时间复杂度
linsert、lrem、ltrim、lset、lindex 是 o(n)
rpush、lpush 是o(k)
lrange 是o(start+n)
其余o(1)
使用场景
消息队列:lpush+brpop,阻塞,生产者与消费者问题
有限集合:lpush+ltrim
集合
内部编码:intset(元素都是整型且个数小于规定,默认规定512个),hashtable(其余情况)
sadd
返回添加成功的个数
127.0.0.1:6379> sadd ms a b c
(integer) 3
127.0.0.1:6379> sadd ms a d
(integer) 1
127.0.0.1:6379> sadd ms a b
(integer) 0
smembers
返回是无序的
127.0.0.1:6379> smembers ms
1) "b"
2) "a"
3) "c"
4) "d"
srem
返回删除的个数
127.0.0.1:6379> srem myset a b c
(integer) 3
127.0.0.1:6379> smembers myset
(empty list or set)
scard
计算元素个数
127.0.0.1:6379> scard ms
(integer) 4
sismember
127.0.0.1:6379> sismember ms d
(integer) 1
127.0.0.1:6379> sismember ms e
(integer) 0
srandmember
随机返回几个元素
127.0.0.1:6379> srandmember ms 3
1) "d"
2) "c"
3) "b"
127.0.0.1:6379> srandmember ms 3
1) "d"
2) "a"
3) "b"
spop
随机弹出元素
127.0.0.1:6379> spop ms
"c"
sinter求交集
127.0.0.1:6379> smembers et
1) "a"
2) "f"
3) "k"
4) "h"
127.0.0.1:6379> smembers ms
1) "a"
2) "b"
3) "d"
127.0.0.1:6379> smembers wx
1) "a"
2) "d"
3) "c"
4) "k"
127.0.0.1:6379> sinter wx ms et
1) "a"
sdiff求差集
#ms有,et没有
127.0.0.1:6379> sdiff ms et
1) "d"
2) "b"
#et有,ms没有
127.0.0.1:6379> sdiff et ms
1) "f"
2) "h"
3) "k"
#wx有,ms和et都没有
127.0.0.1:6379> sdiff wx ms et
1) "c"
sunion求并集
并不会改变集合本身,输出并集
127.0.0.1:6379> sunion ms et wx
1) "a"
2) "b"
3) "k"
4) "c"
5) "d"
6) "f"
7) "h"
集合计算结果保存
127.0.0.1:6379> sinterstore inter ms et
(integer) 1
127.0.0.1:6379> smembers inter
1) "a"
127.0.0.1:6379> sunionstore union ms et
(integer) 6
127.0.0.1:6379> smembers union
1) "f"
2) "b"
3) "a"
4) "k"
5) "d"
6) "h"
127.0.0.1:6379> sdiffstore diff ms et
(integer) 2
127.0.0.1:6379> smembers diff
1) "d"
2) "b"
时间复杂度
sadd、srem 是 o(k),k是元素个数
srandmember 是o(count)
sinter是o(a*b)a是多个集合中元素最少的个数,m是键个数
sunion、sdiff是o(k),k为运算集合的元素个数之和
其余是o(1)
使用场景
标签:给用户添加标签&给标签添加用户(在一个事务执行)、删除用户的标签&删除标签的用户(在一个事务执行)
生成随机数:spop/srandmember
社交需求:sadd+sinter
有序集合
key对应的有序集合中,每个元素由score、member构成,score是排序的指标必须为float。
score可重复,member不可重复
内部编码:ziplist、skiplist(个数超过128个,单个元素大于64字节)
zadd
有nx、xx、ch、incr可以扩展
zadd key 【nx|xx】 【ch】 【incr】 score member
nx的member不存在,xx必须存在(用于修改),ch返回操作结束后元素和分数变化的个数
127.0.0.1:6379> zadd z 40 ada
(integer) 1
127.0.0.1:6379> zadd z 20 tom 23 jan 28 luka
(integer) 3
127.0.0.1:6379> zadd z nx 21 ben
(integer) 1
127.0.0.1:6379> zadd z xx ch 30 ben
(integer) 1
127.0.0.1:6379> zadd z incr 2 ben
"32"
zcard
计算元素个数
127.0.0.1:6379> zcard z
(integer) 5
zscore
127.0.0.1:6379> zscore z ada
"40"
127.0.0.1:6379> zscore z hans
(nil)
zrank&zrevrank
排名,小到大,大到小,返回member的名次
127.0.0.1:6379> zrank z tom
(integer) 0
127.0.0.1:6379> zrevrank z ada
(integer) 0
zrem
返回删除个数
127.0.0.1:6379> zrem z ben
(integer) 1
zincrby
127.0.0.1:6379> zincrby z 10 tom
"30"
zrange&zrevrange
输入的是名次
127.0.0.1:6379> zrange z 0 1
1) "jan"
2) "luka"
127.0.0.1:6379> zrange z 0 -1
1) "jan"
2) "luka"
3) "tom"
4) "ada"
127.0.0.1:6379> zrevrange z 0 1
1) "ada"
2) "tom"
127.0.0.1:6379> zrevrange z 0 1 withscores
1) "ada"
2) "40"
3) "tom"
4) "30"
最后加上withscore会返回分数
zrangebyscore&zrevrangerange
输入的是分数区间,默认闭区间,开区间加个(,或),无穷是±inf
127.0.0.1:6379> zrangebyscore z 20 30 withscores
1) "jan"
2) "23"
3) "luka"
4) "28"
5) "tom"
6) "30"
127.0.0.1:6379> zrangebyscore z (30 +inf withscores
1) "ada"
2) "40"
zcount
个数
127.0.0.1:6379> zcount z 20 30
(integer) 3
zremrangebyrank
删除某名次到某名次的
127.0.0.1:6379> zremrangebyrank z 2 2
(integer) 1
zremrangebyscore
删除某分数段,也可以用(,),±inf
127.0.0.1:6379> zremrangebyscore z 2 25
(integer) 1
zinterstore
zinterstore destination num key
127.0.0.1:6379> zrange z 0 -1
1) "luka"
2) "momo"
3) "ada"
127.0.0.1:6379> zrange y 0 -1
1) "co"
2) "lily"
3) "momo"
4) "ray"
127.0.0.1:6379> zinterstore new 2 z y
(integer) 1
127.0.0.1:6379> zrange new 0 -1
1) "momo"
zunionstore
zunionstore destination num key
127.0.0.1:6379> zunionstore x 2 z y
(integer) 6
127.0.0.1:6379> zrange x 0 -1
1) "co"
2) "lily"
3) "luka"
4) "ray"
5) "ada"
6) "momo"
时间复杂度

使用场景
排行榜系统