redis事务和管道浅析
2016-03-18 23:08
369 查看
redis事务和管道浅析
redis IO模型
redis的事务首先得从redis的网络IO模型讲起,redis使用的是“单线程-多路复用IO模型”,自己封装了一个简单的AeEvent事件处理框架,避免了使用锁,对于IO操作来说,单线程可以将速度发挥到最大。IO模型浅析
基于这种机制,redis轻而易举的实现了事务的隔离性,因为所有事务都是串行执行的。
redis 通讯机制
redis客户端服务端通过tcp连接进行数据交互,采用自己的实现的二进制安全协议。在这个协议中, 所有发送至 Redis 服务器的参数都是二进制安全(binary safe)的。
以下是这个协议的一般形式:
<参数数量> CR LF
$<参数 1 的字节数量> CR LF
<参数 1 的数据> CR LF
…
$<参数 N 的字节数量> CR LF
<参数 N 的数据> CR LF
译注:命令本身也作为协议的其中一个参数来发送。
举个例子, 以下是一个命令协议的打印版本:
*3
$3
SET
$5
mykey
$7
myvalue
这个命令的实际协议值如下:
“*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n”
Redis 命令会返回多种不同类型的回复。
通过检查服务器发回数据的第一个字节, 可以确定这个回复是什么类型:
状态回复(status reply)的第一个字节是 “+”
错误回复(error reply)的第一个字节是 “-“
整数回复(integer reply)的第一个字节是 “:”
批量回复(bulk reply)的第一个字节是 “$”
多条批量回复(multi bulk reply)的第一个字节是 “*”
redis管道-pipeline
执行一个命令就进行一次请求和响应是相当消耗网络IO的,因此,redis实现了管道。Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,服务端接收到所有管道命令后,一次性执行管道中所有命令,并最终一次性读取所有服务端的响应。pipeline期间将“独占”链接,此期间将不能进行非“管道”类型的其他操作,直到pipeline关闭。因此具备原子性、隔离性。
管道实例:
$(echo -en “PING\r\n SET w3ckey redis\r\nGET w3ckey\r\nINCR visitor\r\nINCR visitor\r\nINCR visitor\r\n”; sleep 10) | nc localhost 6379+PONG
+OK
redis
:1
:2
:3
redis事务
redis事务与管道类似,但是比管道拥有更多特性。缺点是每次命令入队都会进行网络交互。事务可以一次执行多个命令, 并且带有以下两个重要的保证:
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
MULTI 、 EXEC 、 DISCARD 和 WATCH 命令是 Redis 事务的基础。
MULTI 命令用于开启一个事务,它总是返回 OK 。
EXEC 命令负责触发并执行事务中的所有命令
DISCARD 命令时, 事务会被放弃, 事务队列会被清空, 并且客户端会从事务状态中退出
而WATCH 命令可以监视键的改动,为 Redis 事务提供 check-and-set (CAS)行为。
事务命令详情请见redisfans-Redis 命令参考
事务例子
redis 127.0.0.1:6379> MULTIOK
redis 127.0.0.1:6379> SET book-name “Mastering C++ in 21 days”
QUEUED
redis 127.0.0.1:6379> GET book-name
QUEUED
redis 127.0.0.1:6379> SADD tag “C++” “Programming” “Mastering Series”
QUEUED
redis 127.0.0.1:6379> SMEMBERS tag
QUEUED
redis 127.0.0.1:6379> EXEC
1) OK
2) “Mastering C++ in 21 days”
3) (integer) 3
4) 1) “Mastering Series”
2) “C++”
3) “Programming”
事务机制
以下事务机制描述主要摘自《redis设计与实现》事务队列
每个redis客户端都有自己的事务状态,这个事务状态保存在客户端状态的mstate属性里。事务状态包含一个事务队列用来保存入队命令。事务队列以先进先出(FIFO)的方式保存命令事务执行
当一个处于事务状态的客户端向服务器发送EXEC命令时,这个EXEC命令将立即被服务器执行。服务器遍历这个客户端的事务队列,执行队列中保存的所有命令,最后将执行命令所得到的所有结果一次性返回给客户端Watch命令
Watch命令是redis对乐观锁的实现。他可以在exec命令执行之前,监视任意数量的数据库键,并在exec命令执行时,检查被监视的键是否至少有一个已经被修改过,如果是的话,服务器将拒接执行事务,并向客户端返回代表事务执行失败的空回复。Watch使用字典实现
当一个c10086客户端执行如下watch命令后
Redis> watch “name” “age”
当键name被修改后c1,c2,c10086三个客户端的REDIS_DIRTY_CAS标识被打开。
当键age被修改后c3,c10086两个个客户端的REDIS_DIRTY_CAS标识被打开。
判断事务是否安全
当服务器接收到一个客户端发来的EXEC命令时,服务器会根据这个客户端是否打开了REDIS_DIRTY_CAS标识来决定是否执行事务Redis事务的ACID性质
ACID描述请见百度百科原子性Atomicity
对于redis来说,事务队列中的命令要么全部执行,要么就一个都不执行,因此redis的事务是具有原子性的。一致性Consistency
为了追求性能,redis通过谨慎的错误检测和简单的设计来保证事务的一致性。入队错误
如果一个事务在入队命令过程中,出现命令不存在,或者命令格式不正确等情况,redis 将拒接执行这个事务
执行错误
执行错误是一些不能在入队是被服务器发现的错误,这些命令只会在执行的时候被触发
即使在事务的执行过程中发生了错误,服务器也不会中断事务的执行,它会继续执行队列中余下的命令。
因为在事务执行过程中,出错的命令会被服务器识别出来,错误命令不会对数据库进行任何修改,也不会对事务的一致性产生影响。
(与关系型数据库如mysql的一致性实现不同,mysql中出现执行错误,会回滚所有的已执行命令,将状态还原到事务的起点,这也是redis事务不完善之处)
3.事务执行中,服务器停机
如果redis服务器在执行过程中停机
在无持久化模式下: 重启后服务器是空白的,因此数据是一致的
在rdb和AOF模式下:
重启后服务器根据现有的rdb和aof文件来恢复数据,会将数据库还原到一个一致性状态,即回到未结束事务的起始状态,如果找不到文件,则数据库是空白的,也保证了一致性
隔离线Isolation
因为redis是使用单线程的方式来执行事务,并且服务器保证不会在事务执行期间对事务中断,因此,redis的事务总是以串行的方式运行的,保证了隔离线持久性Durability
因为redis的事务不过是简单的用队列包裹起一组redis命令,redis并没有对事务提供额外的持久化功能,因此redis事务的持久性由redis所使用的持久化模式决定。在单纯的内存模式下,事务肯定是不持久的。
在 RDB 模式下,服务器可能在事务执行之后、RDB 文件更新之前的这段时间失败,所以
RDB 模式下的 Redis 事务也是不持久的。
在 AOF 的“总是 SYNC ”模式下,事务的每条命令在执行成功之后,都会立即调用 fsync 或 fdatasync 将事务数据写入到 AOF 文件。但是,这种保存是由后台线程进行的,主线程不会阻塞直到保存成功,所以从命令执行成功到数据保存到硬盘之间,还是有一段非常小的间隔,所以这种模式下的事务也是不持久的。
其他 AOF 模式也和“总是 SYNC ”模式类似,所以它们都是不持久的。
redis事务的不足
redis事务在执行的中途遇到错误,不会回滚,而是继续执行后续命令持久性的不足,如上文持久性所述
遇到有查询的情况穿插在事务中间,不会返回结果;
事务中的每条命令都与redis服务器进行了一次网络交互
相关文章推荐
- redis对于key的操作命令
- Kafka+Spark Streaming+Redis实时系统实践
- redis 命令大全
- Java的redis 操作类-优化通用版本
- redis 主从复制及切换
- StackExchange.Redis 使用资料
- redis 在.net 平台的 StackExchange.Redis
- redis 认识
- redis安装+redis集群配置+phpredis扩展安装
- Redis开源代码读书笔记三(zmalloc模块)
- Nginx+Redis+Tomcat实现session共享的集群
- redis Java应用
- Redis Cluster 集群的实现和管理
- Redis Desktop Manager桌面管理工具
- redis hash
- Redis 的常用数据类型
- Redis系列(一)遇见Redis
- Java中使用Jedis操作Redis
- redis 学习笔记-client端示例代码
- Redis五种数据类型命令介绍(4)