本文内容
- redis概述
- redis应用场景
- 单线程架构简介
- 全局命令讲解
- 五种数据类型讲解
redis概述
redis是一种键值对的nosql数据库,值可以支持字符串(string),哈希(hash),列表(list),集合(set),有序集合(zset)等数据结构。redis是将数据存放到内存中,因此读写速度很快,另外还能将内存中数据持久化到硬盘,保证数据不会丢失。除了redis支持的5中数据结构,还有许多额外的额功能,比如键过期功能实现缓存,发布订阅功能实现消息系统等。
redis之所以速度快,官方数据是读写性能10万/秒,原因在于:1,redis是用C语言实现的;2,redis是单线程架构,减少了多线程竞争的开销,避免线程切换和锁机制;3,非阻塞IO,redis使用epoll多路复用技术,使得不会在网络IO上浪费太多时间。
redis对外有五种数据结构(string,hash,list,set,zset),每种数据结构都有两种以上的内部编码实现,可以通过命令object encoding查看
127.0.0.1:6379> object encoding h
"embstr"
这样的设计有两个好处,1,改进内部编码时,对外部无影响;2,不同的编码在不同的场景下有不同的优势。
redis使用场景
1,redis可用于缓存,大部分的大型网站都需要缓存层,为了保护存储层,使用缓存能加快访问速度,降低后端服务器数据源的压力,2,可用于排行榜系统,可根据list和zset数据结构,方便的构建排行榜系统,3,可应用于计数器应用,4,订阅发布,消息系统
redis单线程
redis使用的是单线程架构和IO多路复用实现高性能的内存数据库,每次客户端调用命令都是经过三个过程:发送命令、执行命令、返回结果。由于单线程架构,一条命令达到服务端的时候不会立刻执行,所有的命令都会进入到一个队列中排队执行,不会有两个命令同时执行的情况。但是注意,单线程情况下,如果一个命令执行时间过长,会对其他命令造成阻塞,对整个redis服务造成影响。
redis常用命令
全局命令
RedisDesktopManager是一种redis的可视化工具,使用非常简单。
- 查看所有键keys *
127.0.0.1:6379> keys *
1) "a,1"
2) "a:2"
3) "aa"
4) "z"
5) "a:1"
6) "b"
7) "z1"
8) "c"
9) "s1"
10) "s"
11) "h"
- 键总数dbsize
dbsize不会遍历所有的键,而是直接获取redis内置的键总数变量,时间复杂度O(1),而keys 命令会遍历所有键,时间复杂度O(n),请谨慎使用。
127.0.0.1:6379> dbsize
(integer) 11
- 检查键是否存在exists
1 代表存在,0代表不存在
127.0.0.1:6379> exists a
(integer) 0
127.0.0.1:6379> exists b
(integer) 1
- 删除键 del
若删除一个不存在的键会返回0,也可以同时删除多个键
127.0.0.1:6379> exists d
(integer) 1
127.0.0.1:6379> del d
(integer) 1
127.0.0.1:6379> exists d
(integer) 0
127.0.0.1:6379> del a b c
(integer) 3
- 键过期 expire
为键添加有效期,如以下 10秒后自动删除键。
127.0.0.1:6379> expire c 10
(integer) 1
127.0.0.1:6379> exists c
(integer) 0
- 键的数据结构类型 type,如果键不存在,返回none
127.0.0.1:6379> type h
string
字符串(string)
字符串是redis数据结构中最基础的,其他数据结构都是在其之上构建的。字符串类型的值,可以是字符串(string,json,xml等),数字(int,float),二进制等,但是请注意,最大不能超过512MB
常用命令
- 设置值 set
set key value [ex seconds] [px milliseconds] [nx|xx]
127.0.0.1:6379> set hello world
OK
选项:
-ex 设置秒级别的过期时间
-px 设置毫秒级别的过期时间
-nx:键必须不存在,才可以设置成功,用于添加。
-xx:与nx相反,键必须存在,才可以设置成功,用于更新。
除set命令,还有setnx,setex,作用和选项-ex,-nx作用一样。
setex key seconds value
setnx key value
127.0.0.1:6379> set a test
OK
127.0.0.1:6379> setnx a test # 由于a键已经存在,所以设置失败
(integer) 0
127.0.0.1:6379> set a test1 XX # 由于a键已经存在,所以更新成功
OK
- 获取值 get
127.0.0.1:6379> get a
"test1"
127.0.0.1:6379> get a1
(nil)
- 批量设置值 mset
mset key value [key value ...]
127.0.0.1:6379> mset k1 v1 k2 v2
OK
- 批量获取值 mget
mget key [key ...]
127.0.0.1:6379> mget k1 k2
1) "v1"
2) "v2"
批量操作能提高效率,执行时间较短,但是使用批量操作时,要注意数量,防止过多造成网络阻塞,或者redis阻塞。
- 计数 incr
incr命令用于自增操作,键不存在时,创建一个,返回1。除了自增,还有自减命令(decr)
127.0.0.1:6379> incr key1
(integer) 1
127.0.0.1:6379> incr key1
(integer) 2
字符串类型的内部编码有三种,int:8字节的长整形,embstr:小于等于39字节的字符串,raw:大于39字节的字符串。
redis根据具体场景选择不同的编码。
使用场景
- 缓存
通过设置键的过期时间等作为后端的缓存层,降低后端压力 - 计数
字符串类型有incr自增,可作为计数器使用 - 共享数据
如分布式web服务器共享session - 限制
SET key value EX 60 NX
通过过期时间和NX选项达到限速功能,比如每分钟最多访问多少次,可以结合应用代码实现。
哈希(hash)
hash是形如键值对的类型,比如python中的字典。
常用命令
- 设置值 hset
时间复杂度O(1)
hset key field value
127.0.0.1:6379> hset user:1 name z
(integer) 1
同样,也有hsetnx命令,作用同string的setnx
- 获取值 hget
时间复杂度O(1)
hget key field
127.0.0.1:6379> hget user:1 name
"z"
- 删除 hdel
时间复杂度O(n)
hdel key field [field ...]
删除指定key下的一个或者多个field。 - 计算filed的个数 hlen
hlen key - 批量设置和获取
时间复杂度O(k)
hmget key field [field ...] # 可以获取指定key下的一个或者多个field。
hmset key field value [field value ...] # 可以设置指定key下的一个或者多个field。 - 判断field是否存在 hexists
时间复杂度O(1)
hexists key field - 获取所有field
时间复杂度O(n)
hkeys key
127.0.0.1:6379> hkeys user:1
1) "name"
- 获取所有value
时间复杂度O(n)
hvals key
127.0.0.1:6379> hvals user:1
1) "z"
- 获取所有field-value
时间复杂度O(n)
hgetall key
注意,使用hgetall时,如果元素过多,存在阻塞redis的风险,如果一定要使用,可以使用hscan命令,渐进遍历。
127.0.0.1:6379> hgetall user:1
时间复杂度O(n)
1) "name"
2) "z"
同样,hash也有好几种内部编码,根据不同场景选择合适的。
使用场景
- 缓存
比如缓存用户信息时,每个用户有很多属性,比字符串更加直观,但是会消耗更多的内存。
列表(list)
list可以用于存储多个有序的字符串,一个list最多可以存储2**32-1个元素。可以充当队列和栈。开发中有很多使用场景。
常用命令
- 添加 插入 查找
rpush key value [value ...] #从右边插入n个元素 时间复杂度O(n)
lpush key value [value ...] #从左边插入n个元素 时间复杂度O(n)
linsert key before|after p value # 向元素为p的前或者后插入value 时间复杂度O(n)
lrange key start end # 查找元素 时间复杂度O(s+n) s是start的偏移量,n是范围值
lindex key index # 获取指定下标元素 时间复杂度O(n)
llen key # 获取list长度 时间复杂度O(1)
127.0.0.1:6379> rpush l a s 1
(integer) 3
127.0.0.1:6379> lpush l k n
(integer) 5
127.0.0.1:6379> linsert l before k k1
(integer) 6
127.0.0.1:6379> lrange l 0 -1
1) "n"
2) "k1"
3) "k"
4) "a"
5) "s"
6) "1"
- 删除
lpop key # list左侧弹出 时间复杂度O(1)
rpop key # list右侧弹出 时间复杂度O(1)
lrem key count value # 删除指定元素 count>0:从左到右,删除最多count个元素;
count<0:从右到左,删除最多count的绝对值个元素;count,删除所有。 时间复杂度O(n)
ltrim key start end # 按照范围裁剪,保留start到end的元素 时间复杂度O(n)
lset key index newValue # 修改指定index的元素 时间复杂度O(n)
127.0.0.1:6379> lpop l
"n"
127.0.0.1:6379> rpop l
"1"
127.0.0.1:6379> lrem l 1 k
(integer) 1
127.0.0.1:6379> lrange l 0 -1
1) "k1"
2) "a"
3) "s"
127.0.0.1:6379> ltrim l 0 1
OK
127.0.0.1:6379> lrange l 0 -1
1) "k1"
2) "a"
127.0.0.1:6379> lset l 0 k2
OK
127.0.0.1:6379> lrange l 0 -1
1) "k2"
2) "a"
阻塞操作:
blpop key [key ...] timeout #list空时,timeout时间内返回,当timeout=0,无限阻塞下去. 时间复杂度O(1)
brpop key [key ...] timeout
如在一个客户端执行
127.0.0.1:6379> brpop list:test 3
另一个客户端做插入动作,观察第一个客户端情况。
使用场景
- 消息队列
lpush+brpop组合命令可以实现阻塞队列。
lpush+lpop组合命令可以实现栈等
集合(set)
set也是可以用于存储多个有序的字符串,但是里面的元素是无序的,而且没有重复的。一个set里最多有2**32-1个元素,set除了增删改查,还有交集并集的操作。
常用命令
- 添加 删除 查找 判断
sadd key element [element ...] #往集合添加n个元素。时间复杂度O(n)
srem key element [element ...] #删除集合中n个元素。时间复杂度O(n)
scard key #计算元素个数。 时间复杂度为O(1)
sismember key element # 判断元素是否在集合中。时间复杂度O(1)
srandmember key [count] # 随机返回count个元素 默认为1。时间复杂度O(count)
spop key # 弹出元素。时间复杂度O(1)
smembers key # 获取所有元素。时间复杂度O(n)
127.0.0.1:6379> sadd s a b c 1 2 3
(integer) 6
127.0.0.1:6379> srem s b
(integer) 1
127.0.0.1:6379> scard s
(integer) 5
127.0.0.1:6379> sismember s a
(integer) 1
127.0.0.1:6379> srandmember s 3
1) "c"
2) "3"
3) "a"
127.0.0.1:6379> spop s
"1"
127.0.0.1:6379> smembers s
1) "c"
2) "2"
3) "3"
4) "a"
- 集合间操作
sinter key [key ...] # 多个集合交集。时间复杂度O(m*k),m是键数,k是多个set中元素最少的个数
suinon key [key ...] # 多个集合并集。时间复杂度O(n)
sdiff key [key ...] # 多个集合差集。时间复杂度O(n)
sinterstore destination key [key ...] # 保存交集结果到destination
suionstore destination key [key ...] # 保存并集结果到destination
sdiffstore destination key [key ...] # 保存差集结果到destination
127.0.0.1:6379> smembers s
1) "c"
2) "2"
3) "3"
4) "a"
127.0.0.1:6379> sadd s1 1 2 3 a b c
(integer) 4
127.0.0.1:6379> sinter s s1
1) "c"
2) "2"
3) "3"
4) "a"
127.0.0.1:6379> sunion s s1
1) "6"
2) "1"
3) "c"
4) "5"
5) "2"
6) "3"
7) "b"
8) "a"
127.0.0.1:6379> sdiff s s1
(empty list or set)
127.0.0.1:6379> sdiff s1 s
1) "6"
2) "5"
3) "1"
4) "b"
127.0.0.1:6379> sinterstore d1 s s1
(integer) 4
127.0.0.1:6379> smembers d1
1) "2"
2) "3"
3) "c"
4) "a"
使用场景
- 用户标签(无重复特性)
- 随机数
spop/srandmember 命令组合生成随机数
有序集合(zset)
有序集合保留了set的无重复特性,但是比之多一个特点是有序,给每个元素设置一个score作为排行依据,score的值可以重复。
常用命令
- 添加
zadd key [NX|XX] [CH] [INCR] score member [score member ...] # 添加n个成员。复杂度为O(log(n))
nx和xx选项和string上面的等意义一样
ch选项返回此次操作,有序集合元素和分数发生变化的个数
incr选项对score做增加
127.0.0.1:6379> zadd zs 2 a 20 b 200 c
(integer) 3
- 计算成员
zcard key # 获取元素个数。复杂度为O(1)
zscore key member # 获取元素的分数
zrank key member # 获取元素排名
zrevrank key member # 获取元素排名反向
zincrby key increment member # 增加元素分数
zrange key start end [withscores] # 返回指定排名范围的成员
zrevrange key start end [withscores]
127.0.0.1:6379> zadd zs 2 a 20 b 200 c
(integer) 3
127.0.0.1:6379> zcard zs
(integer) 3
127.0.0.1:6379> zscore zs b
"20"
127.0.0.1:6379> zrank zs b
(integer) 1
127.0.0.1:6379> zincrby zs 30 b
"50"
127.0.0.1:6379> zrange zs 1 3
1) "b"
2) "c"
删除
zrem key member [member ...] # 删除n个元素集合操作
交集并集差集
参考书
《redis开发与运维(付磊)》