Comparison of redis transaction and relational database transaction

redis is a high-performance key-value database. As a no sql database, redis has the advantages of simplicity and flexibility, rich data structure, and high-speed reading and writing compared with traditional relational databases.

This article mainly compares the processing of redis with the traditional relational database (using mysql), the environment used is ubuntu 14.04.1, mysql 5.5.49, redis 3.2.3.

1 Operation transaction command

MySQL uses start transaction to start things, rollback to roll back things, and commit to commit things. redis uses multi to start things, discard to cancel things, and exec to commit things. Before starting the experiment, prepare test data for mysql and redis, prepare 1,000 yuan for the tom1 account, and 500 yuan for the tom2 account.

(Figure 1 - Data Preparation)

Scenario 1: Use transaction to transfer 100 yuan from tom1 to tom2.

Proceed as follows:

 

mysql

say again

turn things on

start transaction

multi

tom1 - 100

update tom1

decrty tom1 100

queued

Query tom1 balance

tom1 balance decreased by 100

get tom1

queued

tom2 + 100

update tom2

incrby tom2 100

queued

Query tom2 balance

tom2 balance increased by 100

get tom2

queued

Submit things

commit

exec

Execute the statement in the queue.

1) (integer) 900

2) "900"

3) (integer) 600

4) "600"

Query the balance of tom1 and tom2

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

The amount of tom1 is reduced by 100, and the account of tom2 is increased by 100

mget tom1 tom2

The amount of tom1 is reduced by 100, and the account of tom2 is increased by 100

(Figure 2-mysql transaction execution process)

(Figure 3-Redis transaction execution process)

From Scenario 1, it can be seen that the SQL statement in the transaction has been executed before the commit after mysql opens the transaction, but it is not actually committed to the database. After redis uses multi to open the transaction, the written sql statements are all entered into the queue queue. When the exec is executed to submit the transaction, it is submitted to the database in the order of entering the queue queue at one time. Also for rollback, mysql executes rollback to roll back the submitted data. Because redis has not submitted the data, using discard simply cancels the sql statement in the queue.

Scenario 2: In the process of executing the 100 yuan transfer from tom1 to tom2, the sql with wrong syntax is used.

Proceed as follows:

 

mysql

say again

turn things on

start transaction

multi

tom1 - 100

update tom1

decrty tom1 100

queued

Query tom1 balance

tom1 balance decreased by 100

get tom1

queued

tom2 + 100

Using the wrong command is tom2+100

Using the wrong command is tom2+100

Query tom2 balance

tom2 balance unchanged

get tom2

queued

Submit things

commit

exec

Because there is a grammatical error statement in the queue, directly discard the statement in the transaction

Query the balance of tom1 and tom2

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

The amount of tom1 is reduced by 100, and the amount of tom2 account remains unchanged

mget tom1 tom2

The amount of tom1 and tom2 has not changed

(Figure 4 - There is a syntax error statement in the mysql transaction)

(Figure 5 - There is a syntax error statement in the redis transaction)

As can be seen from scenario 2, the mysql transaction will submit the correct sql to the database even if it encounters an incorrect statement. The programmer needs to control the rollback when encountering a statement exception. Unlike mysql, redis is different from mysql. Statements with syntax errors will discard sql statements in the entire transaction.

Scenario 3: The wrong execution object is used in the process of transferring 100 yuan from tom1 to tom2.

Proceed as follows:

 

mysql

say again

turn things on

start transaction

multi

tom1 - 100

update tom1

decrty tom1 100

queued

Query tom1 balance

tom1 balance decreased by 100

get tom1

queued

tom2 + 100

Using error object command is tom2+'hundrud'

Using wrong command is tom2+'hundred'

Query tom2 balance

tom2 balance unchanged

get tom2

queued

Submit things

commit

exec

Execute item by item

Query the balance of tom1 and tom2

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

The amount of tom1 is reduced by 100, and the amount of tom2 account remains unchanged

mget tom1 tom2

The amount of tom1 is reduced by 100, and the amount of tom2 account remains unchanged

(图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

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326076675&siteId=291194637