数据库之MyISAM与InnoDB锁方面的区别

一、数据库锁的分类

  • 按锁的粒度划分,可分为页级锁、表级锁、行级锁
  • 按锁级别划分,可分为共享锁(S)、排他锁(X)
  • 按加锁方式划分,可分为自动锁、显式锁(for update,lock in share mode)
  • 操作划分,可分为DML、DDL锁
  • 使用方式划分,可分为乐观锁、悲观锁

二、MyISAM与InnoDB锁方面的区别

  • InnoDb支持行锁和表锁,MyISAM只支持表锁
  • MyISAM的写请求的优先级比读请求的优先级高
  • MyISAM总是一次性获得所需的全部锁,要么全部满足,要么等待,因此不会出现死锁。
  • 但是在InnoDB中,除单个SQL组成的事务外,锁是逐步获得的,这就决定了InnoDB发生死锁是可能的。

我们直接上测试
测试准备:

  • 一张基于MyISAM引擎的50万数据量的表person_info_myisam
  • 一张基于InnoDB引擎的50万数据量的表person_info
  • Navicat for MySQL
  • mysql 8.x
    :测试数据生成可参考:该博客的中间部分,也可以自行百度。
    有朋友可能问,为什么要这么多数据。查询这些数据需要些时间,在这个时间段内,执行另外一个会话中的sql语句来模拟并发

(一)MyISAM测试:

1、表的设计:
在这里插入图片描述
sql脚本:

/*
Navicat MySQL Data Transfer

Source Server         : test
Source Server Version : 80014
Source Host           : localhost:3306
Source Database       : mysql_study

Target Server Type    : MYSQL
Target Server Version : 80014
File Encoding         : 65001

Date: 2020-04-23 19:25:19
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for person_info_myisam
-- ----------------------------
DROP TABLE IF EXISTS `person_info_myisam`;
CREATE TABLE `person_info_myisam` (
  `id` int(7) NOT NULL AUTO_INCREMENT,
  `account` varchar(10) DEFAULT NULL,
  `name` varchar(20) DEFAULT NULL,
  `area` varchar(20) DEFAULT NULL,
  `title` varchar(20) DEFAULT NULL,
  `motto` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_account` (`account`) USING BTREE,
  KEY `index_area_title` (`area`,`title`) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

2、开始测试

  • 2.1、测试上了读锁之后,能否继续写表
    我们先测试更新语句速度:update person_info_myisam set account = account where id = 500001;
    在这里插入图片描述
    可以看到很快,那我们先根据主键进行查询
select * from person_info_myisam where id between 1 and 500000;

在查询期间再来执行上面的更新语句
在这里插入图片描述
可以看到被阻塞了,稍作等待,等待查询一下结束,然后更新语句出现结果
在这里插入图片描述
花了2.+秒,和之前的比相差甚远
2.2、我们再来测试该期间能否select,判断是否是共享锁
在会话2中加入select * from person_info_myisam where id = 1;
在这里插入图片描述
按照之前的步骤,先执行会话1的select,读的过程中,MyiSam引擎会给表上读锁,然后我们执行会话2的选中的语句,结果如下
在这里插入图片描述
时间为0.000s,几乎没用什么时间,说明MyiSam引擎下的读锁为共享锁,不阻塞读操作,其他都阻塞。以上的加锁都是mysql自动给我们加的,我们可以用以下语句手动加读锁和取消它。

-- 添加读锁
LOCK TABLES person_info_myisam read;

-- 释放读锁
UNLOCK TABLES;

2.3、测试加写锁后,能否读和写
会话1语句先执行

-- select语句上排他锁
select * from person_info_myisam where id between 1 and 500000 FOR UPDATE;

先测试读:执行会话1的同时执行以下语句

select * from person_info_myisam where id = 1;

我们会发现,本条几乎不需要时间的语句花了1.8秒,中间被阻塞了

然后测试写

按照以上步骤,同时执行

update person_info_myisam set account = account where id = 500001;

结果也在预料之中,被阻塞了

以上的测试可以看出,MyiSam引擎加的是表锁(读取会话1之外的数据都被阻塞)其共享锁可读不可写,写锁(即排它锁),不可读也不可写。

(二)InnoDB测试

1、设计基于InnoDB引擎的person_info表
在这里插入图片描述

/*
Navicat MySQL Data Transfer

Source Server         : test
Source Server Version : 80014
Source Host           : localhost:3306
Source Database       : mysql_study1

Target Server Type    : MYSQL
Target Server Version : 80014
File Encoding         : 65001

Date: 2020-04-23 20:50:27
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for person_info
-- ----------------------------
DROP TABLE IF EXISTS `person_info`;
CREATE TABLE `person_info` (
  `id` int(7) NOT NULL AUTO_INCREMENT,
  `account` varchar(10) DEFAULT NULL,
  `name` varchar(20) DEFAULT NULL,
  `area` varchar(20) DEFAULT NULL,
  `title` varchar(20) DEFAULT NULL,
  `motto` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_name` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3、
同样我们采用会话1和会话2的方式进行并发测试,注意,InnoDB引擎下不会自动给select上共享锁,需要我们手动添加lock in share mode,另外我们需要将自动提交关闭:set autocommit = 0;可以用show VARIABLES like 'autocommit';查询该变量是否为OFF

3.1、先测试开启读锁后能否读其他的行(检测是否为行锁)
select * from person_info where id = 3 lock in share mode;

在这里插入图片描述
在会话1事务没提交时,我们查询id为5 的数据

select * from person_info where id = 5 lock in share mode;

在这里插入图片描述

3.2、说明InnoDB的是行锁,接下来测试在同一个记录上能否同时加两个共享锁,即多个事务读
select * from person_info where id = 3 lock in share mode;

两个会话都执行该sql,结果显示都能执行,即InnoDB的共享锁支持其他事务读
那支不支持其他事务写呢?
会话1先执行

select * from person_info where id = 3 lock in share mode;

会话2执行

update person_info set title = 'test1' where id = 3;

结果如下:
在这里插入图片描述
被阻塞了,然后超时,只有会话1commit才能执行

3.3、排他锁测试

会话1执行

update person_info set title = 'test3' where id = 5;

会话2执行

select * from person_info where id = 5 lock in share mode;

在这里插入图片描述
读操作被阻塞

update person_info set title = 'test1' where id = 5;

在这里插入图片描述
写操作被阻塞

3.4、我们测试不走索引的情况下会加什么锁(行锁or表锁?)

为了方便测试,我将字段name的索引删除了,
然后将之前两个会话执行commit,提交之前的事务,然后会话1执行

update person_info set motto = 'test2' where name = 'BtomubiKI8qv8Xe7KdII';

在这里插入图片描述
执行成功,会话2查询另外一行记录

select account from person_info where name = 'kKBDcWVDe7KeM0wqoyrl' lock in share mode;

在这里插入图片描述
其他行都没法查询,说明表被锁了,也就是说,不走索引,行锁会升级为表锁。

总结

  • InnoDB 支持表锁和行锁,使用索引作为检索条件修改数据时采用行锁,否则采用表锁
  • InnoDB 自动给修改操作加锁,给查询操作不自动加锁
  • 行锁可能因为未使用索引而升级为表锁,所以除了检查索引是否创建的同时,也需要通过explain执行计划查询索引是否被实际使用。
  • 行锁相对于表锁来说开销要大,但优势在于高并发场景下表现更突出

两个引擎下的共享锁和排斥锁兼容性如下:
在这里插入图片描述

myisam适合的场景

  • 频繁执行全表count语句,存有变量记录行数
  • 对数据进行增删改的频率不高,查询非常频繁,(共享锁可用)
  • 没有事务

myisam引擎中的数据和文件时分离的,是非聚集索引,索引保存的是数据文件的指针,主键索引和辅助索引是独立的,因此,myisam引擎在纯检索系统中或者增删改很少的系统中,性能要好于InnoDB引擎。

InnoDB适合的场景

  • 数据增删改查都相当频繁
  • 可靠性要求比较高,要求支持事务
  • 高并发场景
原创文章 50 获赞 101 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_42173451/article/details/105711178