存储引擎
MySQL体系结构

MySQL体系结构包括四层
连接层
最上层是一些客户端和链接服务,主要完成一些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
服务层
第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如过程、函数等。
引擎层
存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。
存储层
主要是将数据存储在文件系统之上,并完成与存储引擎的交互。
存储引擎简介
存储引擎是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的,所以存储引擎也被叫做表类型
MySQL默认存储引擎是InnoDB
在建表时,指定存储引擎
CREATE TABLE 表名(
字段1 字段1类型 [COMMENT 注释]
......
)ENGINE = INNODE [COMMENT 注释]
查看当前数据库支持的存储引擎
SHOW ENGINES;
存储引擎特点

InnoDB
是一种兼顾高可靠性和高性能的通用存储引擎,在5.5版本后是默认存储引擎
特点:
DML操作遵循ACID模型,支持事务
行级锁,提高并发访问性能
支持外键FOREIGN KEY约束,保证数据的完整性和正确性
文件:xxx.ibd文件,xxx是表名,每张表对应一个表空间,存储表的表结构、数据和索引。
参数:innode_file_per_table
逻辑存储结构

MyISAM
是MySQL早期的默认存储引擎
特点
不支持事务,不支持外键
支持表锁,不支持行锁
访问速度快
文件
xxx.sdi:存储表结构信息
xxx.MYD:存储数据
xxx.MYI:存储索引
Memory
数据存放在内存中,由于受到硬件问题,或断电问题的影响,只能作为临时表或缓存使用
特点:
内存索引
哈希索引(默认)
文件:xxx.sdi:存储表结构信息
存储引擎选择
在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。
InnoDB:是Mysql的默认存储引擎,支持事务、外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择。
MyISAM:如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。
MEMORY:将所有数据保存在内存中,访问速度快,通常用于临时表及缓存。MEMORY的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性。
索引
索引概述
索引是帮助MySQL高效获取数据的有序的数据结构。
优缺点:

索引结构
MySQL的索引在引擎层实现,不同的存储引擎有不同的结构:


平常所说的索引一般为B+Tree索引
二叉树

二叉排序树:顺序插入时退化为链表,查询速度降低。大数据量时层级较深,检索速度慢。
红黑树:大数据量下,层级较深,检索速度慢。
B-Tree(多路平衡查找树)
以一颗最大度数(一个节点的子节点数)为5(5阶)的B-tree为例,每个节点对多存储4个key,5个指针

插入新元素时,如果key未满就直接插入,如果key大于4,中间元素向上裂变作为父节点指向两个子节点(两个子节点分别分配两端的key)
B+Tree
以一颗最大度数为4的B+Tree为例:

叶子节点形成链表,节点向上裂变时会出现在叶子节点。
特点
所有元素都会出现在叶子节点,非叶子节点主要用于索引。
叶子节点形成一个单向链表,每个叶子节点指向下一个叶子节点。
MySQL在经典B+树上进行了优化,叶子节点形成的是双向链表,提高了区间访问的性能。

Hash
采用一定的hash算法,将键值换算为新的hash值,映射到对应的槽位上,然后存储在hash表中。
如果发生hash碰撞,就可以通过链表来解决。
特点:
只能用于等值匹配,不支持范围查询
也不能利用进行排序操作
查询效率搞,通常只需要一次检索(如果没有哈希碰撞)效率通常高于B+Tree
在MySQL中只有Memory支持hash,但InnoDB中存在自适应hash概念,hash索引是存储引擎根据B+Tree索引在特定条件下自动构造的。
Q&A
为什么InnoDB采用B+Tree?
相对于二叉树,层级更少,搜索效率更高
相对于B-Tree,B-Tree所有节点都会保存数据,会导致每页存储的键值减少,每个节点存储的指针减少(扇出度减小),同样要保存大量数据时,只能增加高度,导致性能降低。且B+Tree在叶子节点形成链表,便于查找
如果不考虑存储空间大小,使用B-Tree可能在非叶子节点就能获取值,效率可能高于B+Tree,但是很难不考虑空间大小的问题,
一方面扇出度太大,B-Tree节点太大会导致单页很大,造成单次IO开销更高;
另一方面占用太多空间会导致缓存效率太低一次读取很大的一个页但是可能只能命中一条数据,同样的缓存大小可能只能命中很少的数据量。
且一次读取一页但是只为了一条数据,会导致读放大太过严重
而且B-Tree数据分散,对于范围查询过慢。
而且如果对于HDD机械硬盘,页大小的影响小,因为寻址延迟很大,传输延迟的影响小,页大小对总延迟影响小。可是如果对于SSD固态硬盘,寻址延迟很小,传输次数对读取速率的影响小,主要瓶颈在传输延迟,页太大会造成性能开销。因此使用更小的页更适配SSD时代的硬件
相对Hash,只支持等值匹配,不支持范围匹配
索引分类

在InnoDB中,根据索引的存储形式分为:

默认主键索引是聚集索引,如果表不存在主键,使用第一个唯一索引作为聚集索引。如果都没有,就会自动生成一个rowid作为隐藏的聚集索引。
对于聚集索引,B+Tree叶子节点下面存储的数据的一行的行数据。
对于二级索引,B+Tree叶子节点下面存储的是主键值(或对应的聚集索引的键值)

先进行二级索引查询,再查找聚集索引,叫做回表查询。
如果使用主键(或聚集索引键值)查找,会直接进入聚集索引查询
索引语法
创建索引
CREATE [UNIQUE|FULLTEXT] INDEX 索引名 ON 表名(索引关联字段名,......);
如果索引关联一个字段叫做单列索引,否则就是联合索引
查看索引
SHOW INDEX FROM 表名;
删除索引
DROP INDEX 索引名 ON 表名;
SQL性能分析
SQL执行频率
MySQL客户端连接后,通过show[session|global]status命令可以提供服务器状态信息。通过如下指令可以查看当前数据库的insert、update、delete、select的访问频次;
SHOW GLOBAL STATUS LIKE 'Com______';
慢查询日志
慢查询日志记录了所有执行时间超过预定参数(long_query_time,单位s,默认10s)的所有SQL语句的日志。
MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/my.cnf)中配置:

慢查询的日志文件中记录的信息位于/var/lib/mysql/localhost-slow.log
profile详情
show profiles能够在做SQL优化时帮助了解时间都耗费在那里。通过hava_profiling参数,能够看到当前MySQL是否支持profile操作
SELECT @@have_profiling
默认是关闭的,可以使用set在session/global级别开启:
SET profiling=1;
查找每一条SQL的耗时情况
SHOW PROFILES;
查找指定query_id的SQL的各个阶段的耗时情况
SHOW PROFILE FOR QUERY query_id;
查找指定query_id的SQL的cpu的耗时情况
SHOW PROFILE CPU FOR QUERY query_id;
explain执行计划
EXPLAIN或者DESC命令获取MySQL如何执行SELECT语句的信息,包括SELECT语句执行过程中表如何连接和连接的顺序
语法:
EXPLAIN/DESC SELECT 字段列表 FROM 表名 WHERE 条件;
各字段含义:
id:select查询的序列号,表示查询中执行的select子句或者操作表的顺序(id相同就从上到下,不同,值越大越先执行)
select_type:表示select的类型:SAMPLE(简单表,不使用表连接或子查询),PRIMARY(主查询,即外层的查询)UNION(UNION中的第二个或后面的查询语句)SUBQUERY(SELECT/WHERE之后包含了子查询)
type:表示连接类型,性能由好到差为NULL(基本上不访问表),system(一般为访问系统表),const(一般是主键或唯一索引),eq_ref,ref(非唯一性索引查询),range,index(用了索引,但是对索引扫描),all(全表扫描);
possible_key:显示可能在这张表上用到的索引
key:实际用到的索引
key_len:索引中使用的字节数,表示索引字段最大可能长度,并非实际使用长度,不损失精确性的前提下,长度越短越好
ref:索引被什么引用
rows:执行查询的行数(不一定准确)
filtered:返回结果占总读取行数的百分比,越大越好。
extra:额外信息
索引使用规则
验证索引效率
未建立索引之前,执行SQL语句查看SQL的耗时
针对字段创建索引
再次执行系统的SQL语句查看SQL的耗时
最左前缀法则
如果索引了多列(联合索引)要遵循最左前缀法则。指查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将部分失效(后面的字段索引失效)
联合索引
联合索引中,出现范围查询(>、<)范围查询右侧的列索引失效
解决方法是尽可能使用>=或<=
索引失效情况
不要在索引列上使用运算操作,否则索引会失效。
如果字符串类型字段使用时没有加引号,索引将失效。
如果只是尾部进行模糊匹配,索引不会失效;如果是头部模糊匹配,索引将失效。(优化:使用FULLTEXT索引、使用反向索引、使用开头字符串的辅助表、使用正则表达式)
用or分割开的条件,如果or前的条件中的列有索引而后面没有索引,那么涉及的索引都不会被使用。只有都有索引才会被使用。
MySQL在执行SQL时,优化器会评估如何执行索引比全表扫描更慢,就会放弃索引而是执行全表扫描。因为索引只是用于查找少量数据,如果数据过多,就会放弃索引。
SQL提示
SQL提示是优化数据库的一个重要手段,在SQL语句中加入人为的提示来达到优化的目的
USE INDEX;
EXPLAIN SELECT*FROM table_name USE INDEX(index_name)WHERE 条件;
IGNORE INDEX:
EXPLAIN SELECT*FROM table_name IGNORE INDEX(index_name)WHERE 条件;
FORCE INDEX:
EXPLAIN SELECT*FROM table_name FORCE INDEX(index_name)WHERE 条件;
覆盖索引
尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在索引中已经全部能够找到)减少select*;
extra中的字段不同版本的mysql可能会有不同

如果查询的非主键字段被联合索引包含,就不需要回表查询,否则需要回表查询。
联合索引的叶子节点存储的是索引字段和主键字段。
如果使用select*很容易出现回表查询,除非有覆盖全字段的联合索引或使用主键条件。
前缀索引
当索引类型为字符串时,有时候需要索引很大的字符串,会让索引变得很大,浪费大量的磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀建立索引,可以大大节约索引空间从而节省效率。
语法
CREATE INDEX 索引名 ON 表名(字段名(n));
前缀长度:可以根据索引的选择性来区分,选择性是指不重复的索引值和数据表的记录总数的比值,选择性越高查询效率越高。
select count(distinct email)/count(*)from table_name;
select count(distinct substring(email,1,5))/count(*) from table_name;
值得一提的是,前缀索引必定回表查询,第一次索引查询回表查询出的元素不是所需元素时,索引树可以向后查询链表后一位继续遍历。
单列索引和联合索引
单列索引:每个索引只包含一个字段
联合索引:每个索引包含多个字段
如果涉及到多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引而非单列索引。
如果有单列索引干扰联合索引,可以使用SQL提示
索引设计原则
1.针对于数据量较大,且查询比较频繁的表建立索引。
2.针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。
3.尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
4.如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
5.尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
6.要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
7.如果索引列不能存储NULL值,请在创建表时使用NOTNULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。
SQL优化
插入数据
insert优化
批量插入
避免多次与数据库建立连接,如果批量插入字段过多,建议分割为多次批量插入。
insert into tb_test values(插入字段)(插入字段)(插入字段);
手动提交事务
避免事务频繁的开启与关闭
start transaction;
insert into ......
......
commit
主键顺序插入
主键顺序插入的性能要高于乱序插入
大批量插入数据
如果需要一次性插入大批量数据,使用insert语句插入性能较低,此时可以使用MySQL数据库提供的load指令进行插入

load主键顺序插入的性能也更高
主键优化
数据组织方式
在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表叫做索引组织表
MySQL中B+Tree的每个节点都是一个存储页,每个页可以为空,也可以填充一半,也可以填充满。每个页包含至少2行数据(每行相当于一个指针,如果只有一个指针就相当于链表),如果一行数据过大,会行溢出,根据主键排列。如果一个页写满就写在下一个页。
页分裂:如果数据是乱序插入的,插入到一个已经被写满的数据页,会导致页和数据的移动和指针的更改。也叫页分裂。比较消耗性能。
页合并:当删除一条记录时,实际上记录并没有被删除,只是被标记为删除并且它的空间被允许被其他记录声明使用。当页中删除的记录达到合并阈值(MERGE_THRESHOLD)时(默认为50%)InnoDB会开始寻址最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用。
主键设计原则
需要尽量降低主键长度。如果主键比较长,二级索引较多(二级索引也需要存储主键),会占用大量磁盘空间,搜索时也会占用磁盘IO
插入时尽量选择顺序插入,选择使用auto_increment自增主键。
尽量使用uuid或其他自然主键,如身份证号。乱序且长度大
业务操作时,尽量避免修改主键。
order by优化
①.Usingfilesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sortbuffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫FileSort排序。
②.Usingindex:通过有序索引顺序扫描直接返回有序数据,这种情况即为usingindex,不需要额外排序,操作效率高。
order by语句优化时尽量优化为using index;


根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则。
尽量使用覆盖索引。
多字段排序,一个升序一个降序此时需要注意联合索引在创建时的规则(ASC/DESC)。
如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小sort_buffer_size(默认256k)。
group by优化

在分组时建立适当的索引来提高效率
索引的使用也需要满足最左前缀法则
limit优化
一个常见又非常头疼的问题就是limit 2000000,10,此时需要MySQL排序前2000010记录,仅仅返回2000000-2000010的记录,其他记录丢弃,查询排序的代价非常大
优化策略是覆盖索引和子查询。
count优化
MyISAM引擎中会把一个表的总行数存储到磁盘,执行count(*)的时候会直接返回,效率很高
InnoDB引擎必须把数据一行一行的从引擎中读取出来并累积技术
优化思路:自己计数(插入删除时维护一个计数)
count()是一个聚合函数,对于返回的结果集,一行行地判断,如果count函数的参数不是NULL,累计值就加1,否则不加,最后返回累计值。
用法:count(*)、count(主键)、count(字段)、count(1)
>count(主键)
InnoDB引擎会遍历整张表,把每一行的主键id值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加(主键不可能为null)。
>count(字段)
没有notnull约束:InnoDB引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加。
有notnull约束:InnoDB引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,直接按行进行累加。
>count (1)
InnoDB引擎遍历整张表,但不取值。服务层对于返回的每一行,放一个数字"1"进去,直接按行进行累加。
>count (*)
InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加。
按照效率排序的话,count(字段)<count(主键id)<count(1)≈count(*),所以尽量使用count(*)。
update优化
InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁。
视图/存储器/触发器
视图
介绍与基础语法
视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。
通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。
创建
CREATE [OR REPLACE] VIEW 视图名称[(列名列表)]AS SELECT语句 [WITH [CASADED|LOCAL]CHECK OPTION]
查询
查看创建视图语句:SHOW CREATE VIEW 视图名称
查看视图数据:SELECT * FROM 视图名称......(和操作表一样)
修改
CREATE [OR REPLACE] VIEW 视图名称[(列名列表)]AS SELECT语句 [WITH [CASADED|LOCAL]CHECK OPTION]
ALTER VIEW 视图名称[(列名列表)] AS SELECT语句 [WITH[CASCADED|LOCAL] CHECK OPTION]
删除
DROP VIEW[IF EXISTS]视图名称[,视图名称]......
视图的检查选项
当使用WITHCHECKOPTION子句创建视图时,MySQL会通过视图检查正在更改的每个行,例如插入,更新,删除,以使其符合视图的定义。MySQL允许基于另一个视图创建视图,它还会检查依赖视图中的规则以保持一致性。为了确定检查的范围,mysql提供了两个选项:
CASCADED和LOCAL,默认值为CASCADED(级联)
CASCADED(级联)
比如,v2视图是基于v1视图的,如果在v2视图创建的时候指定了检查选项为 cascaded,但是v1视图创建时未指定检查选项。 则在执行检查时,不仅会检查v2,还会级联检查v2的关联视图v1。
LOCAL(本地)
比如,v2视图是基于v1视图的,如果在v2视图创建的时候指定了检查选项为 local ,但是v1视图创建时未指定检查选项。 则在执行检查时,知会检查v2,不会检查v2的关联视图v1
视图的更新与作用
视图要想更新,视图中的行与表中的行之间必须存在一对一关系。
作用
简单:视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件。
安全:数据库可以授权,但不能授权到数据库特定行和特定的列上。通过视图用户只能查询和修改他们所能见到的数据
数据独立:视图可帮助用户屏蔽真实表结构变化带来的影响。
存储过程
介绍
存储过程是事先经过编译并存储在数据库中的一段SQL语句的集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。
存储过程思想上很简单,就是数据库SQL语言层面的代码封装与重用。
特点:
封装,复用
可以接收参数,也可以返回数据
减少网络交互,效率提升
MySQL存储过程中的声明必须按照这个顺序:
顺序规则:
- 变量 (DECLARE var) ← 必须首先
- 条件 (DECLARE CONDITION) ← 其次
- 游标 (DECLARE CURSOR) ← 再次
- 处理器 (DECLARE HANDLER) ← 最后
- 代码执行 ← 最后
语法
创建
CREATE PROCEDURE 存储过程名称([参数列表])
BEGIN
SQL语句
END;
调用
CALL 名称(参数)
查看
SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA = 'xxx'; -- 查询指定数据库的存储过程及状态信息
SHOW CREATE PROCEDURE 存储过程名称 ; -- 查询某个存储过程的定义
删除
DROP PROCEDURE [ IF EXISTS ] 存储过程名称 ;
注意:在命令行中,执行创建存储过程的SQL时,需要通过关键字delimiter指定SQL语句的结束符。
变量
系统变量
系统变量是MySQL服务器提供,不是用户定义的,属于服务器层面。分为全局变量(GLOBAL)、会话变量(SESSION)。
查看系统变量
SHOW [SESSION|GLOBAL] VARIABLES查看所有系统变量
SHOW [SESSION|GLOBAL]VARIABLES LIKE·.;可以通过LIKE模糊匹配方式查找变量
SELECT @@[SESSION|GLOBAL] 系统变量名;查看指定变量的值
设置系统变量
SET [SESSION| GLOBAL] 系统变量名=值;
SET @@[SESSION|GLOBAL] 系统变量名=值;
注意:
如果没有指定SESSION/GLOBAL,默认是SESSION,会话变量。
mysql服务重新启动之后,所设置的全局参数会失效,要想不失效,可以在 /etc/my.cnf 中配置。
A. 全局变量(GLOBAL): 全局变量针对于所有的会话。
B. 会话变量(SESSION): 会话变量针对于单个会话,在另外一个会话窗口就不生效了。
用户定义变量
用户定义变量 是用户根据需要自己定义的变量,用户变量不用提前声明,在用的时候直接用 "@变量名" 使用就可以。其作用域为当前连接。
1). 赋值
方式一:
SET @var_name = expr [, @var_name = expr] ... ;
SET @var_name := expr [, @var_name := expr] ... ;
赋值时,可以使用 = ,也可以使用 := 。
方式二:
SELECT @var_name := expr [, @var_name := expr] ... ;
SELECT 字段名 INTO @var_name FROM 表名;
2). 使用
SELECT @var_name ;
注意: 用户定义的变量无需对其进行声明或初始化,只不过获取到的值为NULL。
局部变量
局部变量 是根据需要定义的在局部生效的变量,访问之前,需要DECLARE声明。可用作存储过程内的局部变量和输入参数,局部变量的范围是在其内声明的BEGIN ... END块
声明
DECLARE 变量名 变量类型 [DEFAULT ... ] ;
变量类型就是数据库字段类型:INT、、BIGINT、CHAR、VARCHAR、DATE、TIME等。
赋值
SET 变量名 = 值 ;
SET 变量名 := 值 ;
SELECT 字段名 INTO 变量名 FROM 表名 ... ;
if
if用于条件判断,语法为
IF 条件1 THEN
.....
ELSEIF 条件2 THEN -- 可选
.....
ELSE -- 可选
.....
END IF;
在if条件判断的结构中,elseif可以有多个也可以没有,else可以有也可以没有。
参数

CREATE PROCEDURE 存储过程名称 ([ IN/OUT/INOUT 参数名 参数类型 ])
BEGIN
-- SQL语句
END ;
case
case结构及作用,和我们在基础篇中所讲解的流程控制函数很类似。有两种语法格式:
语法1
-- 含义: 当case_value的值为 when_value1时,执行statement_list1,当值为 when_value2时,
执行statement_list2, 否则就执行 statement_list
CASE case_value
WHEN when_value1 THEN statement_list1
WHEN when_value2 THEN statement_list2\] ... \[ ELSE statement_list
END CASE;
语法2
-- 含义: 当条件search_condition1成立时,执行statement_list1,当条件search_condition2成
立时,执行statement_list2, 否则就执行 statement_list
CASE
WHEN search_condition1 THEN statement_list1
WHEN search_condition2 THEN statement_list2\] ... \[ELSE statement_list
END CASE;
while
while 循环是有条件的循环控制语句。满足条件后,再执行循环体中的SQL语句。具体语法为:
-- 先判定条件,如果条件为true,则执行逻辑,否则,不执行逻辑
WHILE 条件 DO
SQL逻辑...
END WHILE;
repeat
repeat是有条件的循环控制语句, 当满足until声明的条件的时候,则退出循环 。具体语法为:
-- 先执行一次逻辑,然后判定UNTIL条件是否满足,如果满足,则退出。如果不满足,则继续下一次循环
REPEAT
SQL逻辑...
UNTIL 条件
END REPEAT;
loop
LOOP 实现简单的循环,如果不在SQL逻辑中增加退出循环的条件,可以用其来实现简单的死循环。
LOOP可以配合一下两个语句使用:
LEAVE :配合循环使用,退出循环。
ITERATE:必须用在循环中,作用是跳过当前循环剩下的语句,直接进入下一次循环。
begin_label:\] LOOP SQL逻辑... END LOOP \[end_label\]; LEAVE label; -- 退出指定标记的循环体 ITERATE label; -- 直接进入下一次循环 上述语法中出现的 begin_label,end_label,label 指的都是我们所自定义的标记。 ###### 游标(光标)cursor 游标(CURSOR)是用来存储查询结果集的数据类型 , 在存储过程和函数中可以使用游标对结果集进 行循环的处理。游标的使用包括游标的声明、OPEN、FETCH 和 CLOSE,其语法分别如下。 声明 DECLARE 游标名称 CURSOR FOR 查询语句 ; 打开游标 OPEN 游标名称 ; 获取游标记录 FETCH 游标名称 INTO 变量 \[, 变量 \] ; 关闭游标 CLOSE 游标名称 ; ###### 条件处理程序(handler) 条件处理程序(Handler)可以用来定义在流程控制结构执行过程中遇到问题时相应的处理步骤。具体 语法为: DECLARE handler_action HANDLER FOR condition_value \[, condition_value
... statement ;
handler_action 的取值:
CONTINUE: 继续执行当前程序
EXIT: 终止执行当前程序
condition_value 的取值:
SQLSTATE sqlstate_value: 状态码,如 02000
SQLWARNING: 所有以01开头的SQLSTATE代码的简写
NOT FOUND: 所有以02开头的SQLSTATE代码的简写
SQLEXCEPTION: 所有没有被SQLWARNING 或 NOT FOUND捕获的SQLSTATE代码的简写
存储函数
存储函数是有返回值的存储过程,存储函数的参数只能是IN类型的。具体语法如下:
CREATE FUNCTION 存储函数名称 ([ 参数列表 ])
RETURNS type [characteristic ...]
BEGIN
-- SQL语句
RETURN ...;
END ;
characteristic说明:
DETERMINISTIC:相同的输入参数总是产生相同的结果
NO SQL :不包含 SQL 语句。
READS SQL DATA:包含读取数据的语句,但不包含写入数据的语句。
触发器
介绍
触发器是与表有关的数据库对象,指在insert/update/delete之前(BEFORE)或之后(AFTER),触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性, 日志记录 , 数据校验等操作 。
使用别名OLD和NEW来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还只支持行级触发,不支持语句级触发。影响一行触发一次就是行级触发器,执行一次语句触发一次就是语句级触发。

语法
创建
CREATE TRIGGER trigger_name
BEFORE/AFTER INSERT/UPDATE/DELETE
ON tbl_name FOR EACH ROW -- 行级触发器
BEGIN
trigger_stmt ;
END;
查看
SHOW TRIGGERS ;
删除
DROP TRIGGER [schema_name.]trigger_name ; -- 如果没有指定 schema_name,默认为当前数据库 。
锁
介绍
锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
MySQL中的锁,按照锁的粒度分,分为以下三类:
全局锁:锁定数据库中的所有表。
表级锁:每次操作锁住整张表。
行级锁:每次操作锁住对应的行数据。
全局锁
介绍
全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。
其典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。
对数据库进行进行逻辑备份之前,先对整个数据库加上全局锁,一旦加了全局锁之后,其他的DDL、DML全部都处于阻塞状态,但是可以执行DQL语句,也就是处于只读状态,而数据备份就是查询操作。那么数据在进行逻辑备份的过程中,数据库中的数据就是不会发生变化的,这样就保证了数据的一致性和完整性。
语法
加全局锁
flush tables with read lock ;
数据备份(这个语句要在shell中执行 不能在mysql中执行)
mysqldump -uroot --p1234 itcast > itcast.sql
释放锁
unlock tables ;
特点
数据库中加全局锁,是一个比较重的操作,存在以下问题:
如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆。
如果在从库上备份,(如果是主从架构)那么在备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟。
在InnoDB引擎中,我们可以在备份时加上参数 --single-transaction 参数来完成不加锁的一致
性数据备份。
mysqldump --single-transaction -uroot --p123456 itcast > itcast.sql
表级锁
介绍
表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中。
对于表级锁,主要分为以下三类:
表锁
元数据锁(meta data lock,MDL)
意向锁
表锁
对于表锁,分为两类:
表共享读锁(read lock)
表独占写锁(write lock)
语法:
加锁:lock tables 表名... read/write。
释放锁:unlock tables / 客户端断开连接 。
对指定表加了读锁,不会影响右侧客户端二的读,但是会阻塞右侧客户端的写。
对指定表加了写锁,会阻塞右侧客户端的读和写。
元数据锁(MDL)
meta data lock , 元数据锁,简写MDL。
MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。为了避免DML与DDL冲突,保证读写的正确性
这里的元数据,可以简单理解为就是一张表的表结构。 也就是说,某一张表涉及到未提交的事务时,是不能够修改这张表的表结构的。
在MySQL5.5中引入了MDL,当对一张表进行增删改查的时候,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)。

在操作过程中可以执行下面的SQL语句,查看元数据锁的加锁情况
select object_type,object_schema,object_name,lock_type,lock_duration from performance_schema.metadata_locks ;
意向锁
为了避免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,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;
行级锁
介绍
行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。
InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类:
行锁(Record Lock):锁定单个行记录的锁,防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持。

间隙锁(Gap Lock):锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持。

临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。

行锁
InnoDB实现了以下两种类型的行锁:
共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁。
排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。


默认情况下,InnoDB在 REPEATABLE READ事务隔离级别运行,InnoDB使用 next-key 锁进行搜索和索引扫描,以防止幻读。
针对唯一索引进行检索时,对已存在的记录进行等值匹配时,将会自动优化为行锁。
InnoDB的行锁是针对于索引加的锁,不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁。
可以通过以下SQL,查看意向锁及行锁的加锁情况:
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;
普通的select语句,执行时,不会加锁。
select...lock in share mode,加共享锁,共享锁与共享锁之间兼容。共享锁与排他锁之间互斥。排它锁与排他锁之间互斥
无索引行锁升级为表锁:当一个事务对表中的大量行加锁时,系统为了提高效率,可能会将多个行锁合并为一个表锁。
如果根据没有索引的字段加行锁,因为行锁是对索引项加的锁,而字段没有索引就会导致行锁升级为表锁。根据索引字段进行更新操作,就可以避免行锁升级为表锁的情况。
间隙锁/邻键锁
默认情况下,InnoDB在 REPEATABLE READ事务隔离级别运行,InnoDB使用 next-key 锁进行搜索和索引扫描,以防止幻读。next-key锁是左开右闭的。
索引上的等值查询(唯一索引),给不存在的记录加锁时, 优化为间隙锁 。
索引上的等值查询(非唯一普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock 退化为间隙锁。
索引上的范围查询(唯一索引)--会访问到不满足条件的第一个值为止。
注意:间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。
InnoDB引擎
逻辑存储引擎
InnoDB的逻辑存储结构如下图所示:

表空间(ibd文件),表空间是InnoDB存储引擎逻辑结构的最高层, 如果用户启用了参数 innodb_file_per_table(在8.0版本中默认开启) ,则每张表都会有一个表空间(xxx.ibd)一个mysql实例可以对应多个表空间,用于存储记录、索引等数据
段:分为数据段,索引段,回滚段,InnoDB是索引组织表,数据段是B+树的叶子节点,索引段即为B+树的非叶子节点。段用来管理多个区
区:表空间的单元结构,每个区大小为1M。默认情况下,InnoDB存储引擎页大小为16K,即一个区中一共有64个连续的页。
页:是InnoDB存储引擎磁盘管理的最小单元,每个页的大小默认为16KB。为确保页的连续性,InnoDB引擎每次从磁盘申请4-5个区。
行:InnoDB存储引擎数据是按照行进行存放的。
架构
概述
MySQL5.5 版本开始,默认使用InnoDB存储引擎,它擅长事务处理,具有崩溃恢复特性,在日常开发中使用非常广泛。下面是InnoDB架构图,左侧为内存结构,右侧为磁盘结构。

内存结构
主要是Buffer Pool、Change Buffer、Adaptive Hash Index、Log Buffer
BufferPool:缓冲池是主内存中的一个区域,里面可以缓存磁盘上经常操作的真实数据,在执行增删改查操作时,先操作缓冲池中的数据(若缓冲池没有数据,则从磁盘加载并缓存),然后再以一定频率刷新到磁盘,从而减少磁盘IO,加快处理速度。
缓冲池以Page页为单位,底层采用链表数据结构管理Page。根据状态,将Page分为三种类型:
free page:空闲page,未被使用。
clean page:被使用page,数据没有被修改过。
dirty page:脏页,被使用page,数据被修改过,其中数据与磁盘的数据产生了不一致。
Change Buffer:更改缓冲区(针对于非唯一二级索引页),在执行DML语句时,如果这些数据Page没有在Buffer Pool中,不会直接操作磁盘,而会将数据变更存在更改缓冲区Change Buffer中,在未来数据被读取时,再将数据合并恢复到BufferPool中,再将合并后的数据刷新到磁盘中。
ChangeBuffer的意义是什么?
与聚集索引不同,二级索引通常是非唯一的,并且以相对随机的顺序插入二级索引。同样,删除和更新可能会影响索引树中不相邻的二级索引页,如果每一次都操作磁盘,会造成大量的磁盘IO。有了ChangeBuffer之后,我们可以在缓冲池中进行合并处理,减少磁盘IO。
AdaptiveHash Index:自适应hash索引,用于优化对Buffer Pool数据的查询。InnoDB存储引擎会监控对表上各索引页的查询,如果观察到hash索引可以提升速度,则建立hash索引,称之为自适应hash索引。
自适应哈希索引,无需人工干预,是系统根据情况自动完成。
参数:adaptive_hash_index
Log Buffer:日志缓冲区,用来保存要写入到磁盘中的log日志数据(redqlog、undo log),默认大小为16MB,日志缓冲区的日志会定期刷新到磁盘中。如果需要更新、插入或删除许多行的事务,增加日志缓冲区的大小可以节省磁盘/0。
参数:
innodb_log_buffer_size:缓冲区大小
innodb_flush_log_at_trx_commit:日志刷新到磁盘时机
1:日志在每次事务提交时写入并刷新到磁盘
0:每秒将日志写入并刷新到磁盘一次。
2:日志在每次事务提交后写入,并每秒刷新到磁盘一次。
磁盘结构

System Tablespace
系统表空间是更改缓冲区的存储区域。如果表是在系统表空间而不是每个表文件或通用表空间中创建的,它也可能包含表和索引数据。(在MySQL5.x版本中还包含InnoDB数据字典、undolog等)
参数:innodb_data_file_path(表空间文件路径)
File-Per-Table Tablespaces
如果开启了innodb_file_per_table开关 ,则每个表的文件表空间包含单个InnoDB表的数据和索引 ,并存储在文件系统上的单个数据文件中。
开关参数:innodb_file_per_table ,该参数默认开启。
General Tablespaces
通用表空间,需要通过 CREATE TABLESPACE 语法创建通用表空间,在创建表时,可以指定该表空
间。
创建表空间
CREATE TABLESPACE ts_name ADD DATAFILE 'file_name' ENGINE = engine_name;
创建表时指定表空间
CREATE TABLE xxx ... TABLESPACE ts_name;
Undo Tablespaces
撤销日志表空间,MySQL实例在初始化时会自动创建两个默认的undolog表空间(undo001、undo002)(初始大小16M),用于存储undo log日志。
Temporary Tablespaces
InnoDB 使用会话临时表空间和全局临时表空间。存储用户创建的临时表等数据。
Doublewrite Buffer Files
双写缓冲区,innoDB引擎将数据页从Buffer Pool刷新到磁盘前,先将数据页写入双写缓冲区文件中,便于系统异常时恢复数据。

Redo Log
重做日志,是用来实现事务的持久性。该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都会存到该日志中, 用于在刷新脏页到磁盘时,发生错误时, 进行数据恢复使用。
以循环方式写入重做日志文件,涉及两个文件:

后台线程

后台线程的目的主要是讲缓冲池中的数据在合适的实际刷新到磁盘。
在InnoDB的后台线程中,分为4类,分别是:Master Thread 、IO Thread、Purge Thread、Page Cleaner Thread。
Master Thread
核心后台线程,负责调度其他线程,还负责将缓冲池中的数据异步刷新到磁盘中, 保持数据的一致性,还包括脏页的刷新、合并插入缓存、undo页的回收
IO Thread
在InnoDB存储引擎中大量使用了AIO来处理IO请求, 这样可以极大地提高数据库的性能,而IO Thread主要负责这些IO请求的回调。

我们可以通过以下的这条指令,查看到InnoDB的状态信息,其中就包含IO Thread信息。
show engine innodb status \G;
Purge Thread
主要用于回收事务已经提交了的undo log,在事务提交之后,undo log可能不用了,就用它来回收。
Page Cleaner Thread
协助 Master Thread 刷新脏页到磁盘的线程,它可以减轻 Master Thread 的工作压力,减少阻塞。
事务原理
概述
事务是一组操作的集合,是一个不分割的工作单位,事务会把索引操作作为一个整体一起向系统提交或撤销请求操作,即这些操作要么同时成功要么同时失败。
特性:
原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
而对于这四大特性,实际上分为两个部分。 其中的原子性、一致性、持久性,实际上是由InnoDB中的两份日志来保证的,一份是redo log日志,一份是undo log日志。 而隔离性是通过数据库的锁,加上MVCC来保证的。
redo log
重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。
该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log file),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都存到该日志文件中, 用于在刷新脏页到磁盘,发生错误时, 进行数据恢复使用。
先刷入redolog 在合适的时机再批处理脏页,不仅能提高用户的响应效率,因为redolog是顺序io;而且可以保证数据的持久性,因为redolog可以协助数据的恢复;且批处理脏页可以加快随机io的寻址。
undo log
回滚日志,用于记录数据被修改前的信息 , 作用包含两个 : 提供回滚(保证事务的原子性) 和
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 log segment。
MVCC
概念
当前读
读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如: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。
隐藏字段

undo log
回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志。
当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除。
而update、delete的时候,产生的undo log日志不仅在回滚时需要,在快照读时也需要,不会立即被删除。
版本链
不同事务或相同事务对同一条记录进行修改,会导致该记录的undolog生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。
readview(读视图)
ReadView(读视图)是 快照读 SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
ReadView中包含了四个核心字段:

trx_id 代表当前undolog版本链对应事务ID。
而在readview中就规定了版本链数据的访问规则:

不同的隔离级别,生成ReadView的时机不同:
READ COMMITTED :在事务中每一次执行快照读时生成ReadView。
REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView
MySQL管理
系统数据库
Mysql数据库安装完成后,自带了一下四个数据库,具体作用如下:

常用工具
mysql(客户端)
语法 :
mysql [options] [database]
选项 :
-u, --user=name #指定用户名
-p, --password[=name] #指定密码
-h, --host=name #指定服务器IP或域名
-P, --port=port #指定连接端口
-e, --execute=name #执行SQL语句并退出
-e选项可以在Mysql客户端执行SQL语句,而不用连接到MySQL数据库再执行,对于一些批处理脚本,这种方式尤其方便。
示例:
mysql -u root --p 123456 db01 -e "select * from stu";
mysqladmin
mysqladmin 是一个执行管理操作的客户端程序。可以用它来检查服务器的配置和当前状态、创建并删除数据库等。
通过帮助文档查看选项:
mysqladmin --help
语法:
mysqladmin [options] command ...
选项:
-u, --user=name #指定用户名
-p, --password[=name] #指定密码
-h, --host=name #指定服务器IP或域名
-P, --port=port #指定连接端口
示例:
mysqladmin -uroot --p1234 drop 'test01';
mysqladmin -uroot --p1234 version;
mysqlbinlog
由于服务器生成的二进制日志文件以二进制格式保存,所以如果想要检查这些文本的文本格式,就会使用到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 客户端对象查找工具,用来很快地查找存在哪些数据库、数据库中的表、表中的列或者索引。
语法 :
mysqlshow [options] [db_name [table_name [col_name]]]
选项 :
--count 显示数据库及表的统计信息(数据库,表 均可以不指定)
-i 显示指定数据库或者指定表的状态信息
示例:
#查询test库中每个表中的字段书,及行数
mysqlshow -uroot -p2143 test --count
#查询test库中book表的详细情况
mysqlshow -uroot -p2143 test book --count
mysqldump
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/source
mysqlimport
mysqlimport 是客户端数据导入工具,用来导入mysqldump 加 -T 参数后导出的文本文件
语法 :
mysqlimport [options] db_name textfile1 [textfile2...]
示例 :
mysqlimport -uroot -p2143 test /tmp/city.txt
source
如果需要导入sql文件,可以使用mysql中的source 指令 :
语法 :
source /root/xxxxx.sql