redis简介(二) 位操作 订阅发布 事务 备份等

1. 字符串中位操作的应用场景

1.1 字符串设置比特位

场景一: 标识文章是否已经阅读。假设标识文章“协议”的内容如下

1bit

1bit

1bit

1bit

1bit

1bit

1bit

1bit

其他内容

是否阅读

是否收藏

是否点赞

是否转发

预留

预留

预留

预留

 

前八位,用于标识用户相对于文章的一些状态,读写这些状态,我们就可以使用setbit和getbit来操作,如下

127.0.0.1:6379> set article "\x00thisisaarticle,balabala..."
OK
127.0.0.1:6379> get article
"\x00thisisaarticle,balabala..."
127.0.0.1:6379> setbit article 0 1
(integer) 0
127.0.0.1:6379> setbit article 1 1
(integer) 0
127.0.0.1:6379> setbit article 2 1
(integer) 0
127.0.0.1:6379> setbit article 3 1
(integer) 0
127.0.0.1:6379> get article
"\xf0thisisaarticle,balabala..."
127.0.0.1:6379> getbit article 0
(integer) 1
127.0.0.1:6379> getbit article 1
(integer) 1
127.0.0.1:6379> getbit article 2
(integer) 1
127.0.0.1:6379> getbit article 3
(integer) 1
127.0.0.1:6379> getbit article 4
(integer) 0
127.0.0.1:6379> getbit article 5
(integer) 0
127.0.0.1:6379> getbit article 6
(integer) 0
127.0.0.1:6379> getbit article 7
(integer) 0

1.2 错误码

自定义协议 前16位表示错误码,后面表示发生错误时间。如下

127.0.0.1:6379> set errmsg "\x00\x00202105111200"
OK
127.0.0.1:6379> get errmsg
"\x00\x00202105111200"
127.0.0.1:6379> setbit errmsg 15 1
(integer) 0
127.0.0.1:6379> get errmsg
"\x00\x01202105111200"
127.0.0.1:6379> setbit errmsg 14 1
(integer) 0
127.0.0.1:6379> get errmsg
"\x00\x03202105111200"

2. redis中存储对象

redis hash 是一个基于string类型 field、value的映射表,它非常适合存储对象。基本操作如下

127.0.0.1:6379> hmset xiaoming chinese 85 english 83 math 90
OK
127.0.0.1:6379> hgetall xiaoming
1) "chinese"
2) "85"
3) "english"
4) "83"
5) "math"
6) "90"
127.0.0.1:6379> hget xiaoming chinese
"85"
127.0.0.1:6379> hget xiaoming math
"90"
127.0.0.1:6379> hget xiaoming english
"83"
127.0.0.1:6379> hset xiaoming math 99
(integer) 0
127.0.0.1:6379> hget xiaoming math
"99"

不过,不结合其他数据结构,没办法遍历全部的hashmap。hashmap各个key之间相互独立,不存在联系。事实上多数情况下,也不需要存在联系。

另外,redis还提供链表list、集合set、有序集合等数据结构。

3. 发布订阅

redis实现了发布/订阅的消息传递机制,所以它通常也可以作为共享队列使用。下面举一个例子

1. 第一个客户端订阅beijingtv 和 tianjingtv 两个channel

127.0.0.1:6379> SUBSCRIBE beijingtv tianjingtv
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "beijingtv"
3) (integer) 1
1) "subscribe"
2) "tianjingtv"
3) (integer) 2

2. 第二个客户端发布beijingtv消息

127.0.0.1:6379> publish beijingtv "tianqiyubao:today rain"

3. 第三个客户端发布tianjingtv消息

127.0.0.1:6379> publish tianjingtv "welcome to tianjin"

4. 第一个客户端收到了后面两者的消息

127.0.0.1:6379> SUBSCRIBE beijingtv tianjingtv
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "beijingtv"
3) (integer) 1
1) "subscribe"
2) "tianjingtv"
3) (integer) 2
1) "message"
2) "beijingtv"
3) "tianqiyubao:today rain"
1) "message"
2) "tianjingtv"
3) "welcome to tianjin"

发送者和接受者都支持一个以上。

4. 事务

涉及到多个客户端,就会涉及到并发,考虑到并发,就得有一种解决读写冲突的方案。redis中实现了事务来解决这个问题。

事务可以一次执行一组命令,并具有两个重要保证

  • 事务中所有命令被序列化顺序执行。永远不会被插入其他客户端的请求或者事务之外的命令,这样可以确保命令得到不干扰的执行。
  • 所有命令都将被处理,或者不处理任何命令,因此Redis事务也是原子的。但是,如果有命令执行失败,其他命令也是能继续执行的。redis的这个原子性,在处理上,和我们一般理解的数据库事务有区别,后面会再讨论。

4.1 基本用法

使用MULTI进入redis事务,每条命令入队列后,答复queued。调用exec后,就执行所有命令 。如果调用DISCARD,则放弃当前事务,事务中的命令将不再执行 。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> incr number1
QUEUED
127.0.0.1:6379(TX)> incr number2
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 1
2) (integer) 1
127.0.0.1:6379> get number1
"1"
127.0.0.1:6379> get number2
"1"
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> incr number1
QUEUED
127.0.0.1:6379(TX)> incr number2
QUEUED
127.0.0.1:6379(TX)> DISCARD
OK
127.0.0.1:6379> get number1
"1"
127.0.0.1:6379> get number2
"1"

4.2 事务中的错误

在事务期间,可能会遇到两种错误

  • 命令无法入队,因此exec之前可能会报错。通常是命令语法上的错误,或者内存不足。
  • exec之后,命令失败

如下

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set num1 111 2222
QUEUED
127.0.0.1:6379(TX)> set num2 333
QUEUED
127.0.0.1:6379(TX)> exec
1) (error) ERR syntax error
2) OK
127.0.0.1:6379> get num1
(nil)
127.0.0.1:6379> get num2
"333"
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set str abc
QUEUED
127.0.0.1:6379(TX)> lpop str
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> get str
"abc"

我们可以看到,即使命令失败,队列中的所有其他命令也会得到处理。

4.3 redis为什么不支持回滚?

在一般的数据库中,如果事务中间执行错误数据会回滚,但redis显然不是。不支持的回滚的理由主要有以下两个

  • 仅当语法错误或者对数据类型操作错误时,redis命令才会失败,也就是说这可以归为“编译错误”
  • 不支持回滚,可以让Redis更精炼

但我们思考下,回滚通常也不能使我们避开错误。我们可以通过其他途径恢复错误之前的上下文,而不是通过redis。

4.4 通过CAS实现的乐观锁

乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于读操作多的场景,这样可以提高程序的吞吐量。

watch为redis事务提供乐观锁。它用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。如下场景,客户端2watch mykey之后,客户端1对其做出了修改,那么再在事务中修改mykey,exec则会报错,返回nil。这样就提供了更可靠的并发支持。

还有一个命令是unwatch,和watch相对,它用于取消对key的监控。当exec执行之后,前面的被watch的key都会自动unwatch。

5. 数据备份恢复

save命令,保存数据,会在redis目录下保存一个dump.rdb的文件

127.0.0.1:6379> save
OK
(1.83s)
127.0.0.1:6379> config get dir
1) "dir"
2) "/var/lib/redis"
127.0.0.1:6379> quit
[root@localhost openfile]# file /var/lib/redis/dump.rdb 
/var/lib/redis/dump.rdb: data

恢复也很简单,拷贝备份的dump.rdb,放到redis目录重启redis即可

采用bgsave在后台执行备份,适合redis中有大量数据的场景。

127.0.0.1:6379> bgsave
Background saving started

参考:

[0] https://codedestine.com/redis-pub-sub-message-broker/

[1] https://redis.io/topics/transactions

[2] https://www.jianshu.com/p/d2ac26ca6525 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值