一、锁的定义
读锁(共享锁):是多事务可以并发读取数据的锁,但任何事务都被阻塞等待对该数据进行写操作和加写锁,直到已释放所有共享锁。
写锁(排他锁):是指只有当前事务才可以进行读写操作的锁,则其他事务都被阻塞等待对该数据进行读写操作和加读写锁,直到已释放所有排他锁。
二、锁特点
- 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
- 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
- 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
三、MyISAM表锁
MyISAM 存储引擎只支持表锁,MySQL 的表级锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)。
读操作:某数据表被一事务加上读锁:
本事务权限:只能读取当前数据表,不能写操作 当前数据表和读取其他数据表,否则报错;
其他事务权限:也可以读所有数据表,但是不能写当前数据表(已被加读锁),如果对当前数据表执行写操作,则会阻塞直到释放所有锁;
写操作:某数据表被一事务加上写锁:
本事务权限:可以读写当前数据表,不能读写其他数据表,否则报错;
其他事务权限:阻塞读写当前数据表,直到释放所有锁;
读锁和写锁都是阻塞锁:例如A对数据表增加了写锁,如果B请求对数据表加写锁,此时B的加锁请求会一直处于阻塞状态,直到A释放了对表的锁,B才加锁成功获取到结果。
四、表锁的加锁/解锁方式
MyISAM 在执行查询语句(SELECT
)前,会自动给涉及的所有表加读锁,在执行更新操作 (UPDATE
、DELETE
、INSERT
等)前,会自动给涉及的表加写锁;因此,用户一般不需要直接用LOCK TABLE
命令给MyISAM表显式加锁。
锁表:lock tables tbl_name read | wrinte,tbl_name1 read | wrinte,…
解锁表:unlock table
查看表加锁状态:show open tables like '%tbl_name%';
分析表锁命令:show status like 'table_locks%';
注意:当某事务在执行 LOCK TABLE
后,只能访问加锁的这些表,该事务不能访问未加锁的表,其他事务可以访问未加锁的表;
此外,Myisam的读写锁调度是写优先,因此Myisam不适合做写为主表的引擎,因为写锁后其他线程不能做任何操作,大量更新会使查询很难得到锁,从而造成永远堵塞。
五、Innodb行锁
行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。有可能会出现死锁的情况。行级锁也分为共享锁和排他锁。
六、Innodb 行加锁方式:
共享锁用法:select ... where id=1|2|3... lock in share mode;
排它锁用法: select ... where id=1|2|3... for update;
七、死锁
MyISAM中是不会产生死锁的,因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么全部等待。而在InnoDB中,锁是逐步获得的,就造成了死锁的可能。
在InnoDB中,行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条sql语句操作了主键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。 当两个事务同时执行,一个锁住了主键索引,在等待其他相关索引。另一个锁定了非主键索引,在等待主键索引。这样就会发生死锁。
八、事务( myisam引擎不支持事务, innodb和BDB引擎支持)
事务的4个特性ACID: 原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
以银行汇款为例,张三给李四转款300元.
原子性: 是指某几句sql的影响,要么都发生,要么都不发生. 即:张三减300, 李四+300 , insert银行流水, 这3个操作,必须都完成,或都不产生效果.
一致性: 事务前后的数据,保持业务上的合理一致. (汇款前)张三的余额+李四的余额 ====== (汇款后) 张三的余额+李四余额
隔离性: 在事务进行过程中, 其他事务,看不到此事务的任何效果.
持久性: 事务一旦发生,不能取消. 只能通过补偿性事务,来抵消效果.
事务的使用流程:
开启事务 start transaction
执行查询 xxxx
提交事务/回滚事务. commit / rollback
事务级别设置:set session transaction isolation level [read uncommitted | read committed | repeatable read |serializable]
查看事务级别: show variables like '%isolation%';
事务级别划分:
read uncommitted: 读未提交的事务内容,显然不符原子性, 称为”脏读”. 在业务中,没人这么用.(最低级别)
read commited: 在一个事务进行过程中, 读不到另一个进行事务的操作,但是,可以读到另一个结束事务的操作影响.(语句级别)
repeatable read: 可重复读,即在一个事务过程中,所有信息都来自事务开始那一瞬间的信息,不受其他已提交事务的影响. (大多数的系统,用此隔离级别,事务级别)
serializeable 串行化 , 所有的事务,必须编号,按顺序一个一个来执行,也就取消了冲突的可能.这样隔离级别最高,但事务相互等待的等待长. 在实用的也不是很多.(最高级别)