锁的概念
锁是计算机协调多个进程或纯线程并发访问某一资源的机制。
- 数据的逻辑结构
锁类型
表级锁:开销小,加锁快;不易出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。会发生在:MyISAM、memory、InnoDB、BDB等存储引擎中;
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。会发生在:InnoDB存储引擎;
页级锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。会发生在:BDB存储引擎(BDB:可替代InnoDB的事务引擎,支持COMMIT、ROLLBACK和其他事务特性)。
- 行锁
共享锁(S Lock)允许事务读一行数据,与X锁互斥,与S锁兼容;
排它锁 (X Lock) 允许事务读一行数据,与X锁,S锁均互斥。
- 表锁
意向共享锁(IS):事务在给一个数据行加共享锁前必须先取得该表的IS锁;
意向排他锁(IX):事务在给一个数据行加排他锁前必须先取得该表的IX锁;
自增锁(AUTO-INC Locks):特殊表锁,自增长计数器通过该“锁”来获得子增长计数器最大的计数值。
兼容性 | IS | IX | S | X |
---|---|---|---|---|
IS | 兼容 | 兼容 | 互斥 | 互斥 |
IX | 兼容 | 兼容 | 互斥 | 互斥 |
S | 兼容 | 互斥 | 兼容 | 互斥 |
X | 互斥 | 互斥 | 互斥 | 互斥 |
意向锁的作用:
意向锁主要用于多粒度的锁并存,在添加行锁之前添加;
意向锁是在添加行锁之前添加;
如果没有意向锁,当向一个表添加表级X锁时,就需要遍历整张表来判断是否存行锁,以免发生冲突;
如果有了意向锁,只需要判断该意向锁与表级锁是否兼容即可。
MVCC
多版本并发控制 MVCC,是行级锁的一个变种,通过保存数据在某个时间节点的快照(snapshot),类似实现了行级锁。实现上通过在不同的数据行后增加创建日期版本号和删除日期版本号,且版本号不断递增,进而实现数据快照。
- MVCC的作用:避免脏读、写不阻塞读、实现可重复读、多版本控制。
脏读:指一个线程中的事务读取到了另外一个线程中未提交的数据;
不可重复读(虚读):指一个线程中的事务读取到了另外一个线程中提交的update的数据;
幻读:指一个线程中的事务读取到了另外一个线程中提交的insert的数据。
- ACID:
原子性:事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做;
一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态;
隔离性:一个事务的执行不能其它事务干扰,即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰;
持续性:也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。
- 事务的实现:
原子性、一致性、持久性通过数据库的redo log和undo log来完成。
redo log称为重做日志,用来保证事务的原子性和持久性;
undo log用来保证事务的一致性。
差别:
redo和undo的作用都可以视为是一种恢复操作;
redo恢复提交事务修改的页操作;
undo回滚行记录到某个特定版本;
redo是物理日志,记录的是页的物理修改操作;
undo是逻辑日志,根据每行记录进行记录。
- 事务的隔离级别:
不同隔离级别的问题 | 脏读(Dirty Read) | 不可重复读(NonRepeatable Read) | 幻读(Phantom Read) |
---|---|---|---|
未提交读(Read uncommitted) | 可能 | 可能 | 可能 |
已提交读(Read committed) | 不可能 | 可能 | 可能 |
可重复读(Repeatable read) | 不可能 | 不可能 | 可能 |
可串行化(Serializable ) | 不可能 | 不可能 | 不可能 |
InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了幻读问题。
Serializable(可串行化):这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
- MVCC一致性非锁定读(快照读)
在事务隔离级别提交读(RC)和可重复读(RR)下,InnoDB存储引擎使用非锁定的一致性读。
RC模式下,读取最新的快照;
RR模式下,读取事务开始时的快照。
Mysql默认RR,InnoDB默认一致性非锁定读。
- MVCC一致性锁定读 (当前读)
InnoDB存储引擎对于SELECT语句支持两种一致性的锁定读(locking read)操作:
SELECT…FOR UPDATE:对读取的行记录加一个X锁,其他事务不能对已锁定的行加上任何锁;
SELECT…LOCK IN SHARE MODE:对读取的行记录加一个S锁,其他事务可以向被锁定的行加S锁,但是如果加X锁,则会被阻塞。
阻塞与死锁
- 阻塞
在有些时刻一个事务中的锁需要等待另一个事务中的锁释放它所占用的资源,这就是阻塞。阻塞并不是一件坏事,其目的是为了确保事务可以并发且正常地运行。
在InnoDB存储引擎中,参数innodb_lock_wait_timeout
用来控制等待的时间(默认是50秒),该参数是动态的,可以在MySQL数据库运行时进行调整;
innodb_rollback_on_timeout
用来设定是否在等待超时时对进行中的事务进行回滚操作(默认是OFF,代表不回滚),innodb_rollback_on_timeout是静态的,不可在启动时进行修改。
- 死锁
产生死锁的的四个必要条件:
1)互斥条件:一个资源每次只能被一个进程使用;
2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放;
3)不剥夺条件:进程已获得的资源,在没使用完之前,不能强行剥夺;
4)循环等待条件:多个进程之间形成一种互相循环等待资源的关系。