MySQL——锁机制和数据库并发问题解决方案
1、锁机制概述
在数据库中,除传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供许多用户共享的资源。为保证数据的一致性,需要对并发操作进行控制,因此产生了锁。
同时锁机制也为实现MySQL的各个隔离级别提供了保证。锁冲突也是影响数据库并发访问性能的一个重要因素。所以锁对数据库而言显得尤其重要,也更加复杂。
数据库锁机制简单来说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问变得有序所设计的一种规则。
MySQL 数据库由于其自身架构的特点,存在多种数据存储引擎,每种存储引擎所针对的应用场景特点都不太一样,为了满足各自特定应用场景的需求,每种存储引擎的锁定机制都是为各自所面对的特定场景而优化设计,所以各存储引擎的锁定机制也有较大区别。
2、MySQL并发事务访问相同数据情况
2.1、读–读
读–读情况,即并发事务相继读取相同的记录数据。读取操作本身不会对记录有任何影响,并不会引起什么问题,所以允许这种情况的发生。
2.2、写–写
写–写情况:并发事务相继对相同的记录数据作出修改。
在这种情况下会发生脏写的问题,任何一种隔离级别都不允许这种问题的发生。所以在多个未提交事务相继对一条记录做改动时,需要让它们排队执行,这个排队的过程其实是通过锁来实现的。
这个所谓的锁其实是一个内存中的结构,在事务执行前本来是没有锁,也就是说一开始是没有锁结构和数据记录进行关联的。
当一个事务想对这条记录数据做改动时,首先会看看内存中有没有与这条记录关联的锁结构,当没有时候就会在内存中生成一个锁结构与这条记录关联。
2.3、读–写 或 写–读
读–写或写–读,即一个事务进行读取操作,另一个进行改动操作。这种情况下可能发生脏读、不可重复读、幻读的并发问题。
各个数据库厂商对SQL标准的支持都可能不一样。比如MySQL在REPEATABLE READ隔离级别上就已经解决了幻读问题。
3、数据库并发问题解决方案
如何解决脏读、不可重复读、幻读的并发问题?两种可选的解决的方案:
方案一:读操作利用多版本并发控制(MVCC),写操作进行加锁
方案二:读、写操作都进行加锁
- 如果我们的一些业务场景不允许读取记录的旧版本,而是每次都必须去读取记录的最新版本。比如,在银行存款的事务中,你需要先把账户的余额读出来,然后将其加上本次存款的数额,最后再写到数据库中。在将账户余额读取出来后,就不想让别的事务再访问该余额,直到本次存款事务执行完成,其他事务才可以访问账户的余额。这样在读取记录的时候就需要对其进行加锁操作,这样也就意味着读操作和写操作也像写–写操作那样排队执行。
- 如果一个事务在写记录时就给这条记录加锁,那么其他事务就无法读取该条记录,就避免了脏读情况。
- 如果一个事务在读记录时就给这条记录加锁,那么其他事务就无法修改该条记录,就避免了不可重复读情况。
- 幻读问题的产生是因为当前事务读取了一个范围的记录,然后另外的事务向该范围内插入了新记录,当前事务再次读取该范围的记录时发现了新插入的新记录。采用加锁的方式解决幻读问题就有一些麻烦,因为当前事务在第一次读取记录时幻影记录并不存在,所以读取的时候加锁就有点尴尬(因为你并不知道给谁加锁)。
两种方案对比:
- 采用MVCC方式:读写操作并不冲突,性能更高
- 采用读写都加锁方式:读写操作需要排队执行,影响性能
- 一般情况下我们当然愿意采用MCC来解决读-写操作并发执行的问题,但是业务在某些特殊情况下,要求必须采用加锁的方式执行。
|