redis事务以及watch的理解

 

2018.05.14 12:04 字数 28 阅读 156评论 0喜欢 0

事务的特性

1.原子性,数据库事务中的多个操作,要么同时执行成功,要么一个也不执行,执行的时候将事务中的命令入队列,
当客户端发送执行命令时,将命令依次执行,如果遇到回滚操作,则抛弃该事务中队列的命令.
2.一致性,数据库在执行事务之前一致的,那么执行之后也一致,这其中的一致是说,数据库中没有产生一些无法解释的数据,
在redis中,主要有三种方式保证事务的一致性
    a.入队错误,如发送一个错误的命令,服务器拒绝执行
    b.执行错误,当命令能够入队,但是在实际执行中发生错误,服务器不会对数据进行修改
    c.服务器宕机
        I.无持久化模式下,重启后数据库数据丢失
        II.备份模式下,数据会进行恢复
3.隔离性,隔离性的本质是,多个事务并发执行,使其串行执行,redis本身是采用单线程,因此,redis的事务总是以
串行的方式允许(个人理解:当我开启一个事务未提交,在另外开启一个事务,这两个是串行执行的,其中因为是单线程,所以不会用阻塞的方法来阻止事务的提交,
我认为,实际只有当真正去提交时,才会去修改数据库中的数据,隔离性出现解决的问题就是脏读,幻读,不可重复读,当a事务发起一堆执行执行,b事务在获取时,
读取的依旧是数据库本身的值,而a事务在获取值时,给他返回的是queued,也就不存在脏读,幻读,不可重复读,因为读取的值看不见,实际上是并未实现真正的隔离性)
4.持久性,redis是存在内存中,有自己的持久化机制,如rdb,aof

实现的机制

1.首先介绍下redis事务的相关命令:multi:开启事务,exec:执行事务,discard:回滚操作,相对应于mysql,分别为begin,commit,rollback
2.事务的实现:
事务的实现从开始到结束通常会经历三个阶段:
    a.事务开始阶段

image.png

b.命令入队列
    当事务执行时,redis客户端有自己的事务状态,状态中含有一个事务队列以及已入队列命令的计数器,命令按照fifo的方式进入队列,事务队
    列是一个multiCmd的数组,每个multiCmd结构都保存了一个已入队命令的相关信息,整个事务状态以及multiCmd的结构如下

image.png

c.事务执行
    当处于事务状态的客户端向服务器发送exec命令时,服务器会遍历这个客户端的事务队列,执行队列中保存的所有命令,最后将结果返回给客户端

WATCH

WATCH是一个乐观锁,它可以在exec命令执行之前,监视任意数量的数据库键,并在exec命令执行时,检查被监视的键是否至少有一个已经被修改过了,如果是
服务器则拒绝执行事务,并向客户端返回代表事务执行失败的空回复.
例如:

时间

A

B

T1

watch name

T2

multi

T3

set name pmj

T4

 

set name Pmj

T5

exec

此时当A事务提交时,发现name的值已经被修改,此时服务器拒绝执行A事务,并向客户端A返回空回复

原理

每个redis数据库都保存着一个watched_keys字典,这个字典的键是某个被watch命令监视的数据库键,而字典的值则是一个链表,链表中记录了所有监视相应数据库键的客户端,数据结构如下图:

image.png

所有对数据库进行修改命令,在执行之后都会对watched_keys字典进行检查,查看是否有客户端正在监视刚刚被命令修改过的数据库键,如果有,则会将监视被修改键的reids_dirty_cas标识打开,
表示该客户端的事务安全性已经被破坏.如果在事务提交时,检测到该标识被打开,则会拒绝执行它们提交的事务,以此来保证事务的安全性.

[参考自redis设计与实现:第19章 事务]