二、MySQL调优之事务:高并发场景下的数据库事务调优
常用的数据库引擎为InnoDB和MyISAM,MyISAM不支持事务处理,所以Mysql事务基于InnoDB引擎。
数据库事务具有以下四个基本属性:原子性(Atomicity)、一致性(Consistent)、隔离性(Isolation)以及持久性(Durable)。正是这些特性,才保证了数据库事务的安全性。
一.并发事务带来的问题
1、数据丢失
据丢失可以基于数据库中的悲观锁来避免发生,即在查询时通过在事务中使用 select xx for update 语句来实现一个排他锁,保证在该事务结束之前其他事务无法更新该数据。
也可以基于乐观锁来避免,即将某一字段作为版本号,如果更新时的版本号跟之前的版本一致,则更新,否则更新失败。
2、脏读(读未提交,B事务在A事务提交之前就读到了修改后的数据,但是A事务发生了回滚,B事务读取到的是错误数据)
3、不可重复读
4、幻读
二.事务隔离解决并发问题
隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交(read uncommitted) | 有 | 有 | 有 |
读已提交 (read committed) | 无 | 有 | 有 |
可重复读 (repeatable read) | 无 | 无 | 有 |
串行化(serializable) | 无 | 无 | 无 |
1、读未提交(Read Uncommitted)
读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到,是最不靠谱的。
2、读已提交(Read Committed,Oracle默认隔离级别)
事务成功提交后才可以被查询到。
3、可重复读(Repeatable Read,MYSQL默认隔离级别)
事务B在事务A提交之前值可以读取,不可以修改。
在事务 A 读取数据时增加了共享锁,事务结束,才释放锁,事务 B 读取修改数据时增加了行级排他锁,直到事务结束才释放锁。也就是说,事务 A 在没有结束事务时,事务 B 只能读取数据,不能修改。当事务 A 结束事务,事务 B 才能修改。这种隔离级别,可以避免脏读、不可重复读,但依然存在幻读的问题。
4、串行化(Serializable)
串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行,但是种方式对数据库性能的影响极高,一般不用。
三.锁具体实现算法
1、结合业务场景,使用低级别事务隔离(使用读已提交)
在高并发业务中,为了保证业务数据的一致性,操作数据库时往往会使用到不同级别的事务隔离。隔离级别越高,并发性能就越低。
2、避免行锁升级表锁
在 InnoDB 中,行锁是通过索引实现的,如果不通过索引条件检索数据,行锁将会升级到表锁。我们知道,表锁是会严重影响到整张表的操作性能的,所以我们应该避免他。
3、控制事务的大小,减少锁定的资源量和锁定时间长度(尽量的减少锁的持有有时间)
执行顺序1比较合适,因为扣除库存会持有锁,而新建订单并不会持有锁,将需要持有锁的操作放在最后,尽量减小锁的持有时间。