在处理并发读或写时,可以通过实现一个由两种类型组成的锁系统来解决问题。这两种类型的锁通常被称为共享锁和排它锁,也叫读锁和写锁。读锁是共享的,相互不阻塞,多个客户在同一时刻可以同时读取同一个资源而不相互干扰。写锁则是排他的,也就是说一个写锁会阻塞其他的写锁和读锁,确保在给定时间内只有一个用户能执行写入并防止其他用户读取正在写入的同一资源。
在实际的数据库系统中,每时每刻都在发生锁定,当某个用户在修改某一部分数据时,MySQL 会通过锁定防止其他用户读取同一数据。写锁比读锁有更高的优先级,一个写锁请求可能会被插入到读锁队列的前面,但是读锁不能插入到写锁前面。
Q3:MySQL 的锁策略有什么?表锁是MySQL中最基本的锁策略,并且是开销最小的策略。表锁会锁定整张表,一个用户在对表进行写操作前需要先获得写锁,这会阻塞其他用户对该表的所有读写操作。只有没有写锁时,其他读取的用户才能获取读锁,读锁之间不相互阻塞。
行锁可以最大程度地支持并发,同时也带来了最大开销。InnoDB 和 XtraDB 以及一些其他存储引擎实现了行锁。行锁只在存储引擎层实现,而服务器层没有实现。
Q4:数据库死锁如何解决?死锁是指多个事务在同一资源上相互占用并请求锁定对方占用的资源而导致恶性循环的现象。当多个事务试图以不同顺序锁定资源时就可能会产生死锁,多个事务同时锁定同一个资源时也会产生死锁。
为了解决死锁问题,数据库系统实现了各种死锁检测和死锁超时机制。越复杂的系统,例如InnoDB 存储引擎,越能检测到死锁的循环依赖,并立即返回一个错误。这种解决方式很有效,否则死锁会导致出现非常慢的查询。还有一种解决方法,就是当查询的时间达到锁等待超时的设定后放弃锁请求,这种方式通常来说不太好。InnoDB 目前处理死锁的方法是将持有最少行级排它锁的事务进行回滚。
死锁发生之后,只有部分或者完全回滚其中一个事务,才能打破死锁。对于事务型系统这是无法避免的,所以应用程序在设计时必须考虑如何处理死锁。大多数情况下只需要重新执行因死锁回滚的事务即可。
Q5:事务是什么?事务是一组原子性的 SQL 查询,或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询。如果其中有任何一条语句因为崩溃或其他原因无法执行,那么所有的语句都不会执行。也就是说事务内的语句要么全部执行成功,要么全部执行失败。
Q6:事务有什么特性?原子性 atomicity
一个事务在逻辑上是必须不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说不可能只执行其中的一部分。
一致性 consistency
数据库总是从一个一致性的状态转换到另一个一致性的状态。
隔离性 isolation
针对并发事务而言,隔离性就是要隔离并发运行的多个事务之间的相互影响,一般来说一个事务所做的修改在最终提交以前,对其他事务是不可见的。
持久性 durability
一旦事务提交成功,其修改就会永久保存到数据库中,此时即使系统崩溃,修改的数据也不会丢失。
Q7:MySQL 的隔离级别有哪些?未提交读 READ UNCOMMITTED
在该级别事务中的修改即使没有被提交,对其他事务也是可见的。事务可以读取其他事务修改完但未提交的数据,这种问题称为脏读。这个级别还会导致不可重复读和幻读,性能没有比其他级别好很多,很少使用。
提交读 READ COMMITTED
多数数据库系统默认的隔离级别。提交读满足了隔离性的简单定义:一个事务开始时只能"看见"已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前的任何修改对其他事务都是不可见的。也叫不可重复读,因为两次执行同样的查询可能会得到不同结果。
可重复读 REPEATABLE READ(MySQL默认的隔离级别)
可重复读解决了不可重复读的问题,保证了在同一个事务中多次读取同样的记录结果一致。但还是无法解决幻读,所谓幻读指的是当某个事务在读取某个范围内的记录时,会产生幻行。InnoDB 存储引擎通过多版本并发控制MVCC 解决幻读的问题。
可串行化 SERIALIZABLE