前言
一、锁
🍟锁是计算机协调多个进程或线程并发访问某一资源的机制。
🍟在数据库中,除传统的计算资源(CPU、RAM、磁盘I/O)的争用以外,数据也是一种供许多用户共享的资源。
🍟如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
🍟MySQL中的锁,按照锁的粒度分,分为以下三类:
- 全局锁:锁定数据库中的所有表。
- 表级锁:每次操作锁住整张表。
- 行级锁:每次操作锁住对应的行数据。
1. 全局锁
🍟全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。
🍟其典型的使用场景是做全库的逻辑备份(备份成一个SQL文件到磁盘中),对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。
🍟语法:
注意:
mysqldump -h 主机IP地址 -uroot -p密码 要备份的表名 > 输出到的.sql文件地址
不是SQL语句,要在Windows命令行执行
🍟数据库中加全局锁,是一个比较重的操作,存在以下问题:
- 如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆。
- 如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟。
在InnoDB引擎中,我们可以在备份时加上参数
--single-transaction
参数来完成不加锁的一致性数据备份。
mysqldump --single-transaction -h 主机IP地址 -uroot -p密码 要备份的表名 > 输出到的.sql文件地址
2. 表级锁
🍟表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中。
🍟对于表级锁,主要分为以下三类:
- 表锁
- 元数据锁( meta data lock,MDL)
- 意向锁
🍟对于表锁,分为两类:
- 表共享读锁( read lock):
lock tables 表名 read;
对于所有客户端都不阻塞读,但是会阻塞写
- 表独占写锁(write lock):
lock tables 表名 write
仅当前客户端(加锁客户端)可读可写,其他客户端全阻塞
# 释放锁(或者客户端断开连接自动解锁)
unlock tables;
🍟元数据锁(meta data lock,MDL):MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。为了避免DML与DDL冲突,保证读写的正确性。
🍟在MySQL5.5中引入了MDL,当对一张表进行增删改查的时候,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)。
# 查看元数据锁
select object_type, object_schema, object_name, lock_type, lock_duration from performance_schema.metadata_locks;
3. 意向锁
说到意向锁就不得不提到多粒度树,多粒度树的根节点就是整个数据库,表示最大的数据粒度,叶子结点表示最小的数据粒度,下图为一个三级粒度树,多粒度封锁协议:对一个结点加锁意味着这个结点后所有后裔结点也被加以同类型的锁。
意向锁就是在要加表锁的上一级添加一个标记,说明该结点的下层结点正在被加锁!
🍟为了避免DML在执行时,加的行锁与表锁的冲突,在innoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。
🍟意向锁的分类:
- 意向共享锁(IS):由语句
select ...lock in share mode
添加,与表锁共享锁(read)兼容,与表锁排它锁(write)互斥。 - 意向排他锁(IX):由
insert
、update
、delete
、select ... for update
添加,与表锁共享锁(read)及排它锁(write)互斥。意向锁间不互斥。
# 可以通过以下SQL,查看意向锁及行锁的加锁情况:
select object_schema, object_name, lock_type, lock_mode, lock_data from performance_schema.data_locks;
🌰意向锁具体操作:
# 终端一先开启事务
begin;
# 终端一加上行锁共享锁以及这张表的意向共享锁(ls)
select * from score where id = 1 lock in share mode;
# 查看意向锁及行锁的加锁情况:
select object_schema, object_name, lock_type, lock_mode, lock_data from performance_schema.data_locks;
# 终端二加上表共享读锁(成功),之后解锁
lock tables score read;
unlock tables;
# 再给终端二加上写锁(阻塞,待终端一提交事务commit后执行成功)
lock tables score write;
commit;
unlock tables;
# 终端一执行update时自动加上行锁、意向排他锁(lx)
begin;
update score set math=66 where id=1;
# 此时终端二加表共享读锁(阻塞,待终端一提交事务commit后执行成功)
lock tables score read;
commit;
unlock tables;
4. 行级锁
🍟行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。
🍟InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类:
-
行锁(Record Lock)∶锁定单个行记录的锁,防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持。
-
间隙锁(Gap Lock)∶锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持。
-
临键锁(Next-Key Lock)︰行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。
4.1. 行锁
🍟InnoDB实现了一下两种类型的行锁:
- 共享锁(S)∶允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁。
- 排他锁(X)︰允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。
🍟在进行常见的增删改查操作加锁类型:
默认情况下,InnoDB在 REPE
ATABLE READ
(默认)事务隔离级别运行,InnoDB使用next-key锁进行搜索和索引扫描,以防止幻读。
- 针对唯一索引(主键)进行检索时,对已存在的记录进行等值匹配时,将会自动优化为行锁。
- InnoDB的行锁是针对于索引加的锁,不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁。
begin;
# 普通selet语句
select * from stu where id = 1;
# 查看意向锁及行锁的加锁情况(没有加锁)
select object_schema, object_name, lock_type, lock_mode, lock_data from performance_schema.data_locks;
# 终端一终端二都加共享锁
select * from stu where id = 1 lock in share mode;
# 对id = 3 的数据执行update操作(成功)
update stu set name = 'mysql' where id = 3;
# 对id = 1 的数据执行update操作(阻塞)
update stu set name = 'mysql' where id = 1;
# update 自动加行锁,然而name字段并无索引,自动升级为表锁
update stu set name = 'mysql' where name = 'Lily';
# 加了表锁,阻塞!
update stu set name = 'mysql' where id = 3;
🌰终端一终端二都加行锁,这一行有两个行锁:
commit
提交事务后自动释放锁
4.2. 间隙锁 & 临键锁
- 索引上的等值查询(唯一索引),给不存在的记录加锁时,优化为间隙锁。
# 表中id = {1,3,8,11,19,25},锁住(3,8)
update stu set age = 10 where id = 5;
# 由于有间隙锁,阻塞(防止幻读)
insert into stu values (7, 'Ruby ',7);
- 索引上的等值查询(普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock(临键锁)退化为间隙锁。
# age 是非唯一索引,(3,7)加间隙锁,3加临键锁
selec * from stu where age = 3 lock in share mode;
- 索引上的范围查询(唯一索引)–会访问到不满足条件的第一个值为止。
# 19行锁,25以及之前的间隙(临键锁),25以后到无穷大加临键锁
select * from stu where id >= 19 lock in share mode;
注意:间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。
二、InnoDB引擎
ibd
文件在MySQL目录下的data/表名/
文件夹中
1. 逻辑存储结构
🍟结构图:
- 表空间(ibd文件),一个mysql实例可以对应多个表空间,用于存储记录、索引等数据。
- 段,分为数据段(Leaf node segment)、索引段(Non-leaf node segment)、回滚段(Rollback segment),InnoDB是索引组织表,数据段就是B+树的叶子节点,索引段即为B+树的非叶子节点。段用来管理多个Extent (区)。
- 区,表空间的单元结构,每个区的大小为1M。默认情况下,InnoDB存储引擎页大小为16K,即一个区中一共有64个连续的页。
- 页,是InnoDB存储引擎磁盘管理的最小单元,每个页的大小默认为16KB。为了保证页的连续性,InnoDB存储引擎每次从磁盘申请4-5个区。
- 行,InnoDB存储引擎数据是按行进行存放的。
Trx_id
:每次对某条记录进行改动时,都会把对应的事务id赋值给trx_id隐藏列。
Roll_pointer
:每次对某条引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。
2. 架构
MySQL5.5版本开始,默认使用InnoDB存储引擎,它擅长事务处理,具有崩溃恢复特性,在日常开发中使用非常广泛。
2.1. 内存架构
- Bufer Pool(80%的内存分给缓冲区,提高并发性能)
- Change Buffer
- Adaptive_hash_index
# 查询自适应哈希是否开启
show variable like '%hash_index%';
- Log Buffer
# 查询这两个参数
show variables like '%log_buffer_size%';
show variables like '%flush_log%';
2.2. 磁盘结构
- System Tablespace & File-per-Table Tablespace
# 查看参数
show variables like 'data_file_path';
show variables like 'file_per_table';
- Tablespaces
# 创建通用表空间
create tablespace ts_ygy datafile 'ygy.ibd' engine = innodb;
# 需要自己去指定关联的表空间
create table a(id int primary key auto_increment, name varchar(10)) engine = innodb tablespace ts_ygy;
- Doublewrite Buffer Files & Redo Log
2.3. 后台线程
# 查看IO线程信息
show engine innodb status;
3. 事务原理
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时生败。
- 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
- 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态
- 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
- 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
3.1. redo log(持久性)
1️⃣先检查缓冲区有没有对应数据,没有则从磁盘中读取得到,再进行增删改操作,buffer pool
存储的数据页变成脏页
2️⃣将 buffer pool
中的修改信息存到 read log
中,并写入到磁盘中ib_logfile0/1
两个文件中
3️⃣每隔一段时间buffer pool
数据就要更新到磁盘中永久保存
4️⃣一旦buffer pool
更新失败,则通过ib_logfile0/1
恢复数据
5️⃣定期删除ib_logfile0/1
3.2. undo log(原子性)
隔离性&一致性:
4. MVCC(面试高频)
4.1. 相关概念
🍟当前读:读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:select ... lock in share mode
(共享锁),select ... for update
、update
、insert
、delete
(排他锁)都是一种当前读。
# 终端二执行更新操作
update stu set name - 'jsp' where id = 1;
# 再在终端一执行查询操作(还是老数据)
select * from stu;
# 读取记录的最新版本(当前读)
select * from stu lock in share mode;
🍟快照读:简单的select(不加锁)就是快照读,快照读读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读(通过MVCC查找对应的历史版本)
Read Committed
:每次select,都生成一个快照读。Repeatable Read
:开启事务后第一个select语句才是快照读的地方(后续读该快照)Serializable
:快照读会退化为当前读。
🍟MVCC:Multi-version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为MySQL实现MVCC提供了一个非阻塞读功能。MVCC的具体实现,还需要依赖于数据库记录中的三个隐式字段、undo log日志、readView。
4.2. 实现原理
🍟记录中的隐藏字段:DB_TRX_ID
,DB_ROLL_PTR
,DB_ROW_ID
# 查看ibd文件的信息
ibd2sdi stu.ibd
🍟undo log日志:回滚日志,在inser
t、 update
、delete
的时候产生的便于数据回滚的日志。当insert
的时候,产生的undo log
日志只在回滚时需要,在事务提交后,可被立即删除。而update
、delete
的时候,产生的undo log
日志不仅在回滚时需要,在快照读时也需要,不会立即被删除。
- undo log版本链
不同事务或相同事务对同一条记录进行修改,会导致该记录的undolog生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。
🍟readview:读视图,是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
- Readview中包含了四个核心字段:
- 版本链数据访问规则:
不同的隔离级别,生成Readview的时机不同:
READ COMMITTED
:在事务中每一次执行快照读时生成Readview.
REPEATABLE READ
:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView.
三、MySQL管理
1. 系统数据库
🍟Mysql数据库安装完成后,自带了一下四个数据库,具体作用如下:
2. 常用工具
2.1. mysql
🍟mysql [options] [database]
,该mysql不是指mysql服务,而是指mysql的客户端工具。
options | 功能 |
---|---|
-u | 指定用户名 |
-p | 指定密码 |
-h | 指定服务器IP或域名 |
-P | 指定连接端口 |
-e | 可以在MySQL客户端执行SQL语句并退出(不用连接到MySQL数据库,脚本中常用) |
# 连接远程MySQL,并执行一条SQL语句
mysql -h192.168.150.100 -P3306 -uroot -p1234 test01 -e "select * from students";
2. mysqladmin
🍟是一个执行管理操作的客户端程序,可以用来检查服务器的配置和当前状态、创建并删除数据库等
# 通过帮助文档查看选项
mysqladmin --help
# 查看当前MySQL版本
mysqladmin -uroot -p1234 version
# 查看当前MySQL系统变量
mysqladmin -uroot -p1234 variables
# 创建/删除数据库
mysqladmin -uroot -p1234 create db02
mysqladmin -uroot -p1234 drop db02
2.3. mysqlbinlog
🍟由于服务器生成的二进制日志文件以二进制格式保存,所以如果想要检查这些文本的文本格式,就会使用到mysqlbinlog日志管理工具,mysqlbinlog [options] log-files1 log-files2 ...
options | 功能 |
---|---|
-d | 指定数据库名称,只列出指定的数据库相关操作 |
-o | 忽略日志的前n行指令 |
-r | 将输出的文本格式日志输出到指定文件 |
-s | 显示简单格式,省略一些信息 |
原文链接:https://blog.csdn.net/aben_sky/article/details/121514406
2.4. mysqlshow
🍟客户端对象查找工具,用来很快的查找存在哪些数据库、数据库中的表、表中的列或者索引,mysqlshow [options] [db_name [table_name [col_name]]]
options | 功能 |
---|---|
–count | 显示数据库及表的统计信息(数据库、表均可以不指定) |
-i | 显示指定数据库或者指定表的状态信息 |
# 查询每个数据库的表的数量及表中记录的数量
mysqlshow -uroot -p1234 --count
#查询test库中每个表中的字段数,及行数
mysqlshow -uroot -p1234 test --count
#查询test库中book表的详细情况
mysqlshow -uroot -p1234 test book --count -i
2.5. mysqldump
🍟mysqldump客户端工具用来备份数据库或在不同数据库之间进行数据迁移。备份内容包含创建表,及插入表的SQL语句
# 备份数据库db01到db01.sql中
mysqldump -uroot -p1234 db01 > db01.aql
# 不要建表语句,只有插入语句,只记录数据信息
mysqldump -uroot -p1234 -t db01 > db02.aql
# 不要插入语句,只有建表语句,只记录表结构信息
mysqldump -uroot -p1234 -d db01 > db03.aql
# 备份成两个文件,但是MySQL认为/root目录不安全,只生成一个sql文件
mysqldump -uroot -p1234 -T /root db01 stu
# 查询MySQL信任的文件目录 --> /var/lib/mysql-files/
show variables like '%secure-file-priv%';
# 成功生成两个文件sql/txt
mysqldump -uroot -p1234 -T /var/lib/mysql-files/ db01 stu
2.6. mysqlimport/source
🍟mysqlimport是客户端数据导入工具,用来导入mysqldump加-T
参数后导出的文本文件,mysqlimport [options] db_name textfile1
# 将表删除后,利用txt文本文件导回
mysqlimport -uroot -p1234 db01 /var/lib/mysql-files/score.txt
# 如果需要导入sql文件,可以使用mysql中的source指令:
source /var/lib/mysql-files/score.sql
总结:
Next
✍下一篇:MySQL日志!