Redis基础(二)

本章是整理知识内容,为强化知识长期更新。

Redis分布式锁

  • 在日常开发中,redis主要用在缓存处理。为保证缓存所以会使用集群的方式,避免缓存雪崩。都使用集群了应用层也不太可能是单应用。在分布式的情况下就会涉及分布式锁。a应用和b应用一起对某条缓存记录操作,a查完数据在内存修改,在放回去。如果这个操作两个应用同时进行就会出现并发问题。为了保证操作的原子性,所以就要使用锁来保证操作执行的顺序。

set扩展

1
2
3
4
5
# 使用set的扩展命令 一次把expire和 setnx的特性带上。下面这条命令就是 过期时间5s 并且不可以重复添加。只能删除或者到期。
192.168.31.7:6379> set lock:redis true ex 5 nx
OK
192.168.31.7:6379> set lock:redis true ex 5 nx # 第二次在设置就失败了。
(nil)
  • 但是这种分布式锁并不能完美解决问题,因为锁有过期时间,如果时间过期了但是操作依然未执行完成,后面的操作进来就可能会造成不可控影响。

异步消息列队

Redis list这个数据类型,非常适合做列队。使用rpush / lpush 操作入列,使用 lpop 和 rpop来出列队。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
192.168.31.7:6379> rpush queue a b c d 
(integer) 4
192.168.31.7:6379> llen queue
(integer) 4
# rpop 压栈
192.168.31.7:6379> lpop queue
"a"
192.168.31.7:6379> lpop queue
"b"
192.168.31.7:6379> lpop queue
"c"
192.168.31.7:6379> lpop queue
"d"
# lpop 列队
192.168.31.7:6379> lpop queue
"a"
192.168.31.7:6379> lpop queue
"b"
192.168.31.7:6379> lpop queue
"c"
192.168.31.7:6379> lpop queue
"d"
  • 但是,redis的list一旦被消费完就会被删除,为了避免客户端一直读取。建议使用阻塞列队blpop\brpop的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 使用blocking 阻塞读,1就是阻塞时间。这样可以避免不断的读。
192.168.31.7:6379> rpush queue a b c d
(integer) 4
192.168.31.7:6379> blpop queue 1
1) "queue"
2) "a"
192.168.31.7:6379> blpop queue 1
1) "queue"
2) "b"
192.168.31.7:6379> blpop queue 1
1) "queue"
2) "c"
192.168.31.7:6379> blpop queue 1
1) "queue"
2) "d"
192.168.31.7:6379> blpop queue 1 # 最后一次 list已经空了。
(nil)
(1.08s) # 阻塞了一秒

key查询

  • 虽然redis已经有keys,并不能像mysql一个提供limit这种,导致读取的太多了。Redis是一个单线程,数据太多会阻塞。scan提供了三个参数 cursor key limit分别是
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
192.168.31.7:6379> set key1 1
OK
192.168.31.7:6379> set key2 1
OK
192.168.31.7:6379> set key3 1
OK
192.168.31.7:6379> set key4 1
OK
192.168.31.7:6379> set key5 1
OK
192.168.31.7:6379> scan 0 match key* count 10
1) "0"
2) 1) "key4"
2) "key3"
3) "key2"
4) "key5"
5) "key1"

Redis事务

Redis事物分别是multi/exec/discard 对应的就是begin/commit/rollback。Redis在收到exec的指令才会执行事务列队,执行完毕后一次返回指令运行结果。但是Redis仅仅是保证了个隔离性。在使用事物的时候建议使用管道pipe,这样可以减少网络请求。

1
2
3
4
5
6
7
8
9
10
# 简单的演示事务
192.168.31.7:6379> multi
OK
192.168.31.7:6379> set name wang
QUEUED
192.168.31.7:6379> set name zhang
QUEUED
192.168.31.7:6379> exec
1) OK
2) OK
  • 事务丢弃 discard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 创建一个字符串并对value进行累加。
192.168.31.7:6379> set count 1
OK
192.168.31.7:6379> get count
"1"
192.168.31.7:6379> multi # 开启事物
OK
192.168.31.7:6379> incr count
QUEUED
192.168.31.7:6379> incr count
QUEUED
192.168.31.7:6379> discard # 丢弃事物
OK
192.168.31.7:6379> get count #检查数据。
"1"

Watch

上面的分布式锁其实是一个悲观锁,实际上可以使用watch实现乐观锁。watch会在事务开始之前,监听一个或者多个参数,当执行exec的时候redis会检查被监听的参数是否改变,如果改变了事务执行失败。这里需要注意的是watch必须在事务之前开启。

1
2
3
4
5
6
7
8
9
10
11
# 监听count
192.168.31.7:6379> watch count
OK
192.168.31.7:6379> incr count # 自增count的value。
(integer) 2
192.168.31.7:6379> multi #开启事物
OK
192.168.31.7:6379> incr count
QUEUED
192.168.31.7:6379> exec
(nil) #事务执行失败。

END