sql事务

事务概念

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 levelDirty readsNon-repeatable readsPhantoms
Read Uncommittedmay occurmay occurmay occur
Read Committeddon’t occurmay occurmay occur
Repeatable Readdon’t occurdon’t occurmay occur
Serializabledon’t occurdon’t occurdon’t occur

事务的默认隔离级别

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

案例分析

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

//in service A
@Transactional
public void deliver(String orderId){
    Order order=orderService.findById(orderId);
    ...
    order.setStatus("delivered");
    notifyOrderChange(orderId);
    ...
}
//in service B
@Transactional
@Async
public 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值