锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
MySQL中的锁,按照锁的粒度分,分为以下三类:
全局锁
: 锁定数据库中的所有表。表级锁
: 每次操作锁住整张表。行级锁
: 每次操作锁住对应的行数据。全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。
其典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。
加全局锁
flush tables with read lock;
备份数据
mysqldump -h指定主机地址 -uroot -p密码 需要备份的数据库 > 需要备份到的SQL文件;
解锁
unlock tables;
特点
数据库中加全局锁,是一个比较重的操作,存在以下问题:
如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆。
如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟。
在InnoDB引擎中,我们可以在备份时加上参数–single-transaction参数来完成不加锁的一致性数据备份。
mysqldump --single-transaction -uroot-p密码 需要备份的数据库 > 需要备份到的SQL文件;
表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中。
对于表锁,分为两类:
语法:
加锁:lock tables 表名... read/write。
释放锁:unlock tables/ 客户端断开连接。
只能够读取该表的内容,但是不能够修改里面的数据,其他客户端要修改其中的内容时,会发生阻塞
MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。
为了避免DML与DDL冲突,保证读写的正确性。
在MySQL5.5中引入了MDL,当对一张表进行增删改查的时候,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)。
对应SQL | 锁类型 | 说明 |
---|---|---|
lock tables xxx read / write | SHARED_READ_ONLY / SHARED_NO_READ_WRITE | |
select 、select … lock in share mode | SHARED_READ 与SHARED_READ、SHARED_WRITE | 兼容,与EXCLUSIVE互斥 |
insert 、update、delete、select … for update | SHARED_WRITE 与SHARED_READ、SHARED_WRITE | 兼容,与EXCLUSIVE互斥 |
alter table … | EXCLUSIVE | 与其他的MDL都互斥 |
select object type,object_ schema,object_ name,lock_type,lock_duration from performance schema.metadata_locks;
意向共享锁(IS)
:由语句select…lock in share mode 添加
与表锁共享锁(read)兼容,与表锁排它锁(write)互斥。
意向排他锁(IX)
:由insert、 update、delete.select … for update添加。
与表锁共享锁(read)及排它锁(write)都互斥。意向锁之间不会互斥。
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;
行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。
InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类
:
行锁(Record Lock)
︰锁定单个行记录的锁,防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持。间隙锁(Gap Lock)
∶锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RRA隔离级别下都支持。临键锁(Next-Key Lock)
∶行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。分为下面两种行锁:
共享锁(S)
︰允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁。排他锁(X)
∶允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。语法展示:
S(共享锁) | X(排他锁) | |
---|---|---|
S(共享锁) | 兼容 | 冲突 |
X(排他锁) | 冲突 | 冲突 |
加锁情况:
SQL | 行锁类型 | 说明 |
---|---|---|
INSERT … | 排他锁 | 自动加锁 |
UPDATE… | 排他锁 | 自动加锁 |
DELETE … | 排他锁 | 自动加锁 |
SELECT(正常) | 不加任何锁 | |
SELECT …LOCK IN SHARE MODE | 共享锁 | 需要手动在SELECT之后加LOCK IN SHARE MODE |
SELECT … FOR UPDATE | 排他锁 | 需要手动在SELECT之后加FOR UPDATE |
默认情况下,InnoDB在REPEATABLE READ事务隔离级别运行,InnoDB使用next-key锁进行搜索和索引扫描,以防止幻读。
针对唯一索引进行检索时,对已存在的记录进行等值匹配时,将会自动优化为行锁
。InnoDB的行锁是针对于索引加的锁,不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁。
可以通过以下SQL,查看意向锁及行锁的加锁情况:
select object_schema,object_name,index_name,ock_type.lock mode,lock data from performance_schema.data_locks;
默认情况下,InnoDB在 REPEATABLE READ事务隔离级别运行,InnoDB使用
next-key
锁进行搜索和索引扫描,以防止幻读。
特点:
注意
:
间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。
MySQL5.5版本开始,默认使用InnoDB存储引擎,它擅长事务处理,具有崩溃恢复特性,在日常开发中使用非常广泛。下面是/nnoDB架构图,左侧为内存结构,右侧为磁盘结构。
Master Thread
核心后台线程,负责调度其他线程,还负责将缓冲池中的数据异步刷新到磁盘中,保持数据的一致性,还包括脏页的刷新、合并插入缓存、undo页的回收
I0 Thread
在InnoDB存储引警中大量使用了AI来处理I0请求这样可以极大地提高数据库的性能,而10Thread主要负责这些lO请求的回调
线程类型 | 默认个数 | 职责 |
---|---|---|
Read thread | 4 | 负责读操作 |
Write thread | 4 | 负责写操作 |
Log thread | 1 | 负责将日志缓冲区刷新到磁盘 |
Insert buffer thread | 1 | 负责将写缓冲区内容刷新到磁盘 |
Purge Thread
主要用于回收事务已经提交了的undo log,在事务提交之后,undo log可能不用了,就用它来回收
Page Cleaner Thread
协助Master Thread 刷新脏页到磁盘的线程,它可以减轻 Master Thread 的工作压力,减少阻塞
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
特性:
原子性
(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。一致性
(Consistency):事务完成时,必须使所有的数据都保持一致状态。隔离性
(lsolation)∶数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。持久性
(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。
该日志文件由两部分组成:重做日志缓冲
(redo log buffer)以及重做日志文件
(redo log file) ,前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都存到该日志文件中,用于在刷新脏页到磁盘,发生错误时,进行数据恢复
使用。
回滚日志,用于记录数据被修改前的信息,作用包含两个:提供回滚和MVCC(多版本并发控制)。
区别:
undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。
Undo log销毁: undo log在事务执行时产生,事务提交时,并不会立即删除undo log,因为这些日志可能还用于MVCC。
Undo log存储: undo log采用段的方式进行管理和记录,存放在前面介绍的rollback segment 回滚段中,内部包含1024个undo logsegment。
读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:
select...lock in share mode(共享锁), select ... for update、update、insert、delete(排他锁)
都是一种当前读。
简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。
Read Committed:每次select,都生成一个快照读。
Repeatable Read:开启事务后第一个select语句才是快照读的地方。
Serializable:快照读会退化为当前读。
MVCC
全称Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为MysQL实现MVCC提供了一个非阻塞读功能。MVCC的具体实现,还需要依赖于数据库记录中的三个隐式字段、undo log日志、readView。
隐藏字段 | 含义 |
---|---|
DB_TRX_ID | 最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID。 |
DB_ROLL_PTR | 回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版本 |
DB_ROW_ID | 隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段 |
回滚日志,在insert update.delete的时候产生的便于数据回滚的日志。
当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除。
而update、delete的时候,产生的undo log日志不仅在回滚时需要,在快照读时也需要,不会立即被删除。
不同事务或相同事务对同一条记录进行修改,会导致该记录的undolog生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。
ReadView(读视图)是 快照读 SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的) id。
ReadView中包含了四个核心字段:
字段 | 含义 |
---|---|
m_ids | 当前活跃的事务ID集合 |
min_trx_id | 最小活跃事务ID |
max_trx_id | 预分配事务ID,当前最大事务ID+1(因为事务ID是自增的) |
creator_trx_id | ReadView创建者的事务ID |
- READ COMMITTED∶在事务中每一次执行快照读时生成ReadView。
- REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView
RC隔离级别下,在事务中每一次执行快照读时生成ReadView。
RR隔离级别下,仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。
Mysql数据库安装完成后,自带了一下四个数据库,具体作用如下:
数据库 | 含义 |
---|---|
mysql | 存储MySQL服务器正常运行所需要的各种信息(时区、主从、用户、权限等) |
information_schema | 提供了访问数据库元数据的各种表和视图,包含数据库、表、字段类型及访问权限等 |
performance_schema | 为MySQL服务器运行时状态提供了一个底层监控功能,主要用于收集数据库服务器性能参数 |
sys | 包含了一系列方便DBA和开发人员利用performance_schema性能数据库进行性能调优和诊断的视图 |
语法:
mysql [options] [database]
选项:
-U, --user=name #指定用户名
-p,--password[=name] #指定密码
-h, --host=name #指定服务器IP或域名
-P, --port=port #指定连接端口
-e,--execute=name #执行SQL语句并退出
mysql-uroot -p123456 db01 -e "select * from stu";
mysqladmin是一个执行管理操作的客户端程序。可以用它来检查服务器的配置和当前状态创建并删除数据库等。
语法:
mysqladmin --help
由于服务器生成的二进制日志文件以二进制格式保存,所以如果想要检查这些文本的文本格式,就会使用到mysqlbinlog日志管理工具。
语法:
mysqlbinlog [options] log-files1 log-files2 ...
选项:
-d, --database=name 指定数据库名称,只列出指定的数据库相关操作。
-O, --offset=# 忽略掉日志中的前n行命令。
-r, --result-file=name 将输出的文本格式日志输出到指定文件。
-s, --short-form 显示简单格式,省略掉一些信息。
--start-datatime=date1 --stop-datetime=date2 指定日期间隔内的所有日志。
--start-position=pos1 --stop-position=pos2 指定位置间隔内的所有日志。
mysqlshow客户端对象查找工具,用来很快地查找存在哪些数据库、数据库中的表、表中的列或者索引。
语法:
mysqlshow [options] [db_name [table_name [col_name]
选项:
--count 显示数据库及表的统计信息(数据库,表均可以不指定)
-i 显示指定数据库或者指定表的状态信息
示例:
#查询每个数据库的表的数量及表中记录的数量
mysqlshow-uroot-p2143 --count
#查询test库中每个表中的字段书,及行数
mysqlshow-uroot-p2143 test --count
#!查询test库中book表的详细情况
mysqlshow-uroot-p2143 test book --count
mysqldump客户端工具用来备份数据库或在不同数据库之间进行数据迁移。备份内容包含创建表,及插入表的SQL语句
语法:
mysqldump [options] db_name [tables]
mysqldump [options] --database/-B db1 [db2 db3..]
mysqldump [options] --all-databases/-A
连接选项:
-u, --USer=name 指定用户名
-p,--password[=name] 指定密码
-h, --host=name 指定服务器ip或域名
-P, --port=# 指定连接端口
输出选项:
--add-drop-database 在每个数据库创建语句前加上drop database语句
--add-drop-table 在每个表创建语句前加上 drop table语句,默认开启;不开启(--skip-add-drop-table)
-n, --no-create-db 不包含数据库的创建语句
-t, --no-create-info 不包含数据表的创建语句
-d --no-data 不包含数据
-T, --tab=name 自动生成两个文件:一个.sql文件,创建表结构的语句;一个.txt文件,数据文件
mysqlimport是客户端数据导入工具,用来导入mysqldump加-T参数后导出的文本文件。
需要在其信任的路径下进行导入
语法:
mysqlimport [options] db_name textfile1 [textfile2...]
示例:
mysqlimport-uroot-p2143 test /tmp/city.bxt
如果需要导入sql文件,可以使用mysql中的source指令:
语法:
source /root/xooox.sql