redis 是一个高性能的key-value 数据库。作为no sql 数据库redis 与传统关系型数据库相比有简单灵活、数据结构丰富、高速读写等优点。

本文主要针对redis 在事物方面的处理与传统关系型数据库(使用mysql)进行比对,所使用的环境为ubuntu 14.04.1,mysql 5.5.49,redis 3.2.3。

1操作事务命令

mysql 使用start transaction 开启事物,rollback 回滚事物,commit 提交事物。redis 使用multi 开始事物,discard 取消事物,exec 提交事物。开始实验前为mysql 和redis 准备测试数据,tom1 账户准备1000元,tom2 账户准备500元。

(图1-数据准备)

场景1:使用事务从tom1转100元到tom2 。

步骤如下:

 

mysql

redis

开启事物

start transaction

multi

tom1 - 100

update tom1

decrty tom1 100

queued

查询tom1 余额

tom1余额减少了100

get tom1

queued

tom2 + 100

update tom2

incrby tom2 100

queued

查询tom2 余额

tom2余额增加了100

get tom2

queued

提交事物

commit

exec

执行queue中的语句。

1) (integer) 900

2) "900"

3) (integer) 600

4) "600"

查询tom1,tom2余额

select * from account where name in (‘tom1’,’tom2’);

tom1金额减少100,tom2账户增加了100

mget tom1 tom2

tom1金额减少100,tom2账户增加了100

(图2-mysql 事务执行过程)

(图3-redis 事务执行过程)

从场景1中可以看出mysql 开启事务后事务中的sql 语句在commit 之前就已经执行了sql 语句的,只是并未真正提交到数据库。redis 使用multi 开启事务后,编写的sql 语句都进入queue 队列中,待执行exec 提交事物时才一次性按进入queue 队列的顺序提交到数据库。同样针对回滚,mysql 执行rollback 会将提交的数据回滚,redis 因为没有提交到数据,使用discard 只是单纯取消在queue 中的sql 语句。

场景2: 在执行tom1 向tom2 转过程中100元的过程中使用了语法错误的sql。

步骤如下:

 

mysql

redis

开启事物

start transaction

multi

tom1 - 100

update tom1

decrty tom1 100

queued

查询tom1 余额

tom1余额减少了100

get tom1

queued

tom2 + 100

使用错误命令为tom2+100

使用错误命令为tom2+100

查询tom2 余额

tom2余额未变

get tom2

queued

提交事物

commit

exec

因为queue中存在语法错误的语句,直接discard事务中的语句

查询tom1,tom2余额

select * from account where name in (‘tom1’,’tom2’);

tom1金额减少100,tom2账户金额未变

mget tom1 tom2

tom1,tom2金额未变

(图4-mysql 事务中存在语法错误语句)

(图5-redis 事务中存在语法错误语句)

从场景2中可以看出,mysql 事务即使遇到错误的语句也会提交正确的sql 到数据库,需要程序员控制当遇到语句异常时进行回滚,redis 与mysql 不同,提交事务时当queue 中有语法错误语句会discard 整个事务中的sql 语句。

场景3: 在执行tom1 向tom2 转过程中100元的过程中使用了错误的执行对象。

步骤如下:

 

mysql

redis

开启事物

start transaction

multi

tom1 - 100

update tom1

decrty tom1 100

queued

查询tom1 余额

tom1余额减少了100

get tom1

queued

tom2 + 100

使用错误对象命令为tom2+’hundrud’

使用错误命令为tom2+’hundred’

查询tom2 余额

tom2余额未变

get tom2

queued

提交事物

commit

exec

逐条执行

查询tom1,tom2余额

select * from account where name in (‘tom1’,’tom2’);

tom1金额减少100,tom2账户金额未变

mget tom1 tom2

tom1金额减少100,tom2账户金额未变

(图6-mysql 中存在操作错误对象的sql 语句)

(图7-redis 中存在操作错误对象的sql语句)

从场景3中可以看出,mysql 和redis 事务当遇到操作对象类型不正确的时候都会提交执行事务。

2事务锁

在关系型数据库中主要通过乐观锁和悲观锁进行数据库事务的并发控制。而在redis 中是通过watch 加乐观锁对数据库进行并发控制。

mysql 悲观锁是通过select * from table where <condition> for update将数据加锁,导致其他线程或事务不能更新该数据。相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。数据版本为数据增加的一个版本标识。当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识进行更新。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对,如果数据库表当前版本号与第一次取出来的版本标识值相等,则予以更新,否则认为是过期数据。

关系型数据库mysql 实现数据版本主要有两种方式,第一种是使用版本号,第二种是使用时间戳。使用版本号时,可以在数据初始化时指定一个版本号,每次对数据的更新操作都对版本号执行+1操作。并判断当前版本号是不是该数据的最新的版本号。

(图8-mysql乐观锁)

图8展示了mysql 乐观锁的示例,开启两个mysql 客户端,客户端1执行start transaction 查询数据,这时客户端2更新了客户端1查询到的数据及version 字段,然后客户端1在第3步试图更新在第一步中查询到的数据时因version 已改变因此更新失败。

(图9-redis 乐观锁(watch))

redis 是通过watch 命令监控数据是否发生变化实现乐观锁的。如图9所示打开两个redis 客户端,客户端1对数据tom2 进行watch,打开事务更改tom1,tom2,客户端2更改客户端1中watch 的对象,步骤3客户端1提交事务由于watch 的对象被更改了,导致事务放弃执行。

3事务的ACID 性质

下面讨论下事务的ACID 性质。传统关系型数据库事务具备ACID 性质,这里不做详细讨论。

针对Redis 事务具有原子性(Atomicity),一致性(Consistency),和隔离性(Isolation),当redis 在持久化模式运行时,也具备持久性(Durability)。在场景1~3和乐观锁示例中redis 全部执行事务队列中的sql 语句即使其中sql存在操作对象错误,要么全部discard放弃执行,因此redis 事务具有原子性(Atomicity)。

redis 事务在执行过程中发生错误或进程被终结,都能保证数据的一致性(Consistency)。因为redis 使用单线程串行方式来执行事务的,在执行事务期间不会对事务进行中断(一致性),因此redis 事务也具有隔离性(Isolation)。redis 可以只将运行结果保存在内存中,但当redis 服务器使用AOF 持久化模式并appendfsync 设置为always 时,程序执行sql 后会调用sync 函数将数据保存到硬盘里,因此redis 事务也可以具有持久性(Durability)。

通过下表对redis 事务和关系型数据库事务做一个简单的总结。

 

关系型数据库(mysql)

redis

开启事务

start transaction命令

multi命令

回滚事务

使用rollback命令可以回滚事务

不能回滚事务。但使用discard命令可以放弃事务queue中的sql

提交事务

commit命令

即使遇到sql语法错误也会提交事务

exec命令

如果遇到sql语法错误会放弃事务中的sql

悲观锁

使用select ... for update实现悲观锁

乐观锁

通常使用version或时间戳来实现乐观锁

使用watch监控对象变化来实现乐观锁

原子性(Atomicity)

具备

具备

一致性(Consistency)

具备

具备

隔离性(Isolation)

具备

具备

持久性(Durability)

具备

当redis服务器使用AOF持久化模式并appendfsync设置为always时具备

转自:http://www.sohu.com/a/111695683_464008

redis事务与关系型数据库事务比较的更多相关文章

  1. Cassandra事务与关系型数据库事务有何区别

    Cassandra不会使用回滚和锁机制来实现关系型数据的ACID事务,相比较于提供原子性,隔离性和持久化,Cassandra提供最终(可调节的)一致性,让用户决定为每个事务提供强一致性或者最终一致性. ...

  2. Java Redis系列1 关系型数据库与非关系型数据库的优缺点及概念

    Java Redis系列1 关系型数据库与非关系型数据库的优缺点及概念 在学习redis之前我们先来学习两个概念,即什么是关系型数据库什么是非关系型数据库,二者的区别是什么,二者的关系又是什么? ** ...

  3. 数据库事务隔离级ORACLE数据库事务隔离级别介绍

    本文系转载,原文地址:http://singo107.iteye.com/blog/1175084 数据库事务的隔离级别有4个,由低到高依次为Read uncommitted.Read committ ...

  4. Spring的事务管理和数据库事务相关知识

    1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱.         比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱. ...

  5. Spring事务传播及数据库事务操作

    从Spring 事务配置说起 先看看Spring 事务的基础配置 <aop:aspectj-autoproxy proxy-target-class="true"/> ...

  6. Redis 01: 非关系型数据库 + 配置Redis

    数据库应用的发展历程 单机数据库时代:一个应用,一个数据库实例 缓存时代:对某些表中的数据访问频繁,则对这些数据设置缓存(此时数据库中总的数据量不是很大) 水平切分时代:将数据库中的表存放到不同数据库 ...

  7. 关系型数据库事务遵循ACID原则

    事务在英文中是transaction,和现实世界中的交易很类似,它有如下四个特性: 1.A (Atomicity) 原子性 原子性很容易理解,也就是说事务里的所有操作要么全部做完,要么都不做,事务成功 ...

  8. Redis(1.3)Redis的基本特性(事务、多数据库)

    [1]两大特性 (1)多数据库 1个redis实例 可以有16个数据库,默认下标为0~15,默认连接到的是 0 下标的数据库. (2)事务 [2]多数据库 [2.1]概念 1个redis实例 可以有1 ...

  9. Redis 如何与数据库事务保持一致

    考虑一个问题,redis 如何 与 数据库保持一致性的问题. 举栗子:如果我们在开发过程中遇到这样的一种情况,我们删除 redis中token 的同时 也需要修改数据库中 储存的 token 的状态为 ...

随机推荐

  1. C# Quartz结合控制台实现定时任务

    前言: Quartz一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,基于C#写成,可应用于winform.asp.net.asp.net core应用中.提 ...

  2. MySQL多表(理论知识总结)

    1. 多表关系    外键    foreign key    添加外键语法:    alter table 表名1 add foreign key(外键名称) references 表名2(主键名称 ...

  3. Netty学习(一)-为什么选择Netty

    前面我们简单学习了NIO.我们知道java的I/O模型一共有四种,分别是:传统的BIO,伪异步I/O,NIO和AIO.为了澄清概念和分清区别,我们还是先简单的介绍一下他们的概念,然后再去比较优劣.以及 ...

  4. Oracle中查看最近被修改过的表的方法

    1.select uat.table_name from user_all_tables uat 该SQL可以获得所有用户表的名称 2.select object_name, created,last ...

  5. Java Web基础面试题整理

    Tomcat的缺省端口是多少,怎么修改 tomcat默认缺省端口是8080 修改方法: 找到Tomcat目录下的conf文件夹 进入conf文件夹里面找到server.xml文件 打开server.x ...

  6. 前端模板引擎doT.js的用法

    简介 一款简单好用的前端模板引擎 用法 <script type="text/javascript" src="js/doT.min.js">< ...

  7. python基础--列表,元组

    list1 = [1,2,3,4,5,6]list2 = ['wang','cong']# 1对列表中的元素取值(通过索引)print(list1[3]) # 4print(list2[1]) # c ...

  8. Visual Studio 中两个窗体(WinForm)之间相互传值的方法

    编写WinowsForm应用程序时,实现两个窗体之间相互传递值的方法其实很简单.以下用一个例子说明:在名为FormMain主窗体运行过程中利用名为FormInfo窗体,获取用户输入信息,并将这些信息返 ...

  9. 对已经存在的hbase表修改压缩方式

    业务上可能会遇到这种情况,在最初创建hbase表时候,未指定压缩方式,当数据导入之后,由rowkey带来的数据膨胀导致hdfs上的数据大小远远大于原始数据大小.所以这时候可能就不得不考虑使用压缩,但是 ...

  10. Protocol, Delegate

    协议的构成: 协议:用来指定代理双方可以做什么,必须做什么. 代理:根据指定的协议,完成委托方需要实现的功能. 委托:根据指定的协议,指定代理去完成什么功能. 协议的修饰符: 协议有两个修饰符@opt ...