sql事务

来源:互联网 发布:淘宝怎样登陆子账号 编辑:程序博客网 时间:2024/06/08 08:50

  • 事务概念
  • 事务特性ACID
  • 事务隔离级别
    • 序列化Serializable
    • 可重复读Repeatable read
    • 读已提交Read committed
    • 读未提交Read uncommitted
    • 事务隔离级别读问题
    • 事务的默认隔离级别
    • 案例分析
  • 参考资料

事务概念

A transaction symbolizes a unit of work performed within a database management system (or similar system) against a database, and treated in a coherent and reliable way independent of other transactions. A transaction generally represents any change in a database.

wiki上是这样定义事务的: 事务表示在数据库管理系统中对数据库执行的单元操作,并且以连贯和可靠的方式独立于其他事务。通常数据库中的所有修改都是事务。
数据库设置事务的目标:
1. 提供可靠的单元操作,以让数据库从错误中恢复过来,并且保持数据库的一致性即使系统错误;
2. 提供多个应用同时访问数据库时的隔离功能。

PS,通常所有单条操作语句都是事务,在mysql中查看提交方式:SELECT @@autocommit,为1表示自动提交,为0表示手动提交

事务特性(ACID)

根据定义,可以得到数据库的特性。
1. 原子性(Atomicity):事务是一个单元操作,具有原子性,要么全部执行,要么全部不执行;
2. 一致性(Consistency):事务执行后要保证数据库状态的一致性,所以写入的数据都要经过规则(约束、级联和触发器等)校验;
3. 隔离性(Isolation):事务并发执行时,是彼此隔离的,数据库有多个隔离级别,不同的级别有不同的效果;
4. 持久性(Durability):事务完成后,修改应当永久保存在数据库中,即使系统挂了。

事务隔离级别

事务在并发执行时,会带来数据上的冲突,为了解决这些冲突,数据库使用加锁或者其他方式来处理,但是大家都知道并发控制会降低执行效率,数据库一般都会提供数个隔离级别,让用户在并发效率和并发控制之间进行权衡和选择。 ANSI/ISO SQL标准定义了四个隔离级别:

序列化(Serializable)

这是最高级的隔离级别,数据实现彻底同步。

可重复读(Repeatable read)

在一个事务中,保证两次读取的数据是相同的。
可能会会出现幻读(Phantom reads),简单来说就是两次查询多条数据,后面那次查询可能会出来多出来N条数据或者少了N条数据。
来看个粟子,假如table_x中有id为’1’和’2’的两条数据

事务1 事务2 select * from table_x where id between ‘1’ and ‘5’; insert into table_x values(‘4’,’0’); commit select * from table_x where id between ‘1’ and ‘5’;

事务1第一次查询时,返回’1’,’2’两条数据,第二次查询时返回了’1’,’2’,’4’三条数据。

读已提交(Read committed)

在一个事务中,保证读取的数据是已提交的。
可能会出现幻读。
可能会出现不可重复读(Non-repeatable reads)
不可重复读,简单来说就是两次查询,结果集不一样,后面那次查询的结果,数据可能发生了变化,举个粟子:

事务1 事务2 select value from table_x where id=’1’; #get value=5 update table_x set value=’10’ where id=’1’; commit select value from table_x where id=’1’; #get value=10

读未提交(Read uncommitted)

这是最低的隔离级别,A事务可以读取B事务已修改未提交的数据。
可能会出现幻读。
可能会出现不可重复读。
可能会出现脏读(Dirty reads)
脏读,即读取到未提交的事务的修改,如果事务提交失败,意味着读取的数据是错的(就算不失败,也有可能导致其它问题),来个栗子:事务1和2并发执行,执行前表table_x中id=1的数据value=3,现在执行事务:

事务1 事务2 update table_x set value=’5’ where id=’1’; select value from table_x where id=’1’; #value=5 some thing error , roll back

那么这样事务1得到的值是错的,数据库中不存在(id=1,value=5)这条记录。

事务隔离级别&读问题

Isolation level Dirty reads Non-repeatable reads Phantoms Read Uncommitted may occur may occur may occur Read Committed don’t occur may occur may occur Repeatable Read don’t occur don’t occur may occur Serializable don’t occur don’t occur don’t occur

事务的默认隔离级别

mysql – repeatable read (使用select @@tx_isolation 查看当前隔离级别)
oracle – read commited

案例分析

隔离级别: repeatable read
场景: 订单发货后通知用户

//in service A@Transactionalpublic void deliver(String orderId){    Order order=orderService.findById(orderId);    ...    order.setStatus("delivered");    notifyOrderChange(orderId);    ...}//in service B@Transactional@Asyncpublic void notifyOrderChange(String orderId){    Order order=orderService.findById(orderId);    pushOrderInfoToUser(order);}

发现用户收到的信息,有时候订单的状态仍然是未发货状态。
根据上面理论知识很容易会发现,由于@Async导致异步调用,deliver和notifyOrderChange运行在不同的事务中,在并发执行时,由于repeatable read的隔离级别,只能读取已提交的数据,所以当notifyOrderChange先于deliver的提交执行时,notifyOrderChange得不到order的修改,这样用户看到的订单仍然是未发货状态了。解决办法就在deliver提交成功后再执行notifyOrderChange.

参考资料

https://en.wikipedia.org/wiki/Database_transaction
https://en.wikipedia.org/wiki/Isolation_(database_systems)
https://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-isolation-levels.html