redis常用命令2
1、STRING字符串
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set k1 v1 #设置值
OK
127.0.0.1:6379> get k1 #获得值
"v1"
127.0.0.1:6379> keys * #获得所有key
1) "k1"
127.0.0.1:6379> exists k1 #判断某个key是否存在
(integer) 1
127.0.0.1:6379> append k1 'liuxin' #追加字符串,如果key不存在相当于新增key
(integer) 8
127.0.0.1:6379> get k1
"v1liuxin"
127.0.0.1:6379>strlen k1 #获取字符串长度
(integer)13
127.0.0.1:6379> append k1 'redis'
(integer) 13
127.0.0.1:6379>
#i++
#步长i+=
127.0.0.1:6379> set views 0 #初始量为0
OK
127.0.0.1:6379> get views #获取初始量
"0"
127.0.0.1:6379> incr views #自增1
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> decr views #自减1
(integer) 1
127.0.0.1:6379> incrby views 10 #自增10,可以设置步长制定增量
(integer) 11
127.0.0.1:6379> decrby views 20 #自减20
(integer) -9
127.0.0.1:6379> get views
"-9"
#替换setrange
127.0.0.1:6379> set key2 abcdefg OK 127.0.0.1:6379> get key2 "abcdefg" 127.0.0.1:6379> setrange key2 1 xx #替换指定位置开始的字符串 (integer) 7 127.0.0.1:6379> get key2 "axxdefg"
#字符串范围range 127.0.0.1:6379> set key 'hello word' #设置key的值 OK 127.0.0.1:6379> get key "hello word" 127.0.0.1:6379> getrange key 0 3 #截取字符串 "hell" 127.0.0.1:6379> getrange key 0 -1 #获取全部字符串 "hello word"
#setex(set with expire) #设置过期时间
#setnx(set if not exist) #不存在再设置(分布式锁中常用)
127.0.0.1:6379> setex key3 30 kvalue #设置key3的值为kvalue 30秒过期 OK 127.0.0.1:6379> ttl key3 (integer) 21 127.0.0.1:6379> get key3 "kvalue" 127.0.0.1:6379> setnx mykey redis #如果mykey的值不存在,创建mykey (integer) 1 127.0.0.1:6379> keys * 1) "key2" 2) "mykey" 3) "key" 127.0.0.1:6379> ttl key3 (integer) -2 127.0.0.1:6379> setnx mykey 'mongodb' #如果mykey存在,创建失败 (integer) 0 127.0.0.1:6379> get mykey "redis"
#mset 批量设置
#mget 批量获取
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #同时设置多个值 OK 127.0.0.1:6379> keys * 1) "k1" 2) "k3" 3) "k2"127.0.0.1:6379> mget k1 k2 k3 #同时获取多个值 1) "v1" 2) "v2" 3) "v3" 127.0.0.1:6379> msetnx k1 v1 k4 v4 #原子性操作,要么一起成功、要么一起失败 (integer) 0 127.0.0.1:6379> get k4 (nil)
#对象
set user:1{name:zhangsan,age:22} #设置一个user:1对象 值为Json字符来保存一个对象
127.0.0.1:6379> set user:1 {name:zhangsan,age:22}
OK
127.0.0.1:6379> get user:1
"{name:zhangsan,age:22}"
127.0.0.1:6379> mset user:2:name lisi user:2:age 23
OK
127.0.0.1:6379> mget user:2:name user:2:age
1) "lisi"
2) "23"
127.0.0.1:6379>
#getset 先get后set
127.0.0.1:6379> getset db redis #如果不存在值则返回nil (nil) 127.0.0.1:6379> get db "redis" 127.0.0.1:6379> getset db mongodb #如果值存在,获取原来的值,并设置新的值 "redis" 127.0.0.1:6379> get db "mongodb" 127.0.0.1:6379>
2、list命令:list命令都是以l开头,不区分大小写
127.0.0.1:6379> lpush list one #将1个或多个值插入列表头部(左) (integer) 1 127.0.0.1:6379> lpush list two (integer) 2 127.0.0.1:6379> lpush list three (integer) 3 127.0.0.1:6379> lrange list 0 1 #通过区间获取具体的值 1) "three" 2) "two" 127.0.0.1:6379> lrange list 0 -1 #获取list中的值 1) "three" 2) "two" 3) "one" 127.0.0.1:6379> rpush list right #将1个或多个值插入列表尾部(右) (integer) 4 127.0.0.1:6379> lrange list 0 -1 1) "three" 2) "two" 3) "one" 4) "right" 127.0.0.1:6379>
#lpop 移除最左边元素
#rpop 移除最右边元素
127.0.0.1:6379> LRANGE list 0 -1 1) "three" 2) "two" 3) "one" 4) "right" 127.0.0.1:6379> lpop list "three" 127.0.0.1:6379> rpop list "right" 127.0.0.1:6379> lrange list 0 -1 1) "two" 2) "one" 127.0.0.1:6379>
#lindex通过下标获取值
127.0.0.1:6379> lrange list 0 -1 1) "two" 2) "one" 127.0.0.1:6379> lindex list 0 #通过下标获取List某一个值 "two" 127.0.0.1:6379> lindex list 1 "one" 127.0.0.1:6379>
#llen获取list长度
127.0.0.1:6379> lpush list one (integer) 1 127.0.0.1:6379> lpush list two (integer) 2 127.0.0.1:6379> lpush list three (integer) 3 127.0.0.1:6379> llen list #返回列表长度 (integer) 3 127.0.0.1:6379>
#lrem移除指定的值
127.0.0.1:6379> lpush list three (integer) 4 127.0.0.1:6379> lrange list 0 -1 1) "three" 2) "three" 3) "two" 4) "one" 127.0.0.1:6379> lrem list 1 one #移除list集合中指定个数的value,精确匹配(移除1个,值为one) (integer) 1 127.0.0.1:6379> lrange list 0 -1 1) "three" 2) "three" 3) "two" 127.0.0.1:6379> lrem list 1 three (integer) 1 127.0.0.1:6379> lrange list 0 -1 1) "three" 2) "two" 127.0.0.1:6379> lpush list three (integer) 3 127.0.0.1:6379> lrange list 0 -1 1) "three" 2) "three" 3) "two" 127.0.0.1:6379> lrem list 2 three #移除list 集合中2个three (integer) 2 127.0.0.1:6379> lrange list 0 -1 1) "two" 127.0.0.1:6379>
#ltrim截取字符
127.0.0.1:6379> lpush mylist hello (integer) 1 127.0.0.1:6379> lpush mylist hello1 (integer) 2 127.0.0.1:6379> lpush mylist hello2 (integer) 3 127.0.0.1:6379> lpush mylist hello3 (integer) 4 127.0.0.1:6379> lrange list 0 -1 (empty array) 127.0.0.1:6379> lrange mylist 0 -1 1) "hello3" 2) "hello2" 3) "hello1" 4) "hello" 127.0.0.1:6379> ltrim mylist 1 2 #通过下标截取指定位置开始指定长度的字符,这个list已经改变,只剩下截取到的元素 OK 127.0.0.1:6379> lrange mylist 0 -1 1) "hello2" 2) "hello1" 127.0.0.1:6379>
#rpoplpush 移除列表最后一个元素,将其添加到新的列表中
127.0.0.1:6379> rpush mylist hello (integer) 1 127.0.0.1:6379> rpush mylist hello1 (integer) 2 127.0.0.1:6379> lpush mylist hello2 (integer) 3 127.0.0.1:6379> lrange mylist 0 -1 1) "hello2" 2) "hello" 3) "hello1" 127.0.0.1:6379> rpoplpush mylist myotherlist #移除最后一个元素hello1,将其添加到新的列表中 "hello1" 127.0.0.1:6379> lrange mylist 0 -1 1) "hello2" 2) "hello" 127.0.0.1:6379> lrange myotherlist 0 -1 # 1) "hello1" 127.0.0.1:6379>
#lset 将列表中指定下标的值替换为另一个值,更新操作
127.0.0.1:6379> exists list #判断这个列表是否存在 (integer) 0 127.0.0.1:6379> lset list 0 item (error) ERR no such key #如果不存在我们去更新就会报错 127.0.0.1:6379> lpush list value (integer) 1 127.0.0.1:6379> lrange list 0 0 1) "value" 127.0.0.1:6379> lset list 0 item #如果存在就更新当下列表的值 OK 127.0.0.1:6379> lrange list 0 0 1) "item" 127.0.0.1:6379> lset list 1 other #如果不存在,则会报错 (error) ERR index out of range 127.0.0.1:6379>
#linsert 插入命令
127.0.0.1:6379> rpush mylist hello (integer) 1 127.0.0.1:6379> rpush mylist word (integer) 2 127.0.0.1:6379> linsert mylist after 'word' 'other' #在word之后插入 (integer) 3 127.0.0.1:6379> lrange mylist 0 -1 1) "hello" 2) "word" 3) "other" 127.0.0.1:6379> linsert mylist before 'hello' 'new' #在hello之前插入 (integer) 4 127.0.0.1:6379> lrange mylist 0 -1 1) "new" 2) "hello" 3) "word" 4) "other" 127.0.0.1:6379>
3、set
#随机删除key
127.0.0.1:6379> smembers myset 1) "xiaomin" 2) "nihao" 127.0.0.1:6379> sopo myset (error) ERR unknown command `sopo`, with args beginning with: `myset`, 127.0.0.1:6379> spop myset #随机删除一些set集合中的元素 "nihao" 127.0.0.1:6379> smembers myset 1) "xiaomin" 127.0.0.1:6379>
#移动元素到指定位置
127.0.0.1:6379> sadd myset liu (integer) 1 127.0.0.1:6379> sadd myset xin (integer) 1 127.0.0.1:6379> sadd myset nihao (integer) 1 127.0.0.1:6379> sadd myset2 hello (integer) 1 127.0.0.1:6379> smembers myset 1) "nihao" 2) "liu" 3) "xin" 127.0.0.1:6379> smembers myset2 1) "hello" 127.0.0.1:6379> smove myset myset2 liu #将一个指定元素移动到另一个集合 (integer) 1 127.0.0.1:6379> smembers myset 1) "nihao" 2) "xin" 127.0.0.1:6379> smembers myset2 1) "liu" 2) "hello" 127.0.0.1:6379>
4、bitmap
5、事务
redis事务本质:一组命令的集合,一个事务中所有命令都会被序列化,在事务执行过程中,会按照顺序执行。
一次性、顺序性、排他性,执行一系列的命令。
-----set set set ...----
Redis事务没有隔离级别的概念!
所有的命令在事务中并没有直接被执行,只有在发起执行命令的时候才会执行!exec
Redis单条命令是有原子性的,但事务不保证原子性!
redis的事务:
- 开启事务(multi)
- 命令入队()
- 执行事务(exec)
127.0.0.1:6379> clear 127.0.0.1:6379> multi #开启事务 OK
#命令入队 127.0.0.1:6379(TX)> set k1 v1 QUEUED 127.0.0.1:6379(TX)> set k2 v2 QUEUED 127.0.0.1:6379(TX)> get k2 QUEUED 127.0.0.1:6379(TX)> set k3 v3 QUEUED 127.0.0.1:6379(TX)> exec #执行事务 1) OK 2) OK 3) "v2" 4) OK 127.0.0.1:6379>
#放弃事务
127.0.0.1:6379> multi #开启事务 OK 127.0.0.1:6379(TX)> set k1 v1 QUEUED 127.0.0.1:6379(TX)> set k2 v2 QUEUED 127.0.0.1:6379(TX)> set k4 v4 QUEUED 127.0.0.1:6379(TX)> DISCARD #取消事务 OK 127.0.0.1:6379> get k4 #事务队列中的命令都不会被执行 (nil) 127.0.0.1:6379>
redis事务执行时常见的有2种错误
①编译型异常(代码有异常,命令有错),事务中所有命令都不会被执行
127.0.0.1:6379(TX)> set k1 v1 QUEUED 127.0.0.1:6379(TX)> set k2 v2 QUEUED 127.0.0.1:6379(TX)> set k3 v3 QUEUED 127.0.0.1:6379(TX)> getset k3 #命令出错 (error) ERR wrong number of arguments for 'getset' command 127.0.0.1:6379(TX)> set k4 v4 QUEUED 127.0.0.1:6379(TX)> set k5 v5 QUEUED 127.0.0.1:6379(TX)> exec #执行事务出错 (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get k5 (nil) 127.0.0.1:6379> get k4 #所有命令都不会被执行 (nil) 127.0.0.1:6379>
②运行时异常(1/0),如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常
127.0.0.1:6379> set k1 v1 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> incr k1 #执行时候失败 QUEUED 127.0.0.1:6379(TX)> set k2 v2 QUEUED 127.0.0.1:6379(TX)> set k3 v3 QUEUED 127.0.0.1:6379(TX)> get k3 QUEUED 127.0.0.1:6379(TX)> exec 1) (error) ERR value is not an integer or out of range #虽然第一条命令出错,但是依旧正常执行成功了 2) OK 3) OK 4) "v3" 127.0.0.1:6379> get k2 "v2" 127.0.0.1:6379> get k3 "v3" 127.0.0.1:6379>
6、监控 watch
- 悲观锁:很悲观,认为什么时候都会出问题,无论做什么都会加锁!
- 乐观锁
- 很乐观、认为什么时候都不会出现问题,所以不会上锁!更新数据时候去判断一下,在此期间是否有人修改数据。
- 获取version
- 更新时候比较version
#redis监视测试
127.0.0.1:6379> set money 100 OK 127.0.0.1:6379> set out 0 OK 127.0.0.1:6379> watch money #监视money对象 OK 127.0.0.1:6379> multi #事务正常结束,期间数据没有变动,这个时候正常执行成功 OK 127.0.0.1:6379(TX)> decrby money 20 QUEUED 127.0.0.1:6379(TX)> incrby out 20 QUEUED 127.0.0.1:6379(TX)> exec 1) (integer) 80 2) (integer) 20 127.0.0.1:6379>
#测试多线程修改值,使用watch可以当作redis的乐观锁操作
127.0.0.1:6379> set money 100 OK 127.0.0.1:6379> set out 10 OK 127.0.0.1:6379> watch money #监视money OK 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> decrby money 10 QUEUED 127.0.0.1:6379(TX)> incrby money 10 QUEUED 127.0.0.1:6379(TX)> exec #执行事务前,另外一个线程修改了我们的值,这个时候就会导致事务执行失败 (nil) 127.0.0.1:6379>
127.0.0.1:6379> unwatch #①获取失败后,先解锁 OK127.0.0.1:6379> watch money #②再获取最新的值,再次监视 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> decrby money 10 QUEUED 127.0.0.1:6379(TX)> incrby out 10 QUEUED 127.0.0.1:6379(TX)> exec #③比对监视的值是否发生变化,如果没有变化,可以执行成功,否则执行失败 1) (integer) 990 2) (integer) 20 127.0.0.1:6379>
7、RDB
8、AOF(Append only file)
将所有的命令记录下来,恢复的时候把这些命令全部执行一遍。默认是不开启的,需要进行手动配置,只需要将appendonly 改为yes就开启了aof,重启redis就可以生效。如果这个aof有错误,这时候redis是启动不起来的,我们需要修复aof文件。如果aof文件大于64兆,会fork一个新的进程来将我们的文件重写。
优缺点:
9、发布订阅
Redis发布订阅是一种消息通信模式:发布者(pub)发送消息,订阅者(sub)接收消息。微信、微博、订阅。redis客户端可以订阅任意数量的频道。
订阅端:
127.0.0.1:6379> subscribe kuangshengshuo #订阅kuangshengshuo频道 Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "kuangshengshuo" 3) (integer) 1 #等待接收推送的消息 1) "message" #消息 2) "kuangshengshuo" #频道 3) "nihao" #消息内容 1) "message" 2) "kuangshengshuo" 3) "word"
发送端:
127.0.0.1:6379> publish kuangshengshuo nihao #发布者发布消息到频道 (integer) 1 127.0.0.1:6379> publish kuangshengshuo word #发布者发布消息到订阅频道 (integer) 1 127.0.0.1:6379>
10、主从复制
主从复制、读写分离,80%情况下是进行读操作,最低配置是一主二从。
环境配置:
127.0.0.1:6379> info replication #查看当前库的信息 # Replication role:master #角色 connected_slaves:0 #没有从机 master_failover_state:no-failover master_replid:d6602c86062dca18863ae603704b1949c8e6219e master_replid2:0000000000000000000000000000000000000000 master_repl_offset:0 second_repl_offset:-1 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 127.0.0.1:6379>
修改配置:
①端口 ②pid名字 ③log文件名字 ④dump.rdb名字
配置完成后启动服务,默认情况下每台服务器都是主节点,一般情况下只配置从机
一主(79)二从(80,81)
#6380从机信息
127.0.0.1:6380> slaveof 127.0.0.1 6379 #6380端口服务器认6379为主机 OK 127.0.0.1:6380> info replication # Replication role:slave #当前角色 master_host:127.0.0.1 #主机信息 master_port:6379 master_link_status:up master_last_io_seconds_ago:7 master_sync_in_progress:0 slave_read_repl_offset:14 slave_repl_offset:14 slave_priority:100 slave_read_only:1 replica_announced:1 connected_slaves:0 master_failover_state:no-failover master_replid:2f8e64512dfcbfb79eb4a3b2c23a35e602869c5c master_replid2:0000000000000000000000000000000000000000 master_repl_offset:14 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:14 127.0.0.1:6380>
#6379主机信息
127.0.0.1:6379> info replication # Replication role:master connected_slaves:1 #从机配置 slave0:ip=127.0.0.1,port=6380,state=online,offset=182,lag=1 #从机信息 master_failover_state:no-failover master_replid:2f8e64512dfcbfb79eb4a3b2c23a35e602869c5c master_replid2:0000000000000000000000000000000000000000 master_repl_offset:182 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:182 127.0.0.1:6379>
主机可以写,从机只能读,主机的信息和数据会自动被从机保存。
127.0.0.1:6379> set k1 v1 #主机可以写 OK 127.0.0.1:6379> keys * 1) "k1" 127.0.0.1:6379>
(empty array) 127.0.0.1:6380> clear 127.0.0.1:6380> get k1 "v1" 127.0.0.1:6380> set k2 v2 #从机不能写 (error) READONLY You can't write against a read only replica. 127.0.0.1:6380>
测试:主机断开连接,从机依旧连接到主机,但是没有写操作,这个时候如果主机回来,从机依旧可以直接获取到主机写的信息。
如果是使用命令行配置的主从,从机如果重启就会变回主机,只要变回从机就会立马从主机中获取数据。
配置方式二:层层链路:上一个主节点连接下一个从节点
11、哨兵模式
我们目前状态是一主二从:
1、配置哨兵文件sentinel.conf
#sentinel monitor 被监控的名称 Host port 1 #数字1代表当主机挂了,从机投票让谁接替成为主机,票数最多的就会成为主机
sentinel monitor myredis 127.0.0.1 6379 1
2、启动哨兵
[root@izm5egx7lv78u7ac9pk8ckz bin]# redis-sentinel kconfig/sentinel.conf #启动哨兵 28440:X 16 Nov 2021 17:00:22.392 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 28440:X 16 Nov 2021 17:00:22.392 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=28440, just started 28440:X 16 Nov 2021 17:00:22.392 # Configuration loaded 28440:X 16 Nov 2021 17:00:22.393 * monotonic clock: POSIX clock_gettime _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 6.2.6 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in sentinel mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 26379 | `-._ `._ / _.-' | PID: 28440 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | https://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 28440:X 16 Nov 2021 17:00:22.393 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 28440:X 16 Nov 2021 17:00:22.396 # Sentinel ID is 580c7dc3ba77902a1ae27007ecd50f38e8ba1c98 28440:X 16 Nov 2021 17:00:22.396 # +monitor master myredis 127.0.0.1 6379 quorum 1 28440:X 16 Nov 2021 17:00:22.397 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379 28440:X 16 Nov 2021 17:00:22.400 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
如果主机宕机:master节点断开,这时候会从从机随机选择一个服务器(这里面有一个投票算法)。
12、缓存穿透