Mysql 中有很多引擎,生活中引擎很常见,比如汽车引擎、飞机引擎、搜索引擎,引擎也就是核心的意思。在数据库中也同样如此,可以理解为数据库的核心部分,核心“部件”。
Mysql 中有很多引擎如MyISAM、InnoDB、MEMORY,但是最常用的是MyISAM、InnoDB。
MyISAM:不支持事务,页级锁,并发能力差
InnDB(从mysql-5.5.5开始作为默认存储引擎):支持事务,行级锁,并发性能相对较强
MyISAM
在执行 SELECT 前,会自动给涉及的所有表加读锁。
在执行 UPDATE、DELETE、INSERT 前,会自动给涉及的表加写锁。
阻塞场景
当一个用户对一张表进行读操作时,不会阻塞其他用户对同一张表的读操作,但会阻塞对同一表的写请求
当一个用户对一张表进行写操作时,会阻塞其他用户对同一张表的读写操作
建立表(引擎设置为MyISAM),插入数据
CREATE TABLE tb_stu(
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(30) DEFAULT NULL,
`sex` varchar(2) DEFAULT NULL,
PRIMARY KEY (`id`)
)ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
INSERT INTO tb_stu(name,sex) VALUES('小明','男');
INSERT INTO tb_stu(name,sex) VALUES('小文','男');
INSERT INTO tb_stu(name,sex) VALUES('小红','女');
读 验证:
一个查询执行: SELECT SLEEP(10),NAME FROM tb_stu;
另一个查询执行: SELECT * FROM tb_stu;
SLEEP()函数,比如 10,我表中有 3 条 数据则暂停 30 秒。
同样方法验证:
先查询: SELECT SLEEP(10),NAME FROM tb_stu;
插入: INSERT INTO tb_stu(name,sex) VALUES(‘小健’,’男’);
更新:UPDATE tb_stu SET NAME=’小小’ WHERE id=14;
删除:DELETE FROM tb_stu WHERE id = 14;
先查询,再执行插入或更新或删除都会阻塞。
写验证:
先更新: UPDATE tb_stu INNER JOIN( SELECT ‘11’ as id,sleep(10)) A on tb_stu.id=A.id SET tb_stu.name=’萝卜头’;
查询:SELECT * FROM tb_stu;
插入:INSERT INTO tb_stu (name,sex) VALUES(‘小樱’,’女’);
删除:DELETE FROM tb_stu WHERE id = 16
先更新,再查询或插入或删除都会阻塞。
同样一个线程当先进行写操作(插入或更新或删除)时,其它先对同一张表数据进行读或写都会阻塞。
MyISAM 存储引擎下读锁和写锁是互斥的,当两个请求同时到达的情况下,一个申请写锁,一个申请读锁,MyISAM 存储引擎只能设置写锁或者读锁,不能同时设置两个锁。
MyISAM存储引擎下写锁的优先级比较高
默认情况下,同一时间点上,同时到达两个进程,分别申请读锁和写锁的情况,Mysql 会优先给写锁。如果读请求先到锁等待队列里面排队,写请求后到锁等待队列的话,也是优先给写锁。默认情况,Mysql 认为写比读更重要。
检验:
- UPDATE tb_stu INNER JOIN( SELECT ‘11’ as id,sleep(10)) A on tb_stu.id=A.id SET tb_stu.name=’萝卜头’
- SELECT * FROM tb_stu;
- UPDATE tb_stu INNER JOIN( SELECT ‘12’ as id,sleep(10)) A on tb_stu.id=A.id SET tb_stu.name=’萝卜文’
按 1、2、3的执行顺序来,结束是 1、3、2。
所以在 MyISAM 引擎默认情况下,如果有大量的更新和查询操作的应用,因为写操作的优先级大于读操作,那么查询很难获取读锁,一直阻塞在那边,直到写操作完成,因此 MyISAM 适合读多写少的情况。
InnoDB
需要知道的常识
在InnoDB下,加锁之前,为什么需要先 start transaction?
InnoDB 锁的释放在事务提交/回滚之后,事务一旦提交/回滚之后,就会自动释放事务中的锁。
InnoDB 默认情况下 autocommit=1 即开启自动提交,在自动提交模式下,每执行一句 sql,就自动提交事务,锁也会立即释放。这种情况下无法手动控制事务的提交以及锁的释放时间。
因此,在进行锁的相关操作之前,先 start transaction(关闭自动提交模式,开启手动提交模式),这样事务中的每一 sql 执行完成,锁一直不会释放,直到我们手动提交事务,锁才会释放。
检索条件使用索引和不使用索引的锁区别?
检索条件有索引的情况会锁定特定的一些行。
检索条件没有使用索引的情况下会进行全表扫描,从而锁定全部的行(包括不存在的记录)
检索条件使用索引和不使用索引的锁区别?
InnoDB引擎加锁原则,遵循二段锁协议,即上锁分为两个阶段,事务开始后进入加锁阶段,事务 commit 或 rollback 进入解锁阶段,这样可以在并发事务的时候串行化处理。
InnoDB 下的锁?
相比于MyISAM引擎,InnoDB下锁的种类更为多样和复杂,大概有如下几种锁:
- 共享锁、排他锁、意向共享锁、意向排他锁
- 记录锁、间隙锁、next-key锁
- 悲观锁、乐观锁
查询事务隔离级别:select @@session.tx_isolation; (Mysql 默认是REPEATABLE-READ)
设置隔离级别:set session transaction isolation level read uncommitted;(READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE)
共享锁和排他锁
什么是共享锁,什么是排他锁?
共享锁称为读锁,简称 S 锁,原理:一个事务获取了一个数据行的共享锁,其它事务也能获取该行对应的共享锁,但不能获取排他锁,即一个事务在读取一个数据行的时候,其他事务也可以读,但不能对该数据进行增删改。
排他锁称为写锁,简称 X 锁,原理:一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其它锁(共享锁或排他锁),即一个事务在读取一个数据行的时候,其他事务不能对该数据进行增删改查。
怎么设置共享锁和排他锁?
设置共享锁:SELECT … LOCK IN SHARE MODE;
设置排他锁:SELECT … FOR UPDATE;
注意
对于 SELECT 语句,InnoDB 不会加任何锁,也就是可以多个并发去进行 SELECT 的操作,不会有任何的锁冲突,因为根本没锁。
对于 insert、update、delete 操作,InnoDB 会自动给涉及到的数据加排他锁,只有查询 SELECT 需要我们手动设置排他锁。
意向共享锁和意向排他锁
意向共享锁:简称IS,其作用在于:通知数据库接下来需要施加什么锁并对表加锁。如果需要对记录 A 加共享锁,那么此时 InnoDB 会先找到这张表,对该表加意向共享锁之后,再对记录 A 添加共享锁。
意向排他锁:简称IX,其作用在于:通知数据库接下来需要施加什么锁并对表加锁。如果需要对记录 A 加排他锁,那么此时 InnoDB会先找到这张表,对该表加意向排他锁之后,再对记录 A 添加排他锁。
共享锁和意向共享、排他锁和意向排他锁区别
- 共享锁和排他锁,系统在特定的条件下会自动添加共享锁或者排他锁,也可以手动添加共享锁或排他锁。 意向共享锁和意向排他锁都是系统自动添加和自动释放的,整个过程无须人工干预。
- 共享锁和排他锁都是锁的行记录,意向共享锁和意向排他锁锁定的是表。
记录锁、间隙锁、next-key锁
InnoDB 下的记录锁(也称行锁),间隙锁,next-key 锁属于排他锁
记录锁:对表中的记录加锁,叫记录锁,简称行锁。
间隙锁:为了解决幻读问题。锁定特定数据的前后间隙让数据无法被插入。
作用:
- 防止间隙内有新数据被插入
- 防止已存在的数据,更新成间隙内的数据
InnoDB自动使用间隙锁的条件
- 必须在 RR(REPEATABLE READ) 级别下
- 检索条件必须有索引 (没有索引的话,mysql 会全表扫描,那样会锁定整张表所有的记录,包括不存在的记录,此时其他事务不能修改不能删除不能添加)
next-key 锁:包含了记录锁和间隙锁,即锁定一个范围并且,锁定记录本身,InnoDB默认加锁方式是 next-key 锁。
乐观锁和悲观锁
悲观锁的实现依靠数据库提供的锁机制,乐观锁是记录数据版本来实现即通过在表中添加版本号字段来作为释放可以成功提交的关键因素。