第八节: Redis事务、pub/sub、PipeLine-管道、benchmark性能测试详解
一. 事务
1. 概念补充
(1). 原子性
一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
2. redis事务说明
Redis的事务并不是我们传统意义上理解的事务,我们都知道 单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
总结:
(1). Redis事务中如果有某一条命令执行失败,之前的命令不会回滚,其后的命令仍然会被继续执行→ →鉴于这个原因,所以说redis的事务严格意义上来说是不具备原子性的。
(2). Redis事务中所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
(3). 在事务开启之前,如果客户端与服务器之间出现通讯故障并导致网络断开,其后所有待执行的语句都将不会被服务器执行。然而如果网络中断事件是发生在客户端执行EXEC命令之后,那么该事务中的所有命令都会被服务器执行。
(4). 当使用Append-Only模式时,Redis会通过调用系统函数write将该事务内的所有写操作在本次调用中全部写入磁盘。然而如果在写入的过程中出现系统崩溃,如电源故障导致的宕机,那么此时也许只有部分数据被写入到磁盘,而另外一部分数据却已经丢失。Redis服务器会在重新启动时执行一系列必要的一致性检测,一旦发现类似问题,就会立即退出并给出相应的错误提示。此时,我们就要充分利用Redis工具包中提供的redis-check-aof工具,该工具可以帮助我们定位到数据不一致的错误,并将已经写入的部分数据进行回滚。修复之后我们就可以再次重新启动Redis服务器了。
3. 流程/指令
(1)multi 开启事务
(2)大量指令入队
(3)exec执行事务块内命令,截止此处一个事务已经结束。
(4)discard 取消事务
(5)watch 监视一个或多个key,如果事务执行前key被改动,事务将打断。unwatch 取消监视。
4. 测试
(1). 正常事务执行
(2). 取消事务
5. Redis为什么不支持事务回滚?(官方解释)
这种做法的优点:
(1). Redis 命令只会因为错误的语法而失败,或是命令用在了错误类型的键上面,这些问题不能在入队时发现,这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中.
(2). 因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。
有种观点认为 Redis 处理事务的做法会产生 bug , 然而需要注意的是, 在通常情况下, 回滚并不能解决编程错误带来的问题。 举个例子, 如果你本来想通过 INCR 命令将键的值加上 1 , 却不小心加上了 2 , 又或者对错误类型的键执行了 INCR , 回滚是没有办法处理这些情况的。鉴于没有任何机制能避免程序员自己造成的错误, 并且这类错误通常不会在生产环境中出现, 所以 Redis 选择了更简单、更快速的无回滚方式来处理事务。
补充一种情况:错误指令在入队的时候,redis是能检测出来的,当exec的时候,全部命令都不会执行。但是开发层次的错误如同上面所说 incr 1,但是不小 incr 2, redis无法判断。
参考:https://www.runoob.com/redis/redis-transactions.html
二. 发布订阅(pub/sub)
1. 简介
Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息,Redis 客户端可以订阅任意数量的频道。
Redis 本身的 发布订阅 (pub/sub) 来实现消息队列的功能,有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃。Redis Stream这个新的数据结构 提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。但还是建议使用专业的消息队列,如:RabbitMq 、RocketMq等等。
2. 指令说明
(1). subscribe:订阅给定的一个或多个频道的信息(频道不存在的时候会创建频道),返回接收到的信息。
SUBSCRIBE channel [channel2 ....]
(2). publish:将信息 message 发送到指定的频道 channel,接收到信息 message 的订阅者数量。
publish channel message
(3). unsubscribe:指示客户端退订给定的频道。
unsubscribe [channel [channel ...]]
(4). psubscribe:订阅一个或多个符合给定模式的频道。
每个模式以 * 作为匹配符,比如 it* 匹配所有以 it 开头的频道( it.news 、it.blog 、 it.tweets 等等), news.* 匹配所有以 news. 开头的频道( news.it 、news.global.today 等等),诸如此类。
psubscribe pattern [pattern ...]
(5). punsubscribe:指示客户端退订所有给定模式。
punsubscribe [pattern [pattern ...]]
3. 实操
(1). 开启客户端A和客户端B,对频道 chanel1 和 chanel2同时进行订阅。
subscribe chanel1 chanel2
(2). 开启一个客户端C,向chanel1频道进行发送信息 。
(3). 查看接收到的消息。
参考:https://www.runoob.com/redis/redis-pub-sub.html 和 redis指令文档
三. PipeLine-管道
1. redis常规请求模型
Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤:
(1). 客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。
(2). 服务端处理命令,并将结果返回给客户端。
2. 管道模型
Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。
客户端可以一次性发送多个请求而不用等待服务器的响应,待所有命令都发送完后再一次性读取服务的响应,这样可以极大的降低多条命令执行的网络传输开销,管道执行多条命令的网络开销实际上只相当于一次命令执行的网络开销。需要注意到是用pipeline方式打包命令发送,redis必须在处理完所有命令前先缓存起所有命令的处理结果。打包的命令越多,缓存消耗内存也越多。所以并不是打包的命令越多越好。pipeline中发送的每个command都会被server立即执行,如果执行失败,将会在此后的响应中得到信息;也就是pipeline并不是表达“所有command都一起成功”的语义,管道中前面命令失败,后面命令不会有影响,继续执行。
jedis代码:
Pipeline pl = jedis.pipelined(); for (int i = 0; i < 10; i++) { pl.incr("pipelineKey"); pl.set("zhuge" + i, "zhuge"); } List<Object> results = pl.syncAndReturnAll(); System.out.println(results);
参考:https://www.runoob.com/redis/redis-pipelining.html
四. benchmark性能测试
1. 说明
benchmark是位于redis安装目录下的一个性能测试工具,可以同时执行n个请求来检测redis的性能。
常用指令:
#1. 用10000个请求检测本机redis的各种指令的性能 ./redis-benchmark -n 10000 -q #2. 测试指定地址指定请求的redis性能(测试set和get性能) ./redis-benchmark -h 127.0.0.1 -p 6379 -t set,get -n 10000 -q
参数说明:
2. 测试在win10下的redis3.2的性能
电脑配置:4核16G
redis默认配置情况下用10000个请求进行测试,测试结果如下图:
3. 测试centos8下redis5.0的性能
电脑配置:2核8G
redis默认配置情况下用10000个请求进行测试,测试结果如下图:
4.测试centos8下redis6.0的性能
电脑配置:2核8G,默认多线程是关闭的,需要手动开启:
redis默认配置情况下用10000个请求进行测试,测试结果如下图:
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。