mysql的存储引擎与锁
一、背景知识
1、锁是计算机协调多个进程或线程并发访问某一资源的机制。
A、锁分类。
| 共享锁(读锁):在锁定期间,多个用户可以读取同一个资源,读取过程中数据不会发生变化。
| 排他锁(写锁):在锁定期间,只允许一个用户写入数据,其它用户的读取,写入等操作都会被拒绝。
B、锁颗粒
| 表锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
| 行锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
| 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
2、事务是由一组SQL语句组成的逻辑处理单元。
A、事务(Transaction)及其ACID属性。
| 原子性(Atomicity):事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。
| 一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束时,所有的内部数据结构(如B树索引或双向链表)也都必须是正确的。
| 隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。
| 持久性(Durable):事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。
B、事务并发问题
| 更新丢失(Lost Update):两个或多个事务同时更新一个资源,前面的事务的操作结果会被最后面的事务的操作结果覆盖。
| 脏读(Dirty Reads):事务A正在更新记录X,事务B读取了记录X,并藉此做进一步处理,事务A更新完毕记录X,此时数据库中的记录X与事务B读到的记录X并不是一致的。那么事务B读取到的就是“脏数据”,此类现象称之为“脏读”。
| 不可重复读(Non-Repeatable Reads):事务A读取了记录X,然后事务B更新了记录X,然后事务A再次读取记录X。此类情况下,事务A读取的记录X并不一定是相同的。
| 幻读(Phantom Reads):事务A按相同的查询条件读取已经读取过的记录X时,发现,事务B更新的记录Y也符合事务A的查询条件。那么,事务A将会读取到记录Y,而不是记录X。此类现象称之为“幻读”。
C、事务隔离解决事务并发问题
| 隔离级别 | 说明 | 脏读 | 不可重复读 | 幻读 |
| Read Uncommitted(读取未提交内容) | 所有事务都可以看到其他未提交事务的执行结果。 | 是 | 是 | 是 |
| Read Committed(读取提交内容) | 一个事务只能看见已经提交事务所做的改变。 | 否 | 是 | 是 |
| Repeatable Read(可重读) | 这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。 | 否 | 否 | 是 |
| Serializable(可串行化) | 它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。 | 否 | 否 | 否 |
二、各个存储引擎的特点
| 特点 | MyISAM | InnoDB | Memory | Archive |
| 存储限制 | 256TB | 64TB | 有 | 无 |
| 事务安全 | - | 支持 | - | - |
| 支持索引 | 支持 | 支持 | 支持 | |
| 锁颗粒 | 表锁 | 行锁 | 表锁 | 行锁 |
| 数据压缩 | 支持 | - | - | 支持 |
| 支持外键 | - | 支持 | - | - |
三、MyISAM的锁详解
在我的test数据库中,有两张MyISAM存储引擎的表分别是User与Log。
下面将演示两个线程(A、B)同时操作一张表的情况,开两个cmd窗口一个代表A一个代表B按顺序执行下面的代码。
| 操作 | 说明 | 线程 |
| mysql> show tables; +----------------+ | Tables_in_test | +----------------+ | log | | user | +----------------+ 2 rows in set |
显示所有的表 | A |
| mysql> show status like 'table%'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Table_locks_immediate | 50 | | Table_locks_waited | 0 | +-----------------------+-------+ 2 rows in set (0.00 sec) |
显示表级锁争用情况 | A |
| mysql> lock table log write local; Query OK, 0 rows affected |
给表log显式加上写锁,“local”选项的作用是在满足MyISAM表并发插入条件的情况下,允许其它线程在表尾并发插入记录。 | A |
| mysql> select * from log; | 线程B读取已经被线程A上了读锁的表log,可以看到线程B正在等待。 | B |
| mysql> select * from user; 1100 - Table 'user' was not locked with LOCK TABLES |
线程A读取了没有被加锁的表User,可以看到,mysql不让读取。 | A |
| mysql> unlock tables; Query OK, 0 rows affected |
线程A释放了对log表的锁。 | A |
| mysql> show status like 'table%'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Table_locks_immediate | 51 | | Table_locks_waited | 0 | +-----------------------+-------+ 2 rows in set (0.00 sec) |
显示表级锁争用情况,可以看到Table_locks_immediate的值加1了。 | A |
| +--------+-----+---------+-------------+ | log_id | uid | content | create_time | +--------+-----+---------+-------------+ | 1 | 2 | 呵呵 | 10 | | 2 | 2 | 哈哈 | 20 | +--------+-----+---------+-------------+ 2 rows in set (5 min 47.34 sec) |
线程B读取到了log的数据。 | B |
1、总结
| MyISAM,在默认情况下会自动加锁,并不需要显式加锁。
| MyISAM总是一次获得SQL语句所需要的全部锁。这也正是MyISAM表不会出现死锁(Deadlock Free)的原因。其它未加锁的表,并不允许操作。
| MyISAM加的是表级锁。
| 当线程A与线程B同时要给某个表上读锁与写锁的时候,MyISAM默认让线程B先上写锁。大量的读与写操作并存的时候,写操作可能会一直得到写锁,导致读操作处于阻塞状态。
2、并发插入调度
MyISAM存储引擎有一个系统变量concurrent_insert,专门用以控制其并发插入的行为,其值分别可以为0、1或2。
| 当concurrent_insert设置为0时,不允许并发插入。
| 当concurrent_insert设置为1时,如果MyISAM表中没有空洞(即表的中间没有被删除的行),MyISAM允许在一个进程读表的同时,另一个进程从表尾插入记录。这也是MySQL的默认设置。
| 当concurrent_insert设置为2时,无论MyISAM表中有没有空洞,都允许在表尾并发插入记录。
3、MyISAM的锁调度
| 通过指定启动参数 low-priority-updates,使MyISAM引擎默认给予读请求以优先的权利。
| 通过执行命令SET LOW_PRIORITY_UPDATES=1,使该连接发出的更新请求优先级降低。
| 通过指定INSERT、UPDATE、DELETE语句的LOW_PRIORITY属性,降低该语句的优先级。
| 给系统变量max_write_lock_count设置一个合适的值,当一个表的读锁达到这个值后,MySQL就暂时将写请求的优先级降低,给读操作一定获得锁的机会。
四、InnoDB的锁详解
在我的test数据库中,有两张InnoDB存储引擎的表分别是User与Log。
下面将演示两个线程(A、B)同时操作一张表的情况,开两个cmd窗口一个代表A一个代表B按顺序执行下面的代码。
第一个表格是,排他锁的实例。
| 操作 | 说明 | 线程 |
| mysql> show tables; +----------------+ | Tables_in_test | +----------------+ | log | | user | +----------------+ 2 rows in set |
显示所有的表 | A |
| mysql> select * from log; +--------+-----+---------+-------------+ | log_id | uid | content | create_time | +--------+-----+---------+-------------+ | 1 | 1 | 呵呵 | 0 | | 2 | 2 | 哈哈 | 0 | +--------+-----+---------+-------------+ 2 rows in set |
显示log表的信息 | A |
| mysql> show status like 'innodb_row_lock%'; +-------------------------------+----------+ | Variable_name | Value | +-------------------------------+----------+ | Innodb_row_lock_current_waits | 0 | | Innodb_row_lock_time | 185770 | | Innodb_row_lock_time_avg | 26538 | | Innodb_row_lock_time_max | 51620 | | Innodb_row_lock_waits | 7 | +-------------------------------+----------+ 5 rows in set (0.00 sec) |
查看InnoDB的锁争用情况 | B |
| mysql> start transaction; Query OK, 0 rows affected |
开启事务 | A |
| mysql> update log set create_time=1 where uid=1; Query OK, 1 row affected Rows matched: 1 Changed: 1 Warnings: 0 |
更新uid=1的数据 Select…For Update 语句可以在读取数据的时候就锁定它。 |
A |
| mysql> update log set create_time=2 where uid=2; |
更新uid=2的数据,可以看线程B在等待,因为此时InnoDB加的是表级排他锁。 |
B |
| mysql> commit; Query OK, 0 rows affected |
提交事务 | A |
| mysql> update log set create_time=2 where uid=2; Query OK, 1 row affected (9.69 sec) Rows matched: 1 Changed: 1 Warnings: 0 |
线程B执行update成功 | B |
| mysql> show status like 'innodb_row_lock%'; +--------------------------------+---------+ | Variable_name | Value | +--------------------------------+---------+ | Innodb_row_lock_current_waits | 0 | | Innodb_row_lock_time | 196980 | | Innodb_row_lock_time_avg | 24622 | | Innodb_row_lock_time_max | 51620 | | Innodb_row_lock_waits | 8 | +--------------------------------+---------+ 5 rows in set (0.00 sec) |
再次查看InnoDB的锁争用情况 。 如 InnoDB_row_lock_waits 和InnoDB_row_lock_time_avg 的值比较高,则锁的争用情况严重。 |
B |
| mysql> select * from log; +--------+-----+---------+-------------+ | log_id | uid | content | create_time | +--------+-----+---------+-------------+ | 1 | 1 | 呵呵 | 1 | | 2 | 2 | 哈哈 | 2 | +--------+-----+---------+-------------+ 2 rows in set (0.00 sec) |
显示log表的信息 | B |
第二个表格是,共享锁的实例。
| 操作 | 说明 | 线程 |
| mysql> start transaction; Query OK, 0 rows affected (0.00 sec) |
开启事务 | A |
| mysql> start transaction; Query OK, 0 rows affected (0.00 sec) |
开启事务 | B |
| mysql> select * from log where uid=1 lock in share mode; +--------+-----+---------+-------------+ | log_id | uid | content | create_time | +--------+-----+---------+-------------+ | 1 | 1 | 呵呵 | 1 | +--------+-----+---------+-------------+ 1 row in set (0.00 sec) |
线程A取得共享锁 | A |
| mysql> select * from log where uid=1 lock in share mode; +--------+-----+---------+-------------+ | log_id | uid | content | create_time | +--------+-----+---------+-------------+ | 1 | 1 | 呵呵 | 1 | +--------+-----+---------+-------------+ 1 row in set (0.00 sec) |
线程B取得共享锁 | B |
| mysql> update log set create_time=1 where uid=1; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
线程A更新一条记录,暂时不要去操作下面的步骤,直到出现报错。 由于线程B的共享锁,导致了线程A无法更新数据。 |
A |
| mysql> update log set create_time=1 where uid=1; Query OK, 0 rows affected (7.82 sec) Rows matched: 1 Changed: 0 Warnings: 0 |
线程A再次更新记录,同时在线程A还在等待的时候,线程B也执行了更新记录。 | A |
| mysql> update log set create_time=1 where uid=1; ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction |
线程B执行更新记录。注意,当线程B执行更新语句的时候,线程B失去了共享锁,线程A获得了排他锁,导致线程B的语句立刻出现了报错。 | B |
| mysql> select * from log where uid=1 lock in share mode; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
线程B再次执行获取共享锁的语句,可以看到,由于线程A已经取得了排他锁,导致线程B争锁失败。 如果线程A执行commit语句释放排他锁后,线程B则可以立马获得共享锁。 |
B |
总结:
InnoDB行锁是通过给索引上的索引项加锁来实现的,无索引的情况下,InnoDB将使用表锁!实际应用中,要特别注意此特性。避免大量锁冲突影响并发性能。
mysql的存储引擎与锁的更多相关文章
- mysql基础(三)存储引擎和锁
存储引擎的概念: 关系型数据库表是用于存储和组织信息的数据结构,可以将表理解为由行和列组成的表格,各种各样,不同的表结构意味着存储不同类型的数据,在数据的处理上也会存在着差异,对于mysql来说,它提 ...
- MYSQL的存储引擎一般只要哪些?
根据个人个人见解: MySQL的存储引擎(构成.安全.锁) Myisam:数据操作快速的一种引擎,支持全文检索.文件保存在数据库名称为目录名的 目录中,有3个文件,分别是表定义文件(.frm).数据文 ...
- MySQL常用存储引擎及如何选择
一.MySQL的存储引擎 完整的引擎说明还是看官方文档:http://dev.mysql.com/doc/refman/5.6/en/storage-engines.html 这里介绍一些主要的引擎 ...
- MySQL数据库----存储引擎
什么是存储引擎? 存储引擎说白了就是如何存储数据.如何为存储的数据建立索引和如何更新.查询数据等技术的实现方法.因为在关系数据库中数据的存储是以表的形式存储的,所以存储引擎也可以称为表类型(即存储和操 ...
- MySql中存储引擎MyISAM与InnoDB区别于选择
InnoDB: 支持事务处理等 不加锁读取 支持外键 支持行锁 不支持FULLTEXT类型的索引 不保存表的具体行数,扫描表来计算有多少行 DELETE 表时,是一行一行的删除 InnoDB 把数据和 ...
- MySQL-TokuDB:MySQL 高性能存储引擎:TokuDB
ylbtech-MySQL-TokuDB:MySQL 高性能存储引擎:TokuDB 1.返回顶部 1. 在安装MariaDB的时候了解到代替InnoDB的TokuDB,看简介非常的棒,这里对ToduD ...
- MySQL优化-存储引擎
MySQL优化-存储引擎 标签(空格分隔): mysql 存储引擎 查看存储引擎 show engines Myisam: 表锁 全文索引 Innodb: 行锁 事物 外键 Memory: 内存存储引 ...
- [转帖]mysql常用存储引擎(InnoDB、MyISAM、MEMORY、MERGE、ARCHIVE)介绍与如何选择
mysql常用存储引擎(InnoDB.MyISAM.MEMORY.MERGE.ARCHIVE)介绍与如何选择原创web洋仔 发布于2018-06-28 15:58:34 阅读数 1063 收藏展开 h ...
- [转载]MySQL的存储引擎
[转载]MySQL的存储引擎 来源:https://www.cnblogs.com/lina1006/archive/2011/04/29/2032894.html 其实这是个只有在MySQL中才存在 ...
随机推荐
- 在论坛中出现的比较难的sql问题:35(时间间隔计算问题)
原文:在论坛中出现的比较难的sql问题:35(时间间隔计算问题) 所以,觉得有必要记录下来,这样以后再次碰到这类问题,也能从中获取解答的思路.
- MySQL 查询优化 - 关联查询
1. 关联查询执行流程 MySQL执行关联查询的策略很简单,他会从一个表中循环取出单条数据,然后用该条数据到下一个表中寻找匹配的行,然后回溯到上一个表,到所有的数据匹配完成为止.因此也被称为" ...
- 【转】SpringCloud学习笔记(一)——基础
什么是微服务架构 简单地说,微服务是系统架构上的一种设计风格,它的主旨是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间通过基于HTTP的RESTful API进 ...
- WCF Restful Service
对 Web Services.WCF 和 Restful 的扫盲可参见:https://www.cnblogs.com/scy251147/p/3382436.html 关于之前对 WCF 的学习,可 ...
- Linux 之 软件安装
单纯一个操作系统是没有办法满足我们的需求的,所以需要各种安装各种软件来满足我们日常工作.生活需求.一般情况下,Linux常用的安装方式有两种,以CentOS为例: 1.从源代码安装软件 将软件源代码编 ...
- TestNG并发执行用例详解和范例
前言 TestNG有多种并发方式支持,方法的并发,class级的并发,test级的并发等:根据实际应用可以灵活的配置和使用,下面分别对几种并发方法进行说明: 一.方法级并发 方法级并发即method级 ...
- Java Data JPA +hibernate 保存或者是查询遇到的坑
由于项目需求,接触了Java Data JPA +hibernate,它的调用方式是controller调用service,service有实现的接口serviceimpl,serviceimpl调用 ...
- 数据库中的Schema到底是什么
参考:http://database.guide/what-is-a-database-schema/ 在数据库中,schema(发音 “skee-muh” 或者“skee-mah”,中文叫模式)是数 ...
- python requests包的基本使用
Requests 安装requests模块: D:\Install\Python36>pip3 install requests 请求方式 #coding:utf-8 import reques ...
- PYTHON WEB开发学习路线
两年大数据广告项目测试:项目覆盖几千万用户前景还不错:只是在工作中感觉测试太无力,最近准备辞职转web开发:同时会离开成都,(/(ㄒoㄒ)/~~待了6年,要离开喜欢的城市,喜欢的女孩很难受) ps:列 ...