场景一: 标识文章是否已经阅读。假设标识文章“协议”的内容如下
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
自定义协议 前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"
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、有序集合等数据结构。
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"
发送者和接受者都支持一个以上。
涉及到多个客户端,就会涉及到并发,考虑到并发,就得有一种解决读写冲突的方案。redis中实现了事务来解决这个问题。
事务可以一次执行一组命令,并具有两个重要保证
使用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"
在事务期间,可能会遇到两种错误
如下
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"
我们可以看到,即使命令失败,队列中的所有其他命令也会得到处理。
在一般的数据库中,如果事务中间执行错误数据会回滚,但redis显然不是。不支持的回滚的理由主要有以下两个
但我们思考下,回滚通常也不能使我们避开错误。我们可以通过其他途径恢复错误之前的上下文,而不是通过redis。
乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于读操作多的场景,这样可以提高程序的吞吐量。
watch为redis事务提供乐观锁。它用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。如下场景,客户端2watch mykey之后,客户端1对其做出了修改,那么再在事务中修改mykey,exec则会报错,返回nil。这样就提供了更可靠的并发支持。
还有一个命令是unwatch,和watch相对,它用于取消对key的监控。当exec执行之后,前面的被watch的key都会自动unwatch。
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