redis基础指令及数据类型
redis有5种数据类型,它们是键值对中的值,对于键来说有些通用的命令。这里称之为全局指令。
set 创建一个键值对
127.0.0.1:6379> set a 1
mset 创建多个键值对
127.0.0.1:6379> mset a 1 b 2 c 3 OK
get 通过键来获得值
127.0.0.1:6379> get a
mget 获取多个键值
127.0.0.1:6379> mget a b c 1) "1" 2) "2" 3) "3
dbsize 统计键的个数
注:dbsize是通过数据库内置的计数器来实现的,并不是将所有键遍历后统计的,不必担心影响服务器的性能。
127.0.0.1:6379> dbsize
exists 检查某个键是否存在,存在返回1,不存在返回0
127.0.0.1:6379> exists a # 键 a存在 (integer) 1 127.0.0.1:6379> exists d # 键 d 不存在 (integer) 0
del 删除指定键,成功返回1,没有指定键返回0
127.0.0.1:6379> del mylist # 删除单个键 127.0.0.1:6379> del a b # 删除多个键
expire 设置键过期,以秒为单位
127.0.0.1:6379> set hello world #设置一个键 127.0.0.1:6379> expire hello 50 #设置键50秒后过期 (integer) 1 127.0.0.1:6379> get hello #在过期之前可以查询到键值 "world" 127.0.0.1:6379> ttl hello # 使用ttl指令查看键的失效时间 (integer) 38 127.0.0.1:6379> ttl hello # -2:表示键不存在 (integer) -2 127.0.0.1:6379> get hello # 查询不到键值了 (nil)
关于ttl指令查询的返回值:
大于0:键剩余的过期时间; -1:键没设置过期时间; -2:键不存在或已过期;
expireat 设置过期时间,以unix时间戳为单位
127.0.0.1:6379> set hello world OK 127.0.0.1:6379> expireat hello 1583478533 # 最后为unix时间戳 (integer) 1 127.0.0.1:6379> ttl hello (integer) 1056 127.0.0.1:6379> ttl hello (integer) 1052
rename 更改键名
127.0.0.1:6379> set hello world OK 127.0.0.1:6379> rename hello abc # 将键名改为abc OK 127.0.0.1:6379> get hello (nil) 127.0.0.1:6379> get abc "world"
*keys 列出所有键**
这个指令生产中不建议使用,忘了就好。
127.0.0.1:6379> keys *
randomkey 随机返回一个键名
127.0.0.1:6379> randomkey "c" 127.0.0.1:6379> randomkey "user:1_2:sdiff"
type 查询键值的数据类型
127.0.0.1:6379> type a string
**数据类型有:** string:字符串。 hash:哈希。 list:列表。 set:集合。 zset:有序集合。
select 切换库
redis中默认有16个库,编号为:0–15,配置文件中下面配置决定了库的数量:
databases 16 # 16个库
默认登录后处于编号为0的库中,使用select指令进行切库操作。
127.0.0.1:6379> select 12 OK 127.0.0.1:6379[12]> select 0 OK
迁移键
move 库和库内部迁移
注:move迁移后,源库将不存在被迁移走的键值对。
127.0.0.1:6379> get abc "world" 127.0.0.1:6379> move abc 12 # 迁移到编号为12的库 (integer) 1 127.0.0.1:6379> get abc # 当前库就没有这个键值了 (nil) 127.0.0.1:6379> select 12 # 切换到编号为12的库 OK 127.0.0.1:6379[12]> get abc # 可以查看到键值 "world"
dump+restore 多个redis实例上进行迁移键值
- dump:进行序列化备份,格式是rdb。
- restore:是将dump产生的序列化的值复原,命令格式为:restore key ttl serialized-value,其中key表示定义的新键名;ttl:设置过期时间,设置为0表示永不过期;serialized-value则为dump产生的序列化值。
# redis01实例上 127.0.0.1:6379> set hello world OK 127.0.0.1:6379> dump hello # 获取下面的序列化编码 "\x00\x05world\t\x00\xc9#mH\x84/\x11s" # redis02实例上 127.0.0.1:6379> restore new_hello 0 "\x00\x05world\t\x00\xc9#mH\x84/\x11s" OK 127.0.0.1:6379> get new_hello "world"
注:整个迁移过过程,并非原子性。
migrate 具有原子性的迁移
命令格式:migrate host port key| destination-db timeout [COPY] [REPLACE] host:主机IP port:端口 key:要迁移的键 destination-db:要迁移到对端的哪个编号的库? timeout:超时时间,单位为毫秒 COPY:是否复制,默认为剪切的方式 replace:migrate不管目标redis是否存在该键都会正常迁移进行数据覆盖。
下面是模拟redis01主机迁移数据到redis02
# 主机redis01上,设置两个键值,以不同的方式进行迁移 127.0.0.1:6379> set a move_test OK 127.0.0.1:6379> set b move_test_02 OK 127.0.0.1:6379> migrate 192.168.20.4 6379 a 0 2 # 这个不加copy,等同于剪切 OK 127.0.0.1:6379> migrate 192.168.20.4 6379 b 0 2 copy # 加了copy,等同于复制 OK 127.0.0.1:6379> get a # 剪切走后,本地就没有了 (nil) 127.0.0.1:6379> get b # 但copy的,本地还有 "move_test_02" # 主机redis02上进行查询,两个键值对都在 [ ~]# redis-cli 127.0.0.1:6379> get a "move_test" 127.0.0.1:6379> get b "move_test_02"
info 查看redis实例的详细信息
127.0.0.1:6379> info # Server redis_version:5.0.5 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:94d37e8f5f58aed5 redis_mode:standalone os:Linux 3.10.0-862.el7.x86_64 x86_64 arch_bits:64 multiplexing_api:epoll ............. # 省略部分内容
redis-cli –stat 动态查看资源使用率
[ ~]# redis-cli --stat #动态刷新redis的资源使用情况 ------- data ------ --------------------- load -------------------- - child - keys mem clients blocked requests connections 20 840.41K 1 0 25 (+0) 2 20 840.41K 1 0 26 (+1) 2 20 840.41K 1 0 27 (+1) 2
redis的事务
- 事务的特点:要么都执行,要么都不执行。
- 场景:user_A关注了user_B,那么user_A的关注列表里应该有user_B,同样,user_B的粉丝列表中也需要有user_A,这就需要用到事务。
127.0.0.1:6379> multi # 开启一个事务 OK # 插入两条数据 127.0.0.1:6379> sadd user:a:follow user:b QUEUED 127.0.0.1:6379> sadd user:b:follow user:a QUEUED # 在事务提交前,是查询不到上面插入的两条数据的 127.0.0.1:6379> exec # 提交事务即可查看对应的数据 1) (integer) 1 2) (integer) 1
如果要停止事务,可以用discard指令代替exec指令。
数据结构和内部编码
type命令实际返回的是当前键的数据结构类型,但这些只是redis对外的数据结构,实际上每种数据结构都有自己底层的内部编码实现,而且是多种实现,这样redis会在合适的场景选择合适的内部编码。可以通过object encoding命令查询内部编码。
127.0.0.1:6379> object encoding a "embstr"
内部编码如下: sting(字符串)有3种:raw、int、embstr。 hash(哈希)有2种:hashtable、ziplist。 list(列表)有2种:linkedlist、ziplist。 set(集合)有2种:hashtable、intest。 zset(有序集合)有2种:skiplist、ziplist。
redis的单线程架构
redis使用单线程架构和I/O多路复用模型来实现高性能的内存数据库服务。
**redis为什么这么快?** * 内存型数据库,这是redis达到每秒万级别访问的重要基础。 * 非阻塞I/O线程,Redis使用epoll作为I/O多路复用技术的实现,再加上redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间。 * 单线程避免了因线程切换和竞态产生的消耗。
.
**单线程的好处:** * 可以简化数据结构和算法的实现。 * 单线程避免了线程切换和竞态产生的消耗。
**单线程的问题:** 当某一个指令执行的时间过长,其他命令等待时间过长,造成阻塞。
redis数据类型
string 字符串
127.0.0.1:6379> set a 1 #设置a的值为1
incr 自增(只能对数字自增) 若对值为字符串的进行自增,则会报错(error) ERR value is not an integer or out of range。若键不存在,按0开始自增。
127.0.0.1:6379> set a 1 OK 127.0.0.1:6379> incr a # 返回自增后的值 (integer) 2 127.0.0.1:6379> get a # 自增后a 的值为2 "2"
incrby 自增指定数字
127.0.0.1:6379> get a "1" 127.0.0.1:6379> incrby a 5 (integer) 6 127.0.0.1:6379> get a "6"
incrbyfloat 自增浮点数
注:若当前值为浮点数,则不可以自增或自减整数,必须使用incrbyfloat将其改变为一个整数才可以自增或自减整数。
127.0.0.1:6379> get a "2" 127.0.0.1:6379> incrbyfloat a 5.5 "7.5" 127.0.0.1:6379> get a "7.5"
decr 自减
127.0.0.1:6379> decr a (integer) 2 127.0.0.1:6379> decr a (integer) 1 127.0.0.1:6379> get a "1"
decrby 自减指定数值
127.0.0.1:6379> get a "6" 127.0.0.1:6379> decrby a 4 (integer) 2 127.0.0.1:6379> get a "2"
append 追加值
相当于把追加的值合并在原有值后面。若键不存在或没有值,则会生成新的值。
127.0.0.1:6379> set a hello OK 127.0.0.1:6379> append a " world" # 在原有值后面追加world。 (integer) 11 127.0.0.1:6379> get a "hello world"
strlen 统计值的长度
127.0.0.1:6379> get a "hello world" 127.0.0.1:6379> strlen a (integer) 11 # 因为值中间有空格,也算一位 127.0.0.1:6379> set a 张三 OK 127.0.0.1:6379> strlen a # 一个中文占三个字节 (integer) 6
setrange 设置指定位置的字符
127.0.0.1:6379> set redis pest OK 127.0.0.1:6379> setrange redis 0 b # 0表示值的第一位,将第一位换为b (integer) 4 127.0.0.1:6379> get redis "best" 127.0.0.1:6379> setrange redis 2 d # 2表示值的第三位,将第三位换为d (integer) 4 127.0.0.1:6379> get redis "bedt"
getrange 获取部分字符串
起始数字为0,-1表示最后一位,-2表示倒数第二位…..
127.0.0.1:6379> get redis "bedt" 127.0.0.1:6379> getrange redis 1 -1 "edt" 127.0.0.1:6379> getrange redis 1 2 "ed"
关于字符串的内部编码,redis根据键对应的值,用以下规则选择相应的编码: * int:8字节长度的整数。 * embstr:小于等于39个字节的字符串。 * raw:大于39个字节的字符串。
hash 哈希类型
几乎所有的编程语言都提供了哈希类型,它们的叫法可能是哈希、字典、关联属组。
在redis中,哈希类型是指键值本身又是一种键值对结构,例如:value=field1,value1 …. fiedN,valueN。
哈希类型中的映射关系叫做:field-value,这里的value是指field对应的值,不是键对应的值。
hset key field value 创建hash类型的数据类型
127.0.0.1:6379> hset user name tom (integer) 1 # 返回1表示成功,0表示不成功
hget key field 获取值
127.0.0.1:6379> hget user name "tom"
hdel key field 删除field
127.0.0.1:6379> hdel user name
hmset及hmget批量设置及获取field
127.0.0.1:6379> hmset user name zhangsan age 18 hobby play city beijing OK 127.0.0.1:6379> hmget user name age hobby age city 1) "zhangsan" 2) "18" 3) "play" 4) "18" 5) "beijing"
hexists 判断是否存在
存在返回1,否则返回0。
127.0.0.1:6379> hexists user name (integer) 1 127.0.0.1:6379> hexists user addr (integer) 0
hkeys 获取所有的field
127.0.0.1:6379> hkeys user 1) "name" 2) "age" 3) "hobby" 4) "city"
hvals 获取所有的value
127.0.0.1:6379> hvals user 1) "zhangsan" 2) "18" 3) "play" 4) "beijing"
hgetall 获取所有的field及value值
注:元素越多,越容易造成阻塞。
127.0.0.1:6379> hgetall user 1) "name" 2) "zhangsan" 3) "age" 4) "18" 5) "hobby" 6) "play" 7) "city" 8) "beijing"
hincrby key field 自增数字
127.0.0.1:6379> hincrby user age 2 # age增加2 (integer) 20 127.0.0.1:6379> hget user age "20"
hstrlen key field 计算哈希值的长度
127.0.0.1:6379> hget user name "zhangsan" 127.0.0.1:6379> hstrlen user name (integer) 8 # 长度为8
hash内部编码
当哈希类型元素个数小于 hash-max-ziplist-entries配置(默认512个)、同时所有的值都小于hash-max-ziplist-value配置(默认64字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。
127.0.0.1:6379> hmset hashkey f1 v1 f2 v2 OK 127.0.0.1:6379> object encoding hashkey "ziplist"
list 列表
列表是用来存储多个有序的字符串,比如a、b、c、d、e五个元素从左到右组成了一个有序的列表,列表中的每个字符串称为元素,一个列表最多可以存储2的32次方-1个元素。在redis中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等,列表是一种比较灵活的数据结构,可以充当栈和队列的角色,在实际开发上有很多应用场景。
**列表类型以下有两个特点:** * 列表中的元素是有序的,这意味着可以通过索引下标获取某个元素或者某个范围内的元素列表。 * 列表中的元素是可以重复的。
rpush、lpush 添加操作
127.0.0.1:6379> rpush listk c b a # r表示右(right),从右面开始插入 (integer) 3 127.0.0.1:6379> lpush listk d e f # l表示左(left),从左面开始插入 (integer) 6 127.0.0.1:6379> lrange listk 0 -1 # 从左到右查看第一个到最后一个所有的元素 1) "f" 2) "e" 3) "d" 4) "c" 5) "b" 6) "a"
向某个元素前或者后插入元素
127.0.0.1:6379> lrange listk 0 -1 # 查看原有数据 1) "f" 2) "e" 3) "d" 4) "c" 5) "b" 6) "a" 127.0.0.1:6379> linsert listk before b redis # 向b后面插入redis (integer) 7 127.0.0.1:6379> linsert listk after b python # 向b前面插入python (integer) 8 127.0.0.1:6379> lrange listk 0 -1 # 查看确认 1) "f" 2) "e" 3) "d" 4) "c" 5) "redis" 6) "b" 7) "python" 8) "a"
lrange 查看某个范围内的值
下标从前往后0开始,从后往前是从-1开始。
127.0.0.1:6379> lrange listk 0 -3 1) "f" 2) "e" 3) "d" 4) "c" 5) "redis" 6) "b"
lindex 获取列表指定索引下标的元素
127.0.0.1:6379> lindex listk 0 "f" 127.0.0.1:6379> lindex listk -3 "b" 127.0.0.1:6379> lindex listk -2 "python"
llen 获取列表的长度
127.0.0.1:6379> llen listk (integer) 8
lpop、rpop 删除列表中的元素
127.0.0.1:6379> lrange listk 0 -1 # 查看原有元素 1) "f" 2) "e" 3) "d" 4) "c" 5) "redis" 6) "b" 7) "python" 8) "a" 127.0.0.1:6379> lpop listk # 从左起,删除一个 "f" 127.0.0.1:6379> rpop listk # 从右起,删除一个 "a" 127.0.0.1:6379> lrange listk 0 -1 # 最后查看数据 1) "e" 2) "d" 3) "c" 4) "redis" 5) "b" 6) "python"
lrem key count value 指定范围内删除指定元素
注: * count > 0:从左往右删,删多少看count的值,如果指定的count范围内没有指定的值,则不删。 * count < 0:从右往左删,其余同上。 * 返回0表示没有删除,1表示有删除操作。
127.0.0.1:6379> lrange listk 0 -1 # 查看现有的集合 1) "e" 2) "d" 3) "c" 4) "redis" 5) "b" 6) "python" 127.0.0.1:6379> lrem listk 4 a # 从左往右删4个元素,如果这4个元素中没有a,则不删 (integer) 0 127.0.0.1:6379> lrange listk 0 -1 # 因为从左往右,4个元素内没有a,所以不执行删除 1) "e" 2) "d" 3) "c" 4) "redis" 5) "b" 6) "python" 127.0.0.1:6379> lrem listk 4 d # 换个条件 (integer) 1 127.0.0.1:6379> lrange listk 0 -1 # 因为从左往右,4个元素内有d,所以执行删除操作 1) "e" 2) "c" 3) "redis" 4) "b" 5) "python"
ltrim key start stop 保留指定下标内的值,其余都删除
下标: * 从左往右,第一个元素的下标为0,依次为0、1、2…. * 从右往左,第一个元素的下标为-1,依次为-1、-2、-3….
127.0.0.1:6379> lrange listk 0 -1 # 查看初始值 1) "e" 2) "c" 3) "redis" 4) "b" 5) "python" 127.0.0.1:6379> ltrim listk 1 3 # 保留下标1到3的值 OK 127.0.0.1:6379> lrange listk 0 -1 # 只剩下原来下标1到3的值了 1) "c" 2) "redis" 3) "b"
lset 修改列表中的元素值
127.0.0.1:6379> lrange listk 0 -1 1) "c" 2) "redis" 3) "b" 127.0.0.1:6379> lset listk 2 python # 修改下标为2的值为python OK 127.0.0.1:6379> lrange listk 0 -1 1) "c" 2) "redis" 3) "python"
list内部编码
**列表的内部编码有两种:** * ziplist:压缩列表,当列表的元素个数小于list-max-ziplistentries配置(默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时(默认64字节),Redis会选用ziplist来作为表的内部实现来减少内存的使用。 * Linkedlist:链表,当列表类型无法满足ziplist的条件时,Redis会使用linkedlist作为列表的内部实现。
list使用场景:消息队列、文章列表……
set 集合
集合类型也是用来保存多个字符串元素,但和列表类型不一样的是,集合中不允许有重复的元素,并且集合中的元素是无序的,不能通过索引下标获取元素。一个集合最多可以存储2的32次方-1个元素,redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集。
sadd 插入元素
127.0.0.1:6379> sadd myset a b c # 创建一个新的集合 (integer) 3 127.0.0.1:6379> sadd myset a b d # a b 这两个元素已存在,所以不会添加到集合中 (integer) 1 # 只添加了一个元素
srem 删除元素
127.0.0.1:6379> srem myset a b (integer) 2
scard 统计集合中的元素总数
127.0.0.1:6379> scard myset (integer) 2 # myset集合中元素总数为2
sismember 判断元素是否在这个集合中
127.0.0.1:6379> sismember myset a (integer) 0 # 0表示不在 127.0.0.1:6379> sismember myset c (integer) 1 # 1表示在
srandmember 随机从集合中返回指定个数的元素,默认返回1个
127.0.0.1:6379> srandmember myset 2 # 返回2个 1) "c" 2) "d" 127.0.0.1:6379> srandmember myset # 不指定则返回1个 "d"
spop 从集合随机删除元素(默认删除1个)
127.0.0.1:6379> spop myset "d" # 元素d被删除
smembers 查看集合内所有元素
127.0.0.1:6379> smembers myset 1) "c" 2) "three" 3) "two" 4) "one" 5) "four"
sinter 求多个集合交集
# 定义两个集合,这两个集合在后面会多次用到 127.0.0.1:6379> sadd user:1 follow it music his sports (integer) 5 127.0.0.1:6379> sadd user:2 follow it news ent sports (integer) 5 127.0.0.1:6379> sinter user:1 user:2 # 返回的是两个集合共有的元素 1) "it" 2) "follow" 3) "sports"
sdiff 求多个集合不同的元素
注:在求不同的元素时,返回的结果是第一个集合有,而第二个集合中没有的元素。
127.0.0.1:6379> sdiff user:1 user:2 1) "music" 2) "his" 127.0.0.1:6379> sdiff user:2 user:1 1) "news" 2) "ent"
**sunion 求多个集合之间的并集(相当于去重后显示)** 127.0.0.1:6379> sunion user:1 user:2 1) "sports" 2) "news" 3) "music" 4) "his" 5) "ent" 6) "it" 7) "follow"
xxxxx+store 差集/并集/交集的结果保存
# 将两个集合中共有的元素保存到集合user:1_2:inter 127.0.0.1:6379> sinterstore user:1_2:inter user:1 user:2 (integer) 3 127.0.0.1:6379> smembers user:1_2:inter 1) "sports" 2) "it" 3) "follow" # 将两个集合中不同的元素保存到集合user:1_2:sdiff 127.0.0.1:6379> sdiffstore user:1_2:sdiff user:1 user:2 (integer) 2 127.0.0.1:6379> smembers user:1_2:sdiff 1) "his" 2) "music" # 将两个集合的并集保存到集合user:1_2:sunion 127.0.0.1:6379> smembers user:1_2:sunion 1) "sports" 2) "news" 3) "music" 4) "his" 5) "ent" 6) "it" 7) "follow"
集合的内部编码
集合的内部编码有两种: * intset:整数集合,当集合中的元素都是整数且元素个数小于set-max-intest-entries配置(默认512个)时,Redis会选用intset来作为集合的内部实现,从而减少内存的使用。 * hashtable:哈希表,当集合类型无法满足intset的条件时,Redis会使用hashtable作为集合的内部实现。
127.0.0.1:6379> sadd setkey 1 2 3 4 (integer) 4 127.0.0.1:6379> object encoding setkey # 内部编码为intset "intset" 127.0.0.1:6379> sadd setkey a (integer) 1 127.0.0.1:6379> object encoding setkey # 内部编码为hashtable "hashtable"
集合的使用场景
集合类型比较典型的使用场景是标签(tag)。比如一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻比较感兴趣,这些兴趣点就是标签。有了这些数据就可以得到喜欢同一个标签的人,以及用户的共同喜好的标签,这些数据对于用户体验以及增强用户黏度比较重要。例如一个电子商务的网站会对不同标签的用户做不同类型的推荐,比如对数码产品比较感兴趣的人,在各个页面或者通过邮件的形式给他们推存最新的数码产品,通常会为网站带来史多的利益。
有序集合
Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 232 – 1 (4294967295, 每个集合可存储40多亿个成员)。
zadd 集合内添加成员
格式为:ZADD key score1 member1 [score2 member2]…..
127.0.0.1:6379> zadd javapoint 1 redis 4 mysql 2 python 3 shell
zcard 计算集合内成员个数
127.0.0.1:6379> zcard javapoint (integer) 4
zscore 求某个成员的分数
127.0.0.1:6379> zscore javapoint shell "3"
zrank 求集合内元素的排名(由低到高,从0开始排名)
127.0.0.1:6379> zrank javapoint mysql (integer) 3 127.0.0.1:6379> zrank javapoint redis (integer) 0 127.0.0.1:6379> zrank javapoint shell (integer) 2
zrevrange 同求排名(由高到低,从0开始排名)
127.0.0.1:6379> zrevrank javapoint mysql (integer) 0 127.0.0.1:6379> zrevrank javapoint shell (integer) 1
还有很多我就不举例了,列个表吧,如下: