MySQL在两个层面实现并发控制:服务器层与存储引擎层。
读锁和写锁:
在处理并发读或写时,可以通过实现一个由两种锁组成的系统来解决问题。
这两种锁通常被称为共享锁和排他锁,或者称为读锁和写锁。
读锁:是共享的,或者说是相互不阻塞的,多个客户可以在同时读取同一数据。
写锁:是排他的,一个写锁会阻塞其他的写锁和读锁,同一时刻只能有一个用户能够写入,并防止其他用户读取正在写入的数据。
锁粒度:
锁粒度:指加锁的对象的大小。显然,锁的粒度越小,并发控制效率越高。
锁的各种操作,包括获得锁、检查锁和释放锁等,都会增加系统开销。
因此,如果系统花费大量时间来管理锁,而不是用来获取数据,就会影响系统性能。
锁策略:在锁的开销和安全性之间寻求平衡。
有两种常见的锁策略,表锁和行级锁。
表锁:开销较小,但是并发控制不好。
行级锁:可以很好地实现并发控制,但是开销比较大。
行级锁是在 mysql 的存储引擎层实现,没有在服务器层实现,innodb 中实现了行级锁。
1.3 事务
事务:是一组原子性的SQL语句,事务内的语句,要么全部执行成功,要么全部执行失败。
事务的四大特性ACID:
原子性:整个事务要么全部提交成功,要么全部失败回滚,不可以只执行一部分。
一致性:数据库总是从一个一致状态转换到另一个一致状态。(转账失败后,卡里的钱应该不变,不能少了)
隔离性:事务提交之前所做的修改对其他事务是不可见的。(通常来说)
持久性:事务提交之后,所做的修改会持久化到数据库中。
事务处理也会使系统做更多额外工作,用户可以根据业务是否需要进行事务处理,选择合适的存储引擎。
四种事务的隔离级别:
READ UNCOMMITTED (未提交读):
事务中的修改,即使没提交,对其他事务也是可见的。
事务可以读取未提交的数据,也叫脏读,一般很少使用。
READ COMITTED (提交读):
一个事务开始时,只能看见已经提交的修改,并且所做的修改对其他事务不可见。
这个级别有时候也叫不可重复读,因为两次执行同样的查询,可能会得到不同的结果。
事务A前后执行两次查询,前一次读取某条记录之后,事务B对其进行了修改并提交,这时当A再次读取该数据的时候就会发现与之前读取的结果不一样。
是大多数数据库默认的隔离级别(但MySQL不是)。
REPEATABLE READ (可重复读):
保证了同一个事务多次读取同样记录的结果是一致的。但无法解决幻读。
幻读:事务A读取某个范围内的记录时,事务B又在该范围内插入新的记录,当事务A再次读取时,会产生幻行。
另一种幻读情况:事务A对数据库所有行做了修改时,事务B对向数据库中插入了一行新的数据。这时A发现还有没有修改的记录,就像发生幻觉一样。
InnoDB存储引擎使用一种被称成next-key locking的策略来避免幻读(phantom)现象。
这是MySQL (InnoDB引擎)的默认事务隔离级别。
SERIALIZABLE (可串行化):
强制事务串行执行,在读取每行数据上都加锁,可能产生大量的超时和锁争用问题。
只有在非常需要确保数据的一致性,且可以接受没有并发的情况下才考虑使用该级别。
死锁:
死锁指的是多个事务在同一资源上相互占用,并请求对方占用的资源,导致恶性循环的现象。
数据库系统中实现了各种死锁检测和死锁超时机制。
InnoDB目前处理死锁的方法是:将持有最少行级排他锁的事务回滚。
事务日志:
存储引擎修改表数据的时候只修改其在内存中的拷贝,再讲修改记录持久化到硬盘上的事务日志中。
不用每次修改都将数据持久化到硬盘,能提高事务的效率。
也被称为预写式日志,修改数据需要写两次硬盘。
系统崩溃,重启后能根据日志恢复这部分修改的数据。
MySQL中的事务:
MySQL提供了两种事务型存储引擎,InnoDB和NDB Cluster。
在MySQL中默认是自动提交事务的,每个查询操作被当作一个事务。
可以使用set autocommit
来设置是否自动提交。
可以通过set session transaction isolation level
来设置隔离级别。
在同一事务中使用多种存储引擎是不可靠的。
隐式锁定:
InnoDB采用的是两阶段锁协议。
在事务执行过程中随时可能执行锁定,只有在提交和回滚的时候才会释放。
显示锁定:
InnoDB支持通过特定语句显示锁定
1.4 多版本并发控制(MVCC)
MVCC相当于行级锁的一个变种,但在很多情况下避免了加锁操作,开销更低。
大多数都实现了非阻塞读操作,写操作也只用锁定必要的行。
MVCC只在读提交和可重复读这两个隔离级别下工作。