存储引擎

MySQL体系结构

  • 连接层
    最上层是一些客户端和链接服务,主要完成一些类似于连接处理,授权认证及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
  • 服务层
    第二层架构,主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如过程、函数等。
  • 引擎层
    存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。
  • 存储层
    主要是将数据存储在文件系统之上,并完成与存储引擎的交互。

存储引擎

存储引擎就是存储数据、建立索引、更新、查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的,所以存储引擎也可被成为表类型。

  • InnoDB
    InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,在MySQL5.5之后,InnoDB是默认的MySQL存储引擎。
    特点:
    DML操作遵循ACID模型,支持事务;
    行级锁,提高并发访问性能;
    支持外键约束,保证数据的完整性和正确性。
    文件:
    xxx.ibd:xxx代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文件,存储该表的表结构(frm、sdi),数据和索引。
    参数:innodb_file_per_table
    逻辑存储结构:
  • mySQL数据库高级语法 mysql高级知识_数据库

  • MyISAM
    MyISAM是MySQL早期的默认存储引擎。不支持事务,不支持外键。支持表锁,不支持行级锁。访问速度快。
    文件:
    xxx.sdi:存储表结构信息
    xxx.MYD:存储数据
    xxx.MYI:存储索引
  • Memory
    Memory引擎的表数据存储在内存中,由于受到硬件问题,或断电问题的影响,只能将这些表作为临时表或缓存使用。特点是内存存放和hash索引(默认)
    文件:
    xxx.sdi:存储表结构信息

在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。

InnoDB是MySQL的默认存储引擎,支持事务、外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操作,那么InnoDB是比较合适的选择。


索引

索引概述

索引是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。

优势

劣势

提高数据检索的效率,降低数据库的IO成本

索引列也是要占用空间的

通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗

索引大大提高了查询效率,同时也降低更新表的速度,如对表进行INSERT、UPDATE、DELETE时,效率降低。

索引结构

MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含以下几种:

索引结构

描述

B+TREE索引

最常见的索引类型,大部分引擎都支持B+树索引

Hash索引

底层数据结构是用哈希表来实现的,只有精确匹配索引列的查询才有效,不支持范围查询

R-tree(空间索引)

空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少

Full-text(全文索引)

是一种通过建立倒排索引,快速匹配文档的方式。类似于Lucene,Solr,ES

我们平常所说的索引,如果没有特别指明,都是指B+树结构组织的索引。

  • B-Tree(多路平衡查找树)
    以一颗最大度数(max-degree)为5的b-tree为例(每个节点最多存储4个key,5个指针):
  • B+Tree、

    以一颗最大度数(max-degree)为4的b+tree为例:

  • mySQL数据库高级语法 mysql高级知识_mysql_02


相对于B-Tree区别: 1、所有的数据都会出现在叶子节点 2、叶子节点形成一个单向链表

MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能。

mySQL数据库高级语法 mysql高级知识_主键_03

索引分类

分类

含义

特点

关键字

主键索引

针对于表中主键创建的索引

默认自动创建,只能有一个

PRIMARY

唯一索引

避免同一个表中某数据列中的值重复

可以有多个

UNIQUE

常规索引

快速定位特定数据

可以有多个

全文索引

全文索引查找的是文本中的关键词,而不是比较索引中的值

可以有多个

FULLTEXT


在innoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:

分类

含义

特点

聚集索引

将数据与索引放到了一块,索引结构的叶子节点保存了行数据

必须有,而且只有一个

二级索引

将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键

可以存在多个

聚集索引选取规则: 1、如果存在主键,主键索引为聚集索引 2、如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引。 3、如果表没有主键,或没有合适的唯一索引,则innoDB会自动生成一个rowid作为隐藏的聚集索引。

索引语法

  • 创建索引

  • CREATE [UNIQUE|FULLTEXT] INDEX index_name ON table_name (index_col_name,...)

  • 查看索引

  • SHOW INDEX FROM table_name;

  • 删除索引

  • DROP INDEX index_name ON table_name;

SQL性能分析

  • SQL执行频率 MySQL客户端连接成功后,通过show [session | global] status命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次。

  • SHOW GLOBAL STATUS LIKE 'Com_______'

  • 慢查询日志 慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志。 MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:

  • #开启MySQL慢日志查询开关 slow_query_log=1 #设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志 long_query_time=2

  • 配置完成之后,重新启动MySQL,查看慢查询日志文件中记录的信息(/var/lib/localhost-slow.log)。

  • profile详情 show profiles 能够在做SQL优化时帮助我们了解时间都耗费去了哪里。通过hava_profiling参数,能够看到当前MySQL是否支持profile操作:

  • SELECT @@have_profiling;

  • 默认profiling是关闭的,可以通过set语句在session或者global级别开启profiling:

  • SET profiling = 1;

  • 执行一系列的业务SQL操作,然后通过如下指令查看指令的执行耗时

  • #查看每一条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语句执行过程中表如何连接和连接的顺序。

    语法:

  • #直接在select语句之前加上关键字explain / desc EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件;

  • EXPLAIN执行计划各字段含义:

    • Id select查询的序列号,表示查询中执行select字句或者是操作表的顺序(id相同,执行顺序从上到下;id不同,值越大,越先执行)。

    • select_type

      表示SELECT的类型,常见的取值有SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION中的第二个或者后面的查询语句)、SUBQUERY(SELECT/WHERE之后包含了子查询)等。

    • type

      表示连接类型,性能由好到差的连接类型为NULL、system、const、eq_ref、ref、range、index、all。

    • possible_key

      显示可能应用在这张表上的索引,一个或多个。

    • Key

      实际使用的索引,如果为NULL,则没有使用索引。

    • Key_len

      表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好。

    • rows

      MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估计值,可能并不总是准确的。

    • filtered

      表示返回结果的行数占需读取行数的百分比,filtered的值越大越好。

索引使用规则

  • 最左前缀法则 如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃了某一列,索引将部分失效(后面的字段索引失效)。跟位置无关,但最左边的列是必须要存在的,不然完全失效。

  • 范围查询

    联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效。

  • 索引列运算

    不要在索引列上进行运算,不然索引将失效。

  • 字符串不加引号

    字符串类型字段使用时,不加引号,索引将失效。

  • 模糊查询

    如果仅仅是尾部进行模糊匹配,索引不会失效。如果是头部进行模糊匹配,索引将会失效。

  • or连接的条件

    用or分割开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。

  • 数据分布影响

    如果MySQL评估使用索引比全表扫描更慢,则不使用索引。

  • SQL提示

    SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些认为的提示来达到优化操作的目的。

    use index:

  • #给SQL优化器一个提示,至于最后的选择由MySQL自己决定 select * from tb_user use index(idx_user_pro) where profession = ""

  • ignore index:

  • #强制取消使用这个索引 select * from tb_user ignore index(idx_user_pro) where profession = ""

  • force index:

  • #强制使用这个索引 select * from tb_user force index(idx_user_pro) where profession = ""

  • 覆盖索引 尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到),减少select *。

  • 前缀索引 当字段类型为字符串(varchar,text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀建立索引,这样可以大大节约索引空间,从而提高索引效率。

    语法:create index idx_xxx on table_name(column(n));

    前缀长度:

    可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的选择性,性能也是最好的。

  • #求出最好选择性 select count(distinct xxx) / count(*) from table_name; #比较最好的选择性 select count(distinct substring(xxx,1,5)) / count(*) from table_name;

  • 单列索引与联合索引

    单列索引:即一个索引只包含单个列。

    联合索引:即一个索引包含了多个列。

    在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引。

索引设计原则

1、针对于数据量较大,且查询比较频繁的表建立索引。 2、针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。 3、尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。 4、如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。 5、尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表查询,提高查询效率。 6、要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。 7、如果索引列不能存储NULL值,请在创建表的时候就是用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。


SQL优化

插入数据

  • insert优化

    1、批量插入 insert into tb_test values (1),(2),(3);

    2、手动提交事务(MySQL的自动提交事务会一句一提交)

    start transaction; insert into tb_test values (1),(2),(3); insert into tb_test values (4),(5),(6); commit;

    3、主键顺序插入 主键乱序插入:8、1、9、21、88、2、4、15、89、5、7、3;

    主键顺序插入:1、2、3、4、5、6、7、8、9、15

  • 大批量插入数据

    如果一次性需要插入大批量数据,使用insert语句插入性能较低,此时可以使用MySQL提供的load指令进行插入。操作如下:

  • #客户端连接服务端时,加上参数 --local-infile mysql --local-infile -u root -p #设置全局参数local_infile为1,开启从本地加载文件导入数据的开关 set global local_infile = 1; #执行load指令将准备好的数据,加载到表结构中 load data local infile '/root/sql.log' into table 'tb_user' fields terminated by ',' lines terminated by '\n';

  • 注意:主键顺序插入性能高于乱序插入。

主键优化

  • 数据组织方式

    在innoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表。

  • 页分裂 页可以为空,也可以填充一般,也可以填充100%。每个页包含了2-N行数据(如果一行数据过大,会行溢出),根据主键排列。 如果主键是乱序插入的话,就有可能出现页分裂现象:

  • mySQL数据库高级语法 mysql高级知识_主键_04


  • 页合并 当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记为删除并且它的空间变得允许被其他记录声明使用。 当页中删除的记录达到MERGE_THRESHOLD(合并页的阈值,默认为页的50%,可以自己设置,在创建表或者创建索引时指定),InnoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用。

  • mySQL数据库高级语法 mysql高级知识_mySQL数据库高级语法_05


  • 主键设置 1、满足业务需求的情况下,尽量降低主键的长度。 2、插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键。 3、尽量不要使用UUID做主键或者是其他自然主键,如身份证号 4、业务操作时,避免对主键的修改。

order by优化

1、Using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫FileSort排序。 2、Using index:通过有序索引顺序扫描直接返回有序数据,这种情况即为using index,不需要额外排序,操作效率高。

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

group by优化

1、在分组操作时,可以通过索引来提高效率。 2、分组操作时,索引的使用也是满足最左前缀法则的。

limit优化

常见而又非常耗时的问题就是limit 2000000,10,此时需要MySQL排序前2000010条记录,仅仅返回2000000-2000010的记录,其他记录丢弃,查询排序的代价非常大。 优化思路:一般查询分页时,通过创建覆盖索引能够比较好地提高性能,可以通过覆盖索引加子查询形式进行优化。

count优化

count(主键) InnoDB引擎会遍历整张表,把每一行的主键id值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加。

count(字段) 没有not null 约束:InnoDB引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加。

count(1) InnoDB引擎会遍历整张表,但不取值。服务层对于返回的每一行,放一个数字"1"进去,直接按行进行累加。

count(*) InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加。

按照效率排序: count(字段)<count(主键id)<count(1)约等于count(*),所以尽量使用count(※)

update优化

InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁。


视图

视图是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。

创建语法:


create [OR REPLACE] VIEW 视图名称[(列名列表)] AS SELECT语句 [WITH [CASCADED | LOCAL] CHECK OPTION]


查询语法:


#查看创建视图语句 SHOW CREATE VIEW 视图名称; #查看视图数据 SELECT * FROM 视图名称 ......;


修改语法:


#方式一 create [OR REPLACE] VIEW 视图名称[(列名列表)] AS SELECT语句 [WITH [CASCADED | LOCAL] CHECK OPTION] #方式二 ALTER VIEW 视图名称[(列名列表)] AS SELECT语句 [WITH[CASCADED | LOCAL] CHECK OPTION]


删除语法:


DROP VIEW [IF EXISTS] 视图名称 [,视图名称]


视图的检查选项: 当使用WITH CHECK OPTION字句创建视图时,MySQL会通过视图检查正在更改的每个行,例如插入,更新,删除,以使其符合视图的定义。MySQL允许基于另一个视图创建视图,它还会检查依赖视图中的规则以保持一致性。为了确定检查的范围,MySQL提供了两个选项: CASCADED和LOCAL,默认值为CASCADED。

视图的更新: 要使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系。如果试图包含以下任何一项,则该视图不可更新: 1、聚合函数或窗口函数(sum/min/max/count等) 2、DISTINCT/GROUP BY/HAVING/UNION或UNION ALL

视图的作用: 1、简单:视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件。 2、安全:数据库可以授权,但不能授权到数据库特定行和列上。通过视图用户只能查询和修改他们所能见到的数据。 3、数据独立:视图可以帮助用户屏蔽真实表结构变化带来的影响。


存储过程

存储过程是事先经过编译并存储在数据库中的一段SQL语句的集合,调用存储过程可以简化开发人员的很多工作,减少数据在数据库和应用服务器之间的网络传输,可以提高数据处理的效率。

基本语法

  • 创建

  • CREATE PROCEDURE 存储过程名称([参数列表]) BEGIN --SQL语句 END;

  • 调用

  • CALL 名称 ([参数]);

变量

  • 系统变量

系统变量是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中配置

  • 用户自定义变量

    用户定义变量是用户根据需要自己定义的变量,用户变量不用提前声明,在用的时候直接用"@变量名"使用就可以。其作用域为当前连接。 赋值:

  • SET @var_name = expr [, @var_name := expr] ...; SELECT @var_name := expr [,@var_name := expr]...; SELECT 字段名 INTO @var_name FROM 表名;

  • 使用:

  • SELECT @var_name;

  • 局部变量 局部变量是根据需要定义的在局部生效的变量,访问之前,需要DECLARE声明。可用作存储过程内的局部变量和输入参数,局部变量的范围是在其内声明的BEGIN...END块。 声明:

  • DECLARE 变量名 变量类型 [DEFAULT ...];

  • 变量类型就是数据库字段类型:INT、BIGINT、CHAR、VARCHAR、DATE、TIME等。

    赋值:

  • SET 变量名 := 值; SELECT 字段名 INTO 变量名 FROM 表名 ...;

IF语法


IF 条件1 THEN ... ELSEIF 条件2 THEN ... ELSE ... END IF;


参数

类型

含义

IN

该类参数作为输入,也就是需要调用时传入值

OUT

该类参数作为输出,也就是该参数可以作为返回值

INOUT

既可以作为输入参数,也可以作为输出参数

用法:


CREATE PROCEDURE 存储过程名称([IN/OUT/INOUT 参数名 参数类型]) BEGIN --SQL语句 END;


CASE语法


#语法一 CASE case_value WHEN when_value1 THEN statement_list1 [WHEN when_value2 THEN statement_list2]... [ELSE statement_list] END CASE; #语法二 CASE WHEN search_condition1 THEN statement_list1 [WHEN search_condition2 THEN statement_list2]... [ELSE statement_list] END CASE;


循环语法

  • while

  • #先判定条件,如果条件为true,则执行逻辑,否则不执行逻辑 WHILE 条件 DO SQL逻辑... END WHILE;

  • repeat

  • #先执行一次逻辑,然后判定逻辑是否满足,如果满足,则退出。如果不满足,则继续下一次循环 REPEAT SQL逻辑... UNTIL 条件 END REPEAT;

  • loop

    LOOP实现简单的循环,如果不在SQL逻辑中增加退出循环的条件,可以用其来实现简单的死循环。LOOP可以配合以下两个语句使用:

    • LEAVE:配合循环使用,退出循环。

    • ITERATE:必须用在循环中,作用是跳过当前循环剩下的语句,直接进入下一次循环

    [begin_label:] LOOP SQL逻辑... END LOOP [end_label]; LEAVE label; #退出指定标记的循环体 ITERATE label; #直接进入下一次循环

游标

游标是用来存储查询结果集的数据类型,在存储过程和函数中可以使用游标对结果集进行循环的处理。游标的使用包括游标的声明、open、fetch、close。

语法:


#声明游标(变量需要声明在游标之前!) DECLEAR 游标名称 CURSOR FOR 查询语句; #打开游标 OPEN 游标名称; #获取游标记录 FETCH 游标名称 INTO 变量[,变量]; #关闭游标 CLOSE 游标名称;


条件处理程序

条件处理程序可以用来定义在流程控制结构执行过程中遇到问题时相应的处理步骤。其语法结构为:


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:所有没有被SQLWARNNING或NOT FOUND捕获的SQLSTATE代码的简写



存储函数

存储函数是有返回值的存储过程,存储函数的参数只能是IN类型的。语法如下:


CREATE FUNCTION 存储函数名称([参数列表]) RETURNS type [characteristic ...] BEGIN --SQL语句 RETURN ...; END; #characteristic说明: DETERMINISTIC:相同的输入参数总是产生相同的效果。 NO SQL:不包含SQL语句。 READS SQL DATA:包含读取数据的语句,但不包含写入数据的语句。



触发器

触发器是与表有关的数据库对象,旨在insert/update/delete之前或之后,触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用再数据库端确保数据的完整性,日志记录,数据校验等操作。

使用别名OLD和NEW来引用触发器中发生变化的记录内容,这与其它数据库是相似的。现在触发器还只支持行级触发,不支持语句级触发。

触发器类型

NEW和OLD

INSERT

NEW表示将要或已经新增的数据

UPDATE

OLD表示修改之前的数据,NEW表示将要或已经修改后的数据

DELETE

OLD表示将要或者已经删除的数据

语法:

  • 创建

  • 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)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。

分类

按照锁的粒度分,分为以下三类: 1、全局锁:锁定数据库中的所有表。 2、表级锁:每次操作锁住整张表。 3、行级锁:每次操作锁住对应的行数据。

全局锁

全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。其典型的使用场景是做全库的逻辑备份,对所有表进行锁定,从而获取一致性视图,保证数据的完整性。

语法:


#加锁 flush tables with read lock; #对数据库进行备份 mysqldump -u root -p 1234 itcast > itcase.sql #解锁 unlock tables;


数据库中加全局锁是一个比较重的操作,存在以下问题: 1、如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆。 2、如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志,会导致主从延迟。

表级锁

表级锁,每次操作锁住整张表,锁定粒度大,发生锁冲突的概率最高,并发度最低。对于表级锁,主要分为以下三类: 1、表锁 2、元数据锁 3、意向锁

  • 表锁

    对于表锁,分为两类:表共享读锁/表独占写锁 语法: 1、加锁:lock tables 表名... read/write。 2、释放锁:unlock tables / 客户端断开连接。 读锁不会阻塞其他客户端的读,但是会阻塞写。写锁既会阻塞其他客户端的读,也会阻塞其它客户端的写。

  • 元数据锁

    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;

  • 意向锁

    为了避免DML在执行时,加的行锁与表锁的冲突,在innoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。

    1、意向共享锁(IS):与表锁共享锁(read)兼容,与表锁排它锁(write)互斥。 2、意向排他锁(IX):与表锁共享锁和排它锁都互斥,意向锁之间不互斥。

    可通过以下SQL,查看意向锁及行锁的加锁情况:

  • select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;

行级锁

行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。 InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分三类: 1、行锁:锁定单个行记录的锁,防止其他事务对此进行update和delete。在RC、RR隔离级别下都支持。 2、间隙锁:锁定索引记录间隙,不含该记录,确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持。 3、临键锁:行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。

  • 行锁 1、共享锁(S):允许一个事务去读一行,阻止其它事务获得相同数据集的排它锁。 2、排它锁(X):允许获取排它锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排它锁。

    当前锁类型/请求锁类型

    S(共享锁)

    X(排他锁)

    S(共享锁)

    兼容

    冲突

    X(排它锁)

    冲突

    冲突

  • 间隙锁/临键锁

    默认情况下,InnoDB在REPEATABLE READ事务隔离级别运行,InnoDB使用next-key锁进行搜索和索引扫描,以防止幻读。

    1、索引上的等值查询(唯一索引),给不存在的记录加锁时,优化为间隙锁。 2、索引上的等值查询(普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock退化为间隙锁。

    3、索引上的范围查询(唯一索引),会访问到不满足条件的第一个值为止。

    注意:间隙锁唯一目的是防止其他事务插入间隙,间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。


InnoDB引擎

逻辑存储结构

  • 表空间

    一个MySQL实例可以对应多个表空间,用于存储记录、索引等数据。

  • 分为数据段、索引段、回滚段,InnoDB是索引组织表,数据段就是B+树的叶子节点,索引段即为B+树的非叶子节点。段用来管理多个区。

  • 区 表空间的单元结构,每个区的大小为1M。默认情况下,InnoDB存储引擎页大小为16K,即一个区中一共有64个连续的页。

  • InnoDB存储引擎磁盘管理的最小单元,每个页的大小默认为16KB。为了保证页的连续性,InnoDB存储引擎每次从磁盘申请4-5个区。

  • InnoDB存储引擎数据是按行进行存放的。 Trx_id:每次对某条记录进行改动时,都会把对应的事务id赋值给trx_id隐藏列。 Roll_pointer:每次对某条引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。

事务原理

事务是一组操作的集合,是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。

  • redo log

    重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。该日志文件由两部分组成:重做日志缓冲以及重做日志文件,前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都存到该日志文件中,用于刷新脏页到磁盘,发生错误时进行数据恢复使用。

  • mySQL数据库高级语法 mysql高级知识_MySQL_06


  • 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称为多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为MySQL实现MVCC提供了一个非阻塞读功能。MVCC的具体实现,还需要依赖于数据库记录中的三个隐式字段、undo log日志、readView。

  • 记录中的隐藏字段

    隐藏字段

    含义

    DB_TRX_ID

    最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID

    DB_ROLL_PTR

    回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版本

    DB_ROW_ID

    隐藏主键,如果表结构没有指定主键,将会生成隐藏字段。

  • readView

    ReadView(读视图)是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务id。 ReadView中还包含了四个核心字段:

    字段

    含义

    m_ids

    当前活跃的事务ID集合

    min_trx_id

    最小活跃事务ID

    max_trx_id

    预分配事务ID,当前最大事务ID+1(因为事务ID为自增)

    creator_trx_id

    ReadView创建者的事务ID

    mySQL数据库高级语法 mysql高级知识_MySQL_07



系统数据库

MySQL数据库安装完成之后,自带了以下四个数据库:

数据库

含义

MySQL

存储MySQL服务器正常运行所需要的各种信息(时区,主从、用户、权限等)

information_schema

提供了访问数据库元数据的各种表和视图,包含数据库、表、字段类型及访问权限等。

performance_schema

为MySQL服务器运行时状态提供了一个底层监控功能,主要用于收集数据库服务器性能参数

sys

包含了一系列方便DBA和开发人员利用performance_schema性能数据库进行性能调优和诊断的视图


日志

错误日志

错误日志时MySQL中最重要的日志之一,它记录了当mysqld启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时,建议首先查看此日志。 该日志默认开启,默认存放目录为/var/log/,默认的日志文件名为mysql.log。 查看日志位置:show variables like '%log_error%'。

二进制日志

二进制日志(BINLOG)记录了所有DDL语句和DML语句,但不包括数据查询语句。 作用: 1、灾难时的数据恢复。 2、MySQL的主从复制。在MySQL8版本中,默认二进制日志时开启着的,涉及到的参数如下: show variables like '%log_bin%'

日志格式:

日志格式

含义

STATEMENT

基于SQL语句的日志记录,记录的是SQL语句,对数据进行修改的SQL都会记录在日志文件中。

ROW

基于行的日志记录,记录的是每一行的数据变更。(默认)

MIXED

混合了STATEMENT和ROW的两种格式,默认采用STATEMENT,在某些特殊情况下会自动切换为ROW进行记录。

日志查看: 由于日志是以二进制方式存储的,不能直接读取,需要通过二进制日志查询工具mysqlbinlog来查看,具体语法:


mysqlbinlog [参数选项] logfilename 参数选项: -d 指定数据库的名称,只列出指定的数据库相关操作。 -o 忽略掉日志中的前n行命令。 -v 将行事件(数据变更)重构为SQL语句。 -w 将行事件(数据变更)重构为SQL语句,并输出注释信息。


日志删除: 对于比较繁忙的业务系统,每天生成的binlog数据巨大,如果长时间不清除,将会占用大量的磁盘空间。可以通过以下方式清理日志:

指令

含义

reset master

删除全部binlog日志,删除之后,日志编号,将从binlog.000001重新开始。

purge master logs to 'binlog.**'

删除该编号之前的所有日志

purge master logs before 'yyyy-mm-dd hh24:mi:ss'

删除日志为该时间之前的所有日志

查询日志

查询日志中记录了客户端的所有操作语句,而二进制日志不包含查询数据的SQL语句。默认情况下,查询日志是未开启的。如果需要开启查询日志,可以设置以下配置:

mySQL数据库高级语法 mysql高级知识_mysql_08

修改MySQL的配置文件/etc/my.cnf文件,添加如下内容:


#该选项用来开启查询日志,可选值:0:代表关闭,1:代表开启 general_log=1 #设置日志的文件名,如果没有指定,默认的文件名为host_name.log general_log_file=mysql_query.log


慢查询日志

慢查询日志记录了所有执行时间超过参数long_query_time设置值并且扫描记录数不小于min_examined_row_limit的所有的SQL语句的日志,默认未开启。long_query_time默认为10秒,最小为0,精度可以到微秒。


#慢查询日志 slow_query_log=1 #执行时间参数 long_query_time=2


默认情况下,不会记录管理语句,也不会记录不使用索引进行查找的查询语句。可以使用log_slow_admin_statements和更改此行为log_queries_not_using_indexes,如下所诉:


#记录执行较慢的管理语句 log_slow_admin_statements=1 #记录执行较慢的未使用索引的语句 log_queries_not_using_indexes=1



主从复制

主从复制是指将主数据库的DDL和DML操作通过二进制文件传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步。 MySQL支持一台主库同时向多台从库进行复制,从库同时也可以作为其他从服务器的主库,实现链状复制。 MySQL复制主要包含以下三个方面: 1、主库出现问题,可以快速切换到从库提供服务。 2、实现读写分离,降低主库的访问压力。 3、可以在从库中执行备份,以避免备份期间影响主库服务。

主从复制原理:

mySQL数据库高级语法 mysql高级知识_数据库_09

从上图看,复制分为三步: 1、Master主库在事务提交时,会把数据变更记录在二进制日志文件binlog中。 2、从库读取主库的二进制日志文件binlog,写入到从库的中继日志Relaylog。 3、slave重做中继日志中的事件,将改变反应它自己的数据。


分库分表

分库分表的中心思想都是将数据分散存储,使得单一数据库/表的数据量变小来缓解单一数据库的性能问题,从而达到提升数据库性能的目的。

垂直拆分

垂直分库:以表为依据,根据业务将不同表拆分到不同库中。 特点:每个库的表结构都不一样;每个库的数据也不一样;所有库的并集是全量数据。

mySQL数据库高级语法 mysql高级知识_mysql_10

垂直分表:以字段为依据,根据字段属性将不同字段拆分到不同表中。 特点:每个表的结构都不一样;每个表的数据也不一样,一般通过一列(主键/外键)关联;所有表的并集是全量数据。

mySQL数据库高级语法 mysql高级知识_MySQL_11

水平拆分

水平分库:以字段为依据,按照一定策略,将一个库的数据拆分到多个库中。 特点:每个库的表结构都一样;每个库的数据都不一样;所有库的并集是全量数据。

mySQL数据库高级语法 mysql高级知识_mySQL数据库高级语法_12

水平分表:以字段为依据,按照一定策略,将一个表的数据拆分到多个表中。 特点:每个表的表结构都一样;每个表的数据都不一样;所有表的并集是全量数据。

mySQL数据库高级语法 mysql高级知识_MySQL_13

实现技术

  • shardingJDBC:基于AOP原理,在应用程序中对本地执行的SQL进行拦截,解析,改写,路由处理。需要自行编码配置实现,只支持Java语言,性能较高。

  • MyCat:数据库分库分表中间件,不用调整代码即可实现分库分表,支持多种语言,性能不及前者。

MyCat

MyCat是开源的、活跃的、基于Java语言编写的MySQL数据库中间件。可以像使用MySQL一样来使用MyCat,对于开发人员来说根本感觉不到MyCat的存在。

目录结构 bin:存放可执行文件,用于启动停止mycat。 conf:存放mycat的配置文件。 lib:存放mycat的项目依赖包(jar)。 logs:存放mycat的日志文件。

概念图

mySQL数据库高级语法 mysql高级知识_数据库_14

分片规则

范围分片 根据指定的字段及其配置的范围与数据节点的对应情况,来决定该数据属于哪一个分片。

mySQL数据库高级语法 mysql高级知识_主键_15

取模分片 根据指定的字段值与节点数量进行求模运算,根据运算结果,来决定该数据属于哪一个分片。

mySQL数据库高级语法 mysql高级知识_主键_16

一致性hash分片 所谓一致性哈希,相同的哈希因子计算值总是被划分到相同的分区表中,不会因为分区节点的增加而改变原来数据的分区位置。

mySQL数据库高级语法 mysql高级知识_mysql_17

枚举分片 通过在配置文件中配置可能的枚举值,指定数据分布到不同数据节点上,本规则适用于按照省份,性别,状态拆分数据等业务。

mySQL数据库高级语法 mysql高级知识_mysql_18

应用指定分片 运行阶段由应用自主决定路由到哪个分片,直接根据字符字串(必须是数字)计算分片号。

mySQL数据库高级语法 mysql高级知识_数据库_19

固定分片hash算法分片 该算法类似于十进制的求模运算,但是为二进制的操作,例如,取id的二进制低10位与1111111111进行位&运算。

mySQL数据库高级语法 mysql高级知识_主键_20

字符串hash解析算法分片 截取字符串中的指定位置的子字符串,进行hash算法,算出分片。

mySQL数据库高级语法 mysql高级知识_mysql_21


读写分离

读写分离就是把对数据库的读和写操作分开,以对应不同的数据库服务器。主数据库提供写操作,从数据库提供读操作,这样能有效地减轻单台数据库的压力。 通过MyCat即可轻易实现上述功能,不仅可以支持MySQL,也可以支持Oracle等。

mySQL数据库高级语法 mysql高级知识_MySQL_22

一主一从

配置 MyCat控制后台数据库的读写分离和负载均衡由schema.xml文件datahost标签的balance属性控制。

mySQL数据库高级语法 mysql高级知识_主键_23

双主双从

一个主机Master1用于处理所有写请求,它的从机Slave1和另一台主机Master2还有它的从机Slave2负责所有读请求。当Master1主机宕机后,Master2主机负责写请求,Master1、Master2互为备机。

mySQL数据库高级语法 mysql高级知识_mysql_24