事务的ACID属性
- 原子性(Atomicity):事务是一个原子操作,对数据的修改,要么全部成功,要么全部失败。
- 比如在一个方法中,开启了事务,方法里面有多个修改或删除数据的操作,方法里面的所有操作要么同时成功,要么同时失败。
- 主要是操作层面
- 一致性(Consistent) ,在事务开始和完成时,数据都必须保持一致状态。
- 比如在方法中,有多个修改或删除数据库的操作,在事务提交后,要求所有的操作过的数据都是成功的,状态都是一致的。
- 主要是数据层面
- 隔离性(Isolation),保证事务在不受外部并发操作影响的“独立”环境执行。
- 例如线程1开启了事务A,线程2开始了事务B,两个事务都对库存做操作。事务A查询了库存的值是10,然后事务B把库存修改为5,事务A再次去查询库存得到的值是5,读到了其他事务修改的值,但我们希望的是,在一个事务里面,同一个查询得到一样的结果而不是会随着外部的修改而改变,所以有了隔离性。
- 事务处理过程中的中间状态对外部是不可见的。
- 持久性(Durable) ,事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。
- MySql会把数据保存到磁盘。
并发事务处理带来的问题
- 更新丢失或者脏写,当有多个事务去修改同一行,因为事务有隔离性,每个事务都不知道其他事务的存在,就会发生丢失更新的问题,最后的更新覆盖了其他事务所做的更新。
- 例如有2个事务去修改库存,事务A查询库存是10,然后库存减去5。此时事务B也去查询库存,也是10,然后把库存减1。事务A提交,然后提交事务B,数据库最后的库存是9,我们想要的最后结果是4,所以出现了覆盖前面的更新。
- 脏读,事务A读取到了事务B已经修改但尚未提交的数据。如果B事务回滚,A读取的数据无效,不符合一致性要求
- 不可重读,事务A内部的相同查询语句在不同时刻读出的结果不一致,不符合隔离性。
- 幻读,事务A读取到了事务B提交的新增数据,不符合隔离性
事务隔离级别
“脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交(Read Uncommitted) | 可能 | 可能 | 可能 |
| 读已提交(Read Committed) | 不可能 | 可能 | 可能 |
| 可重复读(Repeatable Read) | 不可能 | 不可能 | 可能 |
| 可串行化(Serializable) | 不可能 | 不可能 | 不可能 |
数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上“串行化”进行。
Mysql默认的事务隔离级别是可重复读,用Spring开发程序时,如果不设置隔离级别默认用Mysql设置的隔离级别,如果Spring设置了就用已经设置的隔离级别
锁详解
锁分类
- 从性能上分为乐观锁(用版本对比来实现)和悲观锁
- 从对数据库操作的类型分,分为读锁和写锁(都属于悲观锁)
- 读锁(共享锁):多个读操作可以同时进行而不会相互影响,读读共享
- 写锁(排它锁):当写操作没有完成前,会阻塞其他的读和写操作。
- 从对数据操作的粒度分,分为表锁和行锁
加写锁
客户端A:
客户端B再次去查询,会阻塞:
但在客户端A支持增删改查都没问题
- MyISAM表的读操作(加读锁) ,不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作
- MylSAM表的写操作(加写锁) ,会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作
行锁(InnoDB)
每次操作锁住一行数据。
客户端A开启事务更新不提交
客户端B去更新同一条记录,会阻塞
客户端B去更新其他行的记录,正常更新:
InnoDB与MYISAM的最大不同有两点
- InnoDB支持事务(TRANSACTION)
- InnoDB支持行级锁
锁总结
- MyISAM在执行查询语句SELECT前,会自动给涉及的所有表加读锁,在执行update、insert、delete操作会自动给涉及的表加写锁。
- InnoDB在执行查询语句SELECT时(非串行隔离级别),不会加锁。但是update、insert、delete操作会加行锁。
- 就是读锁会阻塞写,但是不会阻塞读。而写锁则会把读和写都阻塞



