MySQL高级知识

MySQL高级知识


文章目录

  • MySQL高级知识
  • 前言
  • 一、索引
    • 1.概述
    • 2.索引优缺点
    • 3.索引的数据结构
    • 4.索引的分类
    • 5.索引的语法
    • 6.索引设计原则
    • 7.避免索引失效
  • 二、视图
    • 1.概述
    • 2.创建或者修改视图
    • 3.查看以及删除视图
  • 三、存储过程和函数
    • 1.存储过程和函数概述
    • 2.创建存储过程
    • 3.调用存储过程
    • 4.查看存储过程
    • 5.删除存储过程
    • 6.语法
  • 四、触发器
    • 1.概述
    • 2.创建触发器
    • 3.删除触发器
    • 4.查看触发器
  • 五、MySQL的体系结构概览
  • 六、存储引擎
    • 1.概述
    • 2.存储引擎的特性
    • 3.MEMORY
    • 4.MERGE
    • 5.存储引擎的选择
  • 七、优化SQL步骤
    • 1.查看SQL执行效率
    • 2.定位低效率执行SQL
    • 3.explain分析执行计划
    • 4.show profile分析SQL
    • 5.trace分析优化器执行计划
  • 八、SQL优化
    • 1.大批量插入数据
    • 2.优化insert语句
    • 3.优化order by语句
    • 4.优化group by语句
    • 5.优化嵌套查询
    • 6.优化or条件
    • 7.优化分页查询
    • 8.使用SQL提示
  • 九、应用优化
    • 1.使用连接池
    • 2.减少对MySQL的访问
    • 3.负载均衡
  • 十、MySQL中查询缓存优化
    • 1.概述
    • 2.操作流程
    • 3.查询缓存配置
    • 4.开启查询缓存
    • 5.查询缓存SELECT选项
    • 6.查询缓存失效的情况
  • 十一、MySQL内存管理及优化
    • 1.内存优化原则
    • 2.MyISAM内存优化
    • 3.InnoDB内存优化
  • 十二、MySQL并发参数调整
    • 1.max_connections
    • 2.back_log
    • 3.table_open_cache
    • 4.thread_cache_size
    • 5.innodb_lock_wait_timeout
  • 十三、MySQL锁问题
    • 1.锁概述
    • 2.锁分类
    • 3.MySQL锁
    • 4.MyISAM表锁
    • 5.InnoDB行锁
  • 十四、常用SQL技巧
    • 1.SQL编写顺序
    • 2.SQL执行顺序
    • 3.正则表达式
    • 4.常用函数
  • 十五、MySQL中常用工具
    • 1.mysql
    • 2.mysqladmin
    • 3.mysqlbinlog
    • 4.mysqldump
    • 5.mysqlimport / source
    • 6.mysqlshow
  • 十六、MySQL日志
    • 1.错误日志
    • 2.二进制日志
    • 3.查询日志
    • 4.慢查询日志
  • 十七、MySQL复制
    • 1.概述
    • 2.复制原理
    • 3.复制优势
    • 4.搭建步骤
  • 总结


前言


一、索引

1.概述

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

2.索引优缺点

优点:

  • 类似于书本的目录索引,提高数据检索的效率,降低数据库的IO成本;
  • 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗。

缺点:

  • 索引也是一张表,该表中保存了主键与索引字段,并指向实体类的记录,所以索引列也是要占用空间的;
  • 虽然索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行INSERT、UPDATE、DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。

3.索引的数据结构

索引是在MySQL的存储引擎层中实现的,而不是在服务器层实现的。所以每种存储引擎的索引都不一定完全相同,也不是所有的存储引擎都支持所有的索引类型的。MySQL目前提供了以下4种索引:

  1. B树索引:最常见的索引类型,大部分索引都支持B树索引;
  2. HASH索引:只有Memory引擎支持,使用场景简单;
  3. R树索引(空间索引):空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少,不做特别介绍;
  4. Full-text(全文索引):全文索引也是MyISAM的一个特殊索引类型,主要用于全文索引,InnoDB从MySQL5.6版本开始支持全文索引。

m叉B树的特性:

  • 树中每个节点最多包含m个孩子;
  • 除根节点与叶子节点外,每个节点至少有[ceil(m/2)]个孩子;
  • 若根节点不是叶子节点,则至少有两个孩子;
  • 所有的叶子节点都在同一层;
  • 每个非叶子节点由n个key与n+1个指针组成,其中[ceil(m/2)-1] <= n <=m-1。

B+树为B树的变种,B+树与B树的区别:

  • n叉B+树最多包含n个key,而B树最多含有n-1个key;
  • B+树的叶子节点保存所有的key信息,依key大小顺序排列;
  • 所有的非叶子节点都可以看作是key的索引部分。
    由于B+树只有叶子节点保存key信息,查询任何key都要从root走到叶子。所以B+树的查询效率更加稳定。
    MySQL高级知识_第1张图片

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

4.索引的分类

  • 单值索引: 即一个索引只包含单个列,一个表可以有多个单列索引;
  • 唯一索引: 索引列的值必须唯一,但允许有空值;
  • 复合索引: 即一个索引包含多个列5

5.索引的语法

创建索引

create index 索引名称 on 表名(字段名); 

查看索引

show index from city;

删除索引

drop index 索引名称 on 表名;

alter命令

1、alter table 表名 add primary key(column_list);
该语句添加一个主键,这意味着索引值必须是唯一的,且不能为null

2、alter table 表名 add unique 索引名称(字段名称);
这条语句创建索引的值必须是唯一的(除了null外,null可能会出现多次)

3、alter table 表名 add index 索引名称(字段名称);
添加普通索引,索引值可以出现多次

4、alter table 表名 add fulltext 索引名称(字段名称);
该语句指定了索引为FULLTEXT,用于全文索引

6.索引设计原则

  • 对查询频次较高,且数据量比较大的表建立索引;
  • 索引字段的选择,最佳候选列应当从where子句的条件中提取,如果where子句中的组合比较多,那么应当挑选最常用、过滤效果最好的列的组合;
  • 使用唯一索引,区分度越高,使用索引的效率越高;
  • 索引可以有效的提升查询数据的效率,但索引数量不是多多益善,索引越多,维护索引的代价自然也就水涨船高。对于插入、更新、删除等DML操作比较频繁的表来说,索引过多,会引入相当高的维护代价,降低DML操作的效率,增加相应操作的时间消耗。另外索引过多的话,MySQL也会犯选择困难病,虽然最终仍然会找到一个可用的索引,但无疑提高了选择的代价;
  • 使用短索引,索引创建之后也是使用硬盘来存储的,因此提升索引访问的I/O效率,也可以提升总体的访问效率。假如构成索引的字段总长度比较短,那么在给定大小的存储块内可以存储更多的索引值,相应的可以有效的提升MySQL访问索引的I/O效率;
  • 利用最左前缀,N个列组合而成的组合索引,那么相当于是创建了N个索引,如果查询时where子句中使用了组成该索引的前几个字段,那么这条查询SQL可以利用组合索引来提升查询效率。
创建复合索引:
CREATE INDEX idx_name_email_status ON tb_seller(NAME,email,STATUS);

就相当于对name创建索引,对name,email创建了索引,对name,email,status创建了索引。

7.避免索引失效

  1. 全值匹配,对索引中所有列都指定具体值。
    例如:创建name和password的复合索引;然后执行下面的语句,索引生效,执行效率高。
    select * from user where name='张三' and password='123456';
    
  2. 最左前缀法则;如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始,并且不跳过索引中的列;
  3. 范围查询右边的列,不能使用索引;
  4. 不要在索引列上进行运算操作,索引将失效;
  5. 字符串不加单引号,造成索引失效;
  6. 尽量使用覆盖索引,避免select *,尽量使用覆盖索引(只访问索引的查询,索引列完全包含查询列);
  7. 用or分隔开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到;
  8. 以%开头的like模糊查询,索引失效,如果仅仅是尾部模糊匹配,索引不会失效;
  9. 如果MySQL评估使用索引比全表更慢,则不使用索引;
  10. in走索引,not in索引失效;
  11. 尽量使用复合索引,而少使用单列索引。单列索引数据库会选择一个最优的索引来使用,并不会使用全部索引。

二、视图

1.概述

视图是一种虚拟存在的表。视图并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。通俗的讲,视图就是一条select语句执行后返回的结果集。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。
视图相对于普通的表的优势主要包括以下几项:

  • 简单:使用视图的用户完全不需要关心后面对应的表的结构、关联条件和筛选条件,对用户来说已经是过滤好的复合条件的结果集;
  • 安全:使用视图的用户只能访问它们被允许查询的结果集,对表的权限管理并不能限制到某个行某个列,但是通过视图就可以简单的实现;
  • 数据独立:一旦视图的结构确定了,可以屏蔽表结构变化对用户的影响,原表增加列对视图没有影响;原表修改列名,则可以通过修改视图来解决,不会造成对访问者的影响。

2.创建或者修改视图

创建视图的语法为:create view 视图名称 as SQL语句;

修改视图的语法为:alter view 视图名称 as SQL语句;

3.查看以及删除视图

查看视图: 从MySQL5.1版本开始,使用show tables命令的时候不仅显示表的名字,同时也会显示视图的名字,而不存在单独显示视图的show views命令。同样,在使用show table status命令的时候,不但可以显示表的信息,同时也可以显示视图的信息。

删除视图: drop view 视图名称; 或者 drop view if exists 视图名称;

三、存储过程和函数

1.存储过程和函数概述

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

存储过程和函数的区别在于函数必须有返回值,而存储过程没有。
函数:是一个有返回值的过程;
过程:是一个没有返回值的函数;

2.创建存储过程

语法:

delimiter $
create procedure 名称()
begin
	语句块;
end$ 

delimiter:该关键字用来声明SQL语句的分隔符,告诉MySQL解释器,该段命令是否已经结束了,MySQL是否可以执行了。默认情况下,delimiter是分号。在命令行客户端中,如果有一行命令以分号结束,那么回车后,MySQL将会执行该命令,导致报错。

3.调用存储过程

语法:

call 名称();

4.查看存储过程

查询db_name数据库中的所有的存储过程
select name from mysql.proc where db='数据库名称';

查询存储过程的状态信息
show procedure status;

查询某个存储过程的定义
show create procedure 数据库名.存储过程名;

5.删除存储过程

drop procedure [if exists] 存储过程名称;

6.语法

存储过程是可以编程的,意味着可以使用变量、表达式、控制结构,来完成比较复杂的功能。

变量
关键字:DECLARE
通过DECLARE可以定义一个局部变量,该变量的作用范围只能在BEGIN…END块中。

DECLARE 变量名 类型 [default value];

示例:

delimiter $
create procedure pro_test1()
begin
	declare num int default 10;
	select num + 5;
end$
delimiter ;

变量赋值
方式一:直接赋值使用SET,可以赋常量或者赋表达式,具体语法如下:

set 变量名 = 值或者表达式[,变量名 = 值或者表达式]……

示例:

delimiter $
create procedure pro_test1()
begin
	declare name varchar(25);
	set name = 'java';
	select name;
end$
delimiter ;

方式二:通过select…into方式进行赋值操作,具体语法如下:

delimiter $
create procedure pro_test1()
begin
	declare num int;
	select count(*) into num from user;
	select num;
end$
delimiter ;

if条件判断
语法结构:

if 条件 then 代码块;
elseif 条件 then 代码块;
else 代码块;
end if;

示例:

DELIMITER $
CREATE PROCEDURE pro_test2()
BEGIN
	DECLARE grade INT DEFAULT 95;
	DECLARE result CHAR(25) DEFAULT '';
	IF grade >=80 THEN
		SET result = '优秀';
	ELSEIF grade >=60 THEN
		SET result = '及格';
	ELSE
		SET result = '不及格';
	END IF;
	SELECT result;
END$

CALL pro_test2();

传递参数
语法格式:

create procedure 存储过程名称([in/out/inout] 参数名称 参数类型)
begin
	……;
end$

in:该参数可以作为输入,也就是需要调用方传入值,默认
out:该参数作为输出,也就是该参数可以作为返回值
inout:既可以作为输入参数,也可以作为输出参数

in的示例:

DELIMITER $
CREATE PROCEDURE pro_test3(IN grade INT)
BEGIN
	DECLARE result CHAR(25) DEFAULT '';
	IF grade >=80 THEN
		SET result = '优秀';
	ELSEIF grade >=60 THEN
		SET result = '及格';
	ELSE
		SET result = '不及格';
	END IF;
	SELECT result;
END$

CALL pro_test3(83);

out的示例:

DELIMITER $
CREATE PROCEDURE pro_test4(IN grade INT, OUT result CHAR(25))
BEGIN
	IF grade >=80 THEN
		SET result = '优秀';
	ELSEIF grade >=60 THEN
		SET result = '及格';
	ELSE
		SET result = '不及格';
	END IF;
END$
调用存储过程
DELIMITER ;
CALL pro_test4(34, @result);
SELECT @result;

注意:@description:这种变量要在变量名称前面加上@符号,叫做用户会话变量,代表整个会话过程他都是有作用的,这个类似于全局变量一样。
@@global.sort_buffer_size:这种在变量前加上@@符号,叫做系统变量。

case结构
语法结构
方式一:

CREATE PROCEDURE pro_test5()
BEGIN
	DECLARE result INT;
	CASE 1
	WHEN 1 THEN 
		SET result = 1;
	WHEN 2 THEN 
		SET result = 2;
	ELSE
		SET result = 3;
	END CASE;
	SELECT result;
END$

方式二:

DELIMITER $
CREATE PROCEDURE pro_test6()
BEGIN
	DECLARE grade INT DEFAULT 60;
	DECLARE result CHAR(25);
	CASE
	WHEN grade>=80 THEN 
		SET result = "优秀";
	WHEN grade>=60 THEN 
		SET result = "及格";
	ELSE
		SET result = "不及格";
	END CASE;
	SELECT result;
END$

while循环
语法结构:

DELIMITER $
CREATE PROCEDURE pro_test7(IN n INT)
BEGIN
	DECLARE result INT DEFAULT 0;
	DECLARE i INT DEFAULT 1;
	WHILE i<=n DO
		SET result=result+i;
		SET i=i+1;
	END WHILE;
	SELECT CONCAT("结果是",result);
END$
DELIMITER ;
CALL pro_test7(10);

repeat循环
有条件的循环控制语句,当满足条件的时候退出循环。while是满足条件才执行,repeat是满足条件就退出循环。
语法结构:

DELIMITER $
CREATE PROCEDURE pro_test8(IN n INT)
BEGIN
	DECLARE result INT DEFAULT 0;
	REPEAT
		SET result=result+n;
		SET n=n-1;
		UNTIL n=0
	END REPEAT;
	SELECT CONCAT("结果是",result);
END$
DELIMITER ;
CALL pro_test8(10);

注意: UNTIL语句是不要加分号的,否则会报错。

loop语句
loop实现简单的循环,退出循环的条件需要使用其他的语句定义,通常可以使用leave语句实现,具体语法如下:

DELIMITER $
CREATE PROCEDURE pro_test9(IN n INT)
BEGIN
	DECLARE result INT DEFAULT 0;
	c:LOOP
		SET result=result+n;
		SET n=n-1;
		IF n <= 0 THEN
			LEAVE c;
		END IF;
	END LOOP c;
	SELECT result;
END$
DELIMITER ;
CALL pro_test9(10);

注意: 如果不在代码块中增加退出循环的语句,那么loop语句可以用来实现简单的死循环。

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

DELIMITER $
CREATE PROCEDURE pro_test11()
BEGIN
	DECLARE id INT;
	DECLARE username CHAR(25);
	DECLARE `password` CHAR(25);
	DECLARE	has_data INT DEFAULT 1;
	
	DECLARE user_result CURSOR FOR SELECT * FROM USER;
	DECLARE EXIT HANDLER FOR NOT FOUND SET has_data=0;
	
	OPEN user_result;
	REPEAT
		FETCH user_result INTO id,username,`password`;
		SELECT CONCAT("id:",id,"   username",username,"   password",`password`) AS "结果";
		UNTIL has_data=0
	END REPEAT;
	CLOSE user_result;
END$
DELIMITER ;
CALL pro_test11();

存储函数
示例:

DELIMITER $
CREATE FUNCTION fun1(userId INT) RETURNS INT
DETERMINISTIC
BEGIN
	DECLARE num INT DEFAULT 0;
	SELECT COUNT(*) INTO num FROM `user` WHERE id=userId;
	RETURN num;
END$
DELIMITER ;
SELECT fun1(1) AS "总数";

四、触发器

1.概述

触发器是与表有关的数据库对象,指在insert/update/delete之前或之后,触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性、日志记录、数据校验等操作。
使用别名OLD和NEW来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还只支持行级触发,不支持语句级触发。

insert型触发器:NEW表示将要或者已经新增的数据;
update型触发器:OLD表示修改之前的数据,NEW表示将要或已经修改后的数据;
delete型触发器:OLD表示将要或者已经删除的数据。

2.创建触发器

创建insert触发器,增加一条数据之后向日志表中记录增加记录。示例:

DELIMITER $
CREATE TRIGGER user_insert_trigger
AFTER INSERT
ON USER
FOR EACH ROW
BEGIN
	INSERT INTO user_logs VALUES(NULL,'insert',NOW(),new.id,CONCAT('插入后(id:',new.id,',username:',new.username,',password:',new.password));
END$

创建update触发器,修改表中数据之后向日志表中记录修改记录。示例:

DELIMITER $
CREATE TRIGGER user_update_trigger
AFTER UPDATE
ON USER
FOR EACH ROW
BEGIN
	INSERT INTO user_logs VALUES(NULL,'update',NOW(),new.id,CONCAT('修改前(id:',old.id,',username:',old.username,',password:',old.password,'修改后(id:',new.id,',username:',new.username,',password:',new.password));
END$

创建delete触发器,删除表中数据之后向日志表中记录删除记录。示例:

DELIMITER $
CREATE TRIGGER user_delete_trigger
AFTER DELETE
ON USER
FOR EACH ROW
BEGIN
	INSERT INTO user_logs VALUES(NULL,'delete',NOW(),old.id,CONCAT('删除的记录(id:',old.id,',username:',old.username,',password:',old.password));
END$

3.删除触发器

drop trigger [数据库名.]触发器名;

如果没有指定数据库名,默认为当前数据库。

4.查看触发器

可以通过执行show triggers;命令查看触发器的状态、语法等信息。

show triggers;

五、MySQL的体系结构概览

整个MySQL Server由以下组成:

  • Connection Pool:连接池组件;
  • Management Services & Utilities:管理服务和工具组件;
  • SQL Interface:SQL接口组件;
  • Parser:查询分析器组件;
  • Optimizer:优化器组件;
  • Caches & Buffers:缓冲池组件;
  • Pluggable Storage Engines:存储引擎;
  • File System:文件系统。
  1. 连接层
    最上层是一些客户端和链接服务,包含本地sock通信和大多数基于客户端/服务端工具实现的类似于TCP/IP的通信。主要完成一些类似于连接处理、授权认证及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。

  2. 服务层
    第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如过程、函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定表的查询的顺序,是否利用索引等,最后生成相应的执行操作。如果是select语句,服务器还会查询内存的缓存,如果缓存空间足够大,这样在解决大量读操作的环境中能够很好的提升系统的性能。

  3. 引擎层
    存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。

  4. 存储层
    数据存储层,主要是将数据存储在文件系统之上,并完成与存储引擎的交互。

和其他数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎上,插件式的存储引擎架构,将查询处理和其他的系统任务以及数据的存储提取分离。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。

六、存储引擎

1.概述

和大多数的数据库不同,MySQL中有一个存储引擎的概念,针对不同的存储需求可以选择最优的存储引擎。
存储引擎就是存储数据,建立索引,更新查询数据等等技术的实现方式。存储引擎是基于表的,而不是基于库的。所以存储引擎也可被称为表类型。
Oracle、SQL server等数据库只有一种存储引擎。MySQL提供了插件式的存储引擎架构。所以MySQL存在多种存储引擎,可以根据需要使用相应引擎,或者编写存储引擎。
MySQL5.0支持的存储引擎包含:InnoDB、MyISAM、BDB、MEMORY、MERGE、EXAMPLE、NDB Cluster、ARCHIVE、CSV、BLACKHOLE、FEDERATED等,其中InnoDB和BDB提供事务安全表,其他存储引擎是非事务安全表。

2.存储引擎的特性

InnoDB:支持事务、外键、表级锁、行级锁;
MyISAM:不支持事务、行级锁、外键,支持表级锁。

InnoDB的表结构定义放在.frm,数据和索引放在.idb;
MyISAM的表结构定义放在.frm,数据放在.myd,索引放在.myi

3.MEMORY

Memory存储引擎将表的数据存放在内存中。每个MEMORY表实际对应一个磁盘文件,格式是.frm,该文件中只存储表的结构,,而其数据文件,都是存储在内存中,这样有利于数据的快速处理,提高整个表的效率。MEMORY类型的表访问非常
地快,因为他的数据是存放在内存中的,并且默认使用HASH索引,但是服务一旦关闭,表中的数据就会丢失。

4.MERGE

MEGER存储引擎是一组MyISAM表的组合,这些MyISAM表必须结构完全相同,MERGE表本身并没有存储数据,对MERGE类型的表可以进行查询、更新、删除操作,这些操作实际上是对内部的MyISAM表进行的。
对于MERGE类型表的插入操作,是通过INSERT_METHOD子句定义插入的表,可以有3个不同的值,使用FIRST或LAST值使得插入操作被相应地应用在第一或者最后一个表上,不定义这个子句或者定义为NO,表示不能对这个MERGE表执行插入操作。
可以对MERGE表进行DROP操作,但是这个操作只是删除MERGE表的定义,对内部的表是没有任何影响的。

5.存储引擎的选择

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

  • InnoDB:是MySQL的默认存储引擎,用于事务处理应用程序,支持外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询意外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择。InnoDB存储引擎除了有效的降低由于删除和更新导致的锁定,还可以确保事务的完整提交和回滚,对于类似于计费系统或者财务系统等对数据准确性要求比较高的系统,InnoDB是最合适的选择。
  • MyISAM:如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。
  • MEMORY:将所有数据保存在RAM中,在需要快速定位记录和其他类似数据环境下,可以提供几块的访问。MEMORY的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,其次是要确保表的数据可以恢复,数据库异常终止后表中的数据是可以恢复的。MEMORY表通常用于更新不太频繁的小表,用以快速得到访问结果。
  • MERGE:用于将一系列等同的MyISAM表以逻辑方式组合在一起,并作为一个对象引用它们。MERGE表的优点在于可以突破对单个MyISAM表的大小限制,并且通过将不同的表分布在多个磁盘上,可以有效的改善MERGE表的访问效率。这对于存储诸如数据仓储等VLDB环境十分合适。

七、优化SQL步骤

1.查看SQL执行效率

MySQL客户端连接成功后,通过show [session | global] status命令可以提供服务器状态信息。show [session | global] status可以根据需要加上参数“session”或者“global”来显示session级(当前连接)的统计结果和global级(自数据库上次启动至今)的统计结果。如果不写,默认使用参数是“session”。

显示当前session中所有统计参数的值:

show status like 'Com_______';

显示当前global中所有统计参数的值:

show global status like 'Com_______';

显示当前session中所有统计参数的值(操作的行数):

show status like 'Innodb_rows_%';

Com_xxx表示每个xxx语句执行的次数,参数含义如下:
Com_select:执行select操作的次数,一次查询只累加1;
Com_insert:执行insert操作的次数,对于批量插入的insert操作,只累加一次;
Com_update:执行update操作的次数;
Com_delete:执行delete操作的次数;
Innodb_rows_read:select查询返回的行数;
Innodb_rows_inserted:执行insert操作插入的行数;
Innodb_rows_updated:执行update操作更新的行数;
Innodb_rows_deleted:执行delete操作删除的行数;
Connections:试图连接MySQL服务器的次数;
Uptime:服务器工作时间;
Slow_queries:慢查询的次数。

2.定位低效率执行SQL

可以通过以下两种方式定位执行效率较低的SQL语句。

  • 慢查询日志:通过慢查询日志定位那些执行效率较低的SQL语句,用–log-slow-queries[=file_name]选项启动时,mysql写一个包含所有执行时间超过long_query_time秒的SQL语句的日志文件。
  • show processlist:慢查询日志在查询结束以后才记录,所以在应用反映执行效率出现问题的时候查询慢查询日志并不能定位问题,可以使用show processlist命令查看当前MySQL在进行的线程,包括线程的状态、是否锁表等,可以实时地查看SQL的执行情况,同时对一些锁表操作进行优化。

3.explain分析执行计划

通过以上步骤查询到效率低的SQL语句后,可以通过explain或者desc命令获取MySQL如何执行select语句的信息,包括在select语句执行过程中表如何连接和连接的顺序。
示例:

explain select * from user where id = 3;

查询之后显示以下字段:
id:select查询的序列号,是一组数字,表示的是查询中执行select子句或者操作表的顺序;
select_type:表示select的类型,常见的取值有simple(简单表,即不使用表连接或者子查询)、primary(主查询,即外层的查询)、union(union中的第二个或者后面的查询语句)、subquery(子查询中的第一个select)等;
table:输出结果集的表;
type:表示表的连接类型,性能由好到差的连接类型为(system–>const–>eq_ref–>ref–>ref_or_null–>index_merge–>index_subquery–>range–>index–>all);
possible——keys:表示查询时,可能使用的索引;
key:表示实际使用的索引;
key_len:索引字段的长度;
rows:扫描行的数量;
extra:执行情况的说明和描述。

explain之id
id字段是select查询的序列号,是一组数字,表示的是查询中执行select子句或者是操作表的顺序。

  • id相同表示加载表的顺序是从上到下;
  • id不同id值越大,优先级越高,越先被执行。

explain之select_type
表示select的类型,常见的取值,如下表所示:

  • simple:简单的select查询,查询中不包含子查询或者union;
  • primary:查询中若包含任何复杂的子查询,最外层查询标记为该标识;
  • subquery:在select或where列表中包含了子查询;
  • derived:在from列表中包含的子查询,被标记为derived(衍生)MySQL会递归执行这些子查询,把结果放在临时表中;
  • union:若第二个select出现在union之后,则标记为union;若union包含在from子句的子查询中,外层select将标记为derived;
  • union result:从union表获取结果的select。

explain之table
展示这一行的数据是关于哪一张表的

explain之type
type显示的是访问类型,此指标较为重要,可取值为:

  • NULL:MySQL不访问任何表,索引,直接返回结果;
  • system:表只有一行记录(等于系统表),这是const类型的特例,一般不会出现;
  • const:表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快。如将主键置于where列表中,MySQL就能将该查询转换为一个常量。const将“主键”或“唯一”索引的所有部分与常量值进行比较;
  • eq_ref:类似ref,区别在于使用的是唯一索引,使用主键的关联查询,关联查询出的记录只有一条。常见于主键或唯一索引扫描;
  • ref:非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,返回所有匹配某个单独值的所有行(多个);
  • range:只检索给定返回的行,使用一个索引来选择行。where之后出现between,<,>,in等操作;
  • index:index和ALL的区别为index类型只是遍历了索引树,通常比ALL快,ALL是遍历数据文件;
  • all:将遍历全表以找到匹配的行。

explain之key

  • possible_keys:显示可能应用在这张表的索引,一个或多个;
  • key:实际使用的索引,如果为NULL,则没有使用索引;
  • key_len:表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好。

explain之rows
扫描行的数量

explain之extra
其他的额外的执行计划信息,在该列展示。 可取值为:

  • using filesort:说明MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取,称为“文件排序”;
  • using temporary:使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于order by和group by;
  • using index:表示相应的select操作使用了覆盖索引,避免访问表的数据行,效率不错。

4.show profile分析SQL

MySQL从5.0.37版本开始增加了对show profiles和show profile语句的支持。show profiles能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。

通过have_profiling参数,能够看到当前MySQL是否支持profile。示例:

SELECT @@have_profiling;

默认profiling是关闭的,可以通过set语句在session级别开启profiling。可用以下语句查看

SELECT @@profiling;

开启语句如下:

SET profiling = 1;

执行show profiles指令来查看SQL语句执行的耗时:

SHOW PROFILES;

通过show profile for query query_id语句可以查看到该SQL执行过程中每个线程的状态和消耗的时间,示例:

show profile for query 52;

显示结果:
MySQL高级知识_第3张图片

5.trace分析优化器执行计划

MySQL5.6提供了对SQL的跟踪trace,通过trace文件能够进一步了解为什么优化器选择A计划,而不是选择B计划。
打开trace,设置格式为JSON,并设置trace最大能够使用的内存大小,避免解析过程中因为默认内存过小而不能够完整显示。

set optimizer_trace = "enabled=on",end_markers_in_json=on;
set optimizer_trace_max_mem_size=1000000;

执行SQL语句:

select * from user where id=1;

最后,检查information_schema.optimizer_trace就可以知道MySQL是如何执行SQL的:

SELECT * FROM information_schema.`OPTIMIZER_TRACE`\G;

八、SQL优化

1.大批量插入数据

当使用load命令导入数据的时候,适当的设置可以提高导入的效率。

load data local infile '路径' into table '表名' fields terminated by ',' lines terminated by '\n';

对于InnoDB类型的表,有以下几种方式可以提高导入的效率:

  1. 主键顺序插入。 因为InnoDB类型的表是按照主键的顺序保存的,所以将导入的数据按照主键的顺序排列,可以有效的提高导入数据的效率。如果InnoDB表没有主键,那么系统会自动默认创建一个内部列作为主键,所以如果可以给表创建一个主键,将可以利用这点来提高导入数据的效率。
  2. 关闭唯一性校验。 在导入数据前执行SET UNIQUE_CHECKS=0,关闭唯一性校验,在导入结束后执行SET UNIQUE_CHECKS=1,恢复唯一性校验,可以提高导入的效率。
  3. 手动提交事务。 如果应用使用自动提交的方式,建议在导入前执行 SET AUTOCOMMIT=0,关闭自动提交,导入结束后再执行 SET AUTOCOMMIT=1,打开自动提交,也可以提高导入的效率。

2.优化insert语句

  • 如果需要同时对一张表插入很多行数据时,应该尽量使用多个值表的insert语句,这种方式将大大的缩减客户端与数据库之间的连接、关闭等消耗。使得效率比分开执行的单个insert语句快。
    例如:
    优化前
    insert into user values(null,'张三','zs123456');
    insert into user values(null,'李四','ls123456');
    insert into user values(null,'王五','ww123456');
    
    优化后
    insert into user values(null,'张三','zs123456'),(null,'李四','ls123456'),(null,'王五','ww123456');
    
  • 开启事务进行数据插入。
    例如:
    start transaction;
    insert into user values(null,'张三','zs123456');
    insert into user values(null,'李四','ls123456');
    insert into user values(null,'王五','ww123456');
    commit;
    
  • 数据有序插入。
    例如:
    优化前
    	insert into user values(2,'张三','zs123456');
    	insert into user values(1,'李四','ls123456');
    	insert into user values(3,'王五','ww123456');
    
    优化后
    	insert into user values(1,'张三','zs123456');
    	insert into user values(2,'李四','ls123456');
    	insert into user values(3,'王五','ww123456');
    

3.优化order by语句

两种排序方式:

  • 第一种是通过对返回数据进行排序,也就是通常说的file sort排序,所有不是通过索引直接返回排序结果的排序都叫file sort排序;
  • 第二种通过有序索引顺序扫描直接返回有序数据,这种情况即为using index,不需要额外排序,操作效率高。

了解了MySQL的排序方式,优化目标就清晰了:尽量减少额外的排序,通过索引直接返回有序数据。where条件和order by使用相同的索引,并且order by的顺序和索引顺序相同,并且order by的字段都是升序或者都是降序。否则肯定需要额外的操作,这样就会出现file sort。

file sort的优化:
通过创建合适的索引,能够减少file sort的出现,但是在某些情况下,条件限制不能让file sort消失,那就需要加快file sort的排序操作。对于file sort,MySQL有两种排序算法:

  1. 两次扫描算法:MySQL4.1之前,使用该方式排序。首先根据条件取出排序字段和行指针信息,然后在排序区sort buffer中排序,如果sort buffer不够,则在临时表temporary table中存储排序结果。完成排序之后,再根据行指针回表读取记录,该操作可能会导致大量随机I/O操作;
  2. 一次扫描算法:一次性取出满足条件的所有字段,然后在排序区sort buffer中排序后直接输出结果集。排序时内存开销较大,但是排序效率比两次扫描算法要高。

MySQL通过比较系统变量max_length_for_sort_data的大小和Query语句取出的字段总大小,来判定使用哪种排序算法,如果max_length_for_sort_data更大,那么使用第二种优化之后的算法;否则使用第一种。
可以适当提高sort_buffer_size和max_length_for_sort_data系统变量,来增大排序区的大小,提高排序的效率。

4.优化group by语句

由于group by实际上也同样会进行排序操作,而且与order by相比,group by主要只是多了排序之后的分组操作。当然,如果在分组的时候还使用了其他的一些聚合函数,那么还需要一些聚合函数的计算。所以,在group by的实现过程中,与order by一样也可以利用到索引。
如果查询包含group by但是用户想要避免排序结果的消耗,则可以执行order by null禁止排序。如下:

select age,count(*) from user group by age order by null;

5.优化嵌套查询

MySQL4.1版本之后,开始支持SQL的子查询。这个技术可以使用select语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询是可以被更高效的连接(JOIN)替代。

6.优化or条件

对于包含or的查询子句,如果要利用索引,则or之间的每个条件列都必须用到索引,而且不能使用到复合索引;如果没有索引,则应该考虑增加索引。
建议使用union替换or

7.优化分页查询

一般分页查询时,通过创建覆盖索引能够比较好地提高性能。一个常见又非常头疼的问题就是limit 2000000, 10,此时需要MySQL排序前2000010记录,仅仅返回2000000-2000010的记录,其他记录丢弃,查询排序的代价非常大。

优化思路一:
在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容。

优化思路二:
该方案适用于主键自增的表,可以把limit查询转换成某个位置的查询。例如

select * from user where id > 2000000 limit 10;

8.使用SQL提示

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

USE INDEX
在查询语句中表名的后面,添加use index来提供希望MySQL去参考的索引列表,就可以让MySQL不再考虑其他可用的索引。示例:

select * from user use index(idx_user_name) where name='张三';

IGNORE INDEX
如果用户只是单纯的想让MySQL忽略一个或者多个索引,则可以使用ignore index 作为hint。

select * from user ignore index(idx_user_name) where name='张三';

FORCE INDEX
为强制MySQL使用一个特定的索引,可在查询中使用force index作为hint。

select * from user force index(idx_user_name) where name='张三';

九、应用优化

1.使用连接池

对于访问数据库来说,建立连接的代价是比较昂贵的,因为我们频繁的创建关闭连接,是比较耗费资源的,我们有必要建立数据库连接池,以提高访问的性能。

2.减少对MySQL的访问

避免对数据进行重复检索
在编写应用代码时,需要能够理清对数据库的访问逻辑。能够一次连接就获取到结果的,就不用两次连接,这样可以大大减少对数据库无用的重复请求。

增加cache层
在应用中,我们可以在应用中增加缓存层来达到减轻数据库负担的目的。缓存层有很多种,也有很多实现方式,只要能达到降低数据库的负担又能满足应用需求就可以。
因此可以部分数据从数据库中抽取出来放到应用端以文本方式存储,或者使用框架(mybatis)提供的一级缓存/二级缓存,或者使用redis数据库来缓存数据。

3.负载均衡

负载均衡是应用中使用非常普遍的一种优化方法,它的机制就是利用某种均衡算法,将固定的负载量分布到不同的服务器上,以此来降低单台服务器的负载,达到优化的效果。

利用MySQL复制分流查询
通过MySQL的主从复制,实现读写分离,使增删改操作走主节点,查询操作走从节点,从而可以降低单台服务器的读写压力。
MySQL高级知识_第4张图片

采用分布式数据库架构
分布式数据库架构适合大数据量、负载高的情况,它有良好的拓展性和高可用性。通过在多台服务器之间分布数据,可以实现在多台服务器之间的负载均衡,提高访问效率。

十、MySQL中查询缓存优化

1.概述

开启MySQL的查询缓存,当执行完全相同的SQL语句的时候,服务器就会直接从缓存中读取结果,当数据被修改,之前的缓存会失效,修改比较频繁的表不适合做查询缓存。

2.操作流程

MySQL高级知识_第5张图片

  1. 客户端发送一条查询给服务器;
  2. 服务器先会检查查询缓存,如果命中了缓存,则立即返回存储在缓存中的结果。否则进入下一阶段;
  3. 服务器端进行SQL解析、预处理,再由优化器生成对应的执行计划;
  4. MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询;
  5. 将结果返回给客户端。

3.查询缓存配置

  1. 查看当前的MySQL数据库是否支持查询缓存:
    SHOW VARIABLES LIKE 'have_query_cache';
    
  2. 查看当前MySQL是否开启了查询缓存:
    SHOW VARIABLES LIKE 'query_cache_type';
    
  3. 查看查询缓存的占用大小:
    SHOW VARIABLES LIKE 'query_cache_size';
    
  4. 查看查询缓存的状态变量:
    SHOW STATUS LIKE 'Qcache%';
    
    参数的含义:
    Qcache_free_blocks:查询缓存中的可用内存块数;
    Qcache_free_memory:查询缓存的可用内存量;
    Qcache_hits:查询缓存命中数;
    Qcache_inserts:添加到查询缓存的查询数;
    Qcache_lowmen_prunes:由于内存不足而从查询缓存中删除的查询数;
    Qcache_not_cached:非缓存查询的数量(由于query_cache_type设置而无法缓存或未缓存);
    Qcache_queries_in_cache:查询缓存中注册的查询数;
    Qcache_total_blocks:查询缓存中的块总数。

4.开启查询缓存

MySQL的查询缓存默认是关闭的,需要手动配置参数query_cache_type来开启查询缓存。query_cache_type该参数的可取值有三个:

  • OFF或0:查询缓存功能关闭;
  • ON或1:查询缓存功能打开,SELECT的结果符合缓存条件即会缓存,否则,不予缓存,显式指定SQL_NO_CACHE,不予缓存;
  • DEMAND或2:查询缓存功能按需进行,显式指定SQL_CACHE的SELECT语句才会缓存;其他均不予缓存。

在/usr/my.cnf配置中,增加以下配置:

# 开启MySQL的查询缓存
query_cache_type=1

配置完毕之后,重启服务即可生效;
然后就可以在命令行执行SQL语句进行验证,执行一条比较耗时的SQL语句,然后再多执行几次,查看后面几次的执行时间;获取通过查看查询缓存的缓存命中数,来判定是否走查询缓存。

5.查询缓存SELECT选项

可以在SELECT语句中指定两个与查询缓存相关的选项:
SQL_CACHE:如果查询结果是可缓存的,并且query_cache_type系统变量的值为ON或DEMAND,则缓存查询结果。
SQL_NO_CACHE:服务器不使用查询缓存。它既不检查查询缓存,也不检查结果是否已缓存,也不缓存查询结果。
例如:

SELECT SQL_CACHE id,name FROM user;
SELECT SQL_NO_CACHE id,name FROM user;

6.查询缓存失效的情况

  1. SQL语句不一致的情况。要想命中查询缓存,查询的SQL语句必须一致;
  2. 当查询语句中有一些不确定的语句时,则不会缓存。如:now(),current_date(),curdate(),curtime(),rand(),uuid(),user(),database();
  3. 不使用任何表查询语句;如:SELECT ‘hello,MySQL’;
  4. 查询mysql、information_schema或performance_schema数据库中的表时,不会走查询缓存;
  5. 在存储的函数,触发器或事件的主体内执行的查询;
  6. 如果表更改,则使用该表的所有高速缓存查询都将变为无效并从高速缓存中删除。如:INSERT、UPDATE、DELETE、TRUNCATE TABLE、ALTER TABLE、DROP TABLE或DROP DATABASE。

十一、MySQL内存管理及优化

1.内存优化原则

  1. 将尽量多的内存分配给MySQL做缓存,但要给操作系统和其他程序预留足够内存;
  2. MyISAM存储引擎的数据文件读取依赖于操作系统自身的IO缓存,因此,如果有MyISAM表,就要预留更多的内存给操作系统做IO缓存;
  3. 排序区、连接区等缓存是分配给每个数据库会话(session)专用的,其默认值的设置要根据最大连接数合理分配,如果设置太大,不但浪费资源,而且在并发连接较高时会导致物理内存耗尽。

2.MyISAM内存优化

MyISAM存储引擎使用key_buffer缓存索引块,加速MyISAM索引的读写速度。对于MyISAM表的数据块,MySQL没有特别的缓存机制,完全依赖于操作系统的IO缓存。

key_buffer_size
key_buffer_size决定MyISAM索引块缓存区的大小,直接影响到MyISAM表的存取效率。可以在MySQL参数文件中设置key_buffer_size的值,对于一般MyISAM数据库,建议至少将1/4可用内存分配给key_buffer_size。
在/usr/my.cnf中做如下配置:

key_buffer_size=512M

read_buffer_size
如果需要经常顺序扫描MyISAM表,可以通过增大read_buffer_size的值来改善性能。但需要注意的是read_buffer_size是每个session独占的,如果默认值设置太大,就会造成内存浪费。

read_rnd_buffer_size
对于需要做排序的MyISAM表的查询,如带有order by子句的sql,适当增加read_rnd_buffer_size的值,可以改善此类的sql性能。但需要注意的是read_rnd_buffer_size是每个session独占的,如果默认值设置太大,就会造成内存浪费。

3.InnoDB内存优化

InnoDB用一块内存区做IO缓存池,该缓存池不仅用来缓存InnoDB的索引块,而且也用来缓存InnoDB的数据块。

innodb_buffer_pool_size
该变量决定了innodb存储引擎表数据和索引数据的最大缓存区大小。在保证操作系统及其他程序有足够内存可用的情况下,innodb_buffer_pool_size的值越大,缓存命中率越高,访问InnoDB表需要的磁盘I/O就越少,性能也就越高。

innodb_buffer_pool_size=512M

innodb_log_buffer_size
决定了innodb重做日志缓存的大小,对于可能产生大量更新记录的大事务,增加innodb_log_buffer_size的大小,可以避免innodb在事务提交前就执行不必要的日志写入磁盘操作。

innodb_log_buffer_size=10M

十二、MySQL并发参数调整

从实现上来说,MySQL server是多线程结构,包括后台线程和客户服务线程。多线程可以有效利用服务器资源,提高数据库的并发性能。在MySQL中,控制并发连接和线程的主要参数包括max_connections、back_log、thread_cache_size、table_open_cache。

1.max_connections

采用max_connections控制允许连接到MySQL数据库的最大数量,默认值是151。如果状态变量connection_errors_max_connections不为零,并且一直增长,则说明不断有连接请求因数据库连接数已达到允许最大值而失败,这是可以考虑增大max_connections的值。
MySQL最大可支持的连接数,取决于很多因素,包括给定操作系统平台的线程库的质量、内存大小、每个连接的负荷、CPU的处理速度,期望的响应时间等。在Linux平台下,性能好的服务器,支持500-1000个连接不是难事,需要根据服务器性能进行评估设定。

2.back_log

back_log参数控制MySQL监听TCP端口时设置的积压请求栈大小。如果MySQL的连接数达到max_connections时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即back_log,如果等待连接的数量超过back_log,将不被授予连接资源,将会报错。5.6.6版本之前默认值为50,之后的版本默认为50+(max_connections/5),但最大不超过900。
如果需要数据库在较短的时间内处理大量连接请求,可以考虑适当增大back_log的值。

3.table_open_cache

该参数用来控制所有SQL语句执行线程可打开表缓存的数量,而在执行SQL语句时,每一个SQL执行线程至少要打开1个表缓存。该参数的值应该根据设置的最大连接数max_connections以及每个连接执行关联查询中涉及的表的最大数量来设定。

4.thread_cache_size

为了加快连接数据库的速度,MySQL会缓存一定数量的客户服务线程以备重用,通过参数thread_cache_size可控制MySQL缓存客户服务线程的数量。

5.innodb_lock_wait_timeout

该参数是用来设置InnoDB事务等待行锁的时间,默认值是50ms,可以根据需要进行动态设置。对于需要快速反馈的业务系统来说,可以将行锁的等待时间调小,以避免事务长时间挂起;对于后台运行的批量处理程序来说,可以将行锁的等待时间调大,以避免发生大的回滚操作。

十三、MySQL锁问题

1.锁概述

锁是计算机协调多个进程或线程并发访问某一资源的机制(避免争抢)。
在数据库中,除传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。

2.锁分类

从对数据操作的粒度分:

  • 表锁:操作时,会锁定整个表;
  • 行锁:操作时,会锁定当前操作行。

从对数据操作的类型分:

  • 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响;
  • 写锁(排它锁):当前操作没有完成之前,它会阻断其他写锁和读锁。

3.MySQL锁

MySQL高级知识_第6张图片
MySQL高级知识_第7张图片

4.MyISAM表锁

MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此,用户一般不需要直接用LOCK TABLE命令给MyISAM表显式加锁。

显示加表锁的语法:

#加读锁
lock table 表名 read;

#加写锁
lock table 表名 write;

简而言之,读锁会阻塞写,但是不会阻塞读。而写锁,则既会阻塞读,又会阻塞写。

查看锁的争用情况:

show open tables;

In_user:表当前被查询使用的次数。如果该数为零,则表是打开的,但是当前没有被使用;
Name_locked:表名称是否被锁定。名称锁定用于取消表或对表进行重命名等操作。

show status like 'Table_locks%';

Table_locks_immediate:指的是能够立即获得表级锁的次数,每立即获取锁,值加1;
Table_locks_waited:指的是不能立即获取表级锁而需要等待的次数,每等待一次,该值加1,此值高说明存在着较为严重的表级锁争用情况。

5.InnoDB行锁

行锁介绍
行锁特点:偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度很小,发生锁冲突的概率降低,并发度升高。
InnoDB与MyISAM的最大不同有两点:一是支持事务;二是采用了行级锁。

InnoDB的行锁模式
InnoDB实现了以下两种类型的行锁。

  • 共享锁(S):又称为读锁,简称S锁,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改;
  • 排他锁(X):又称为写锁,简称X锁,排他锁就是不能与其他锁并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据行读取和修改。

对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);
对于普通SELECT语句,InnoDB不会加任何锁。

可以通过以下语句显式给记录集加共享锁或排他锁:

#共享锁(S)
SELECT * FROM 表名 WHERE …… LOCK IN SHARE MODE;

#排他锁(X)
SELECT * FROM 表名 WHERE …… FOR UPDATE;

无索引行锁升级为表锁
如果不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,实际效果跟表锁一样。

查看当前表的索引:

show index from 表名;

由于执行更新时,name字段本来为varchar类型,我们是作为数组类型使用,存在类型转换,索引失效,最终行锁变为表锁。

间隙锁危害
当我们用范围条件,而不是使用相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据进行加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-key锁)。
例如:
user表中有id为1,3,4,5数据;
在客户端1执行update user set username=“张三” where id>4;
在客户端2执行insert into user values(2,‘李四’);
这时候客户端2处于阻塞状态,因为客户端1锁定了id为1,2,3行的数据。

InnoDB行锁争用情况

show status like 'innodb_row_lock%';

十四、常用SQL技巧

1.SQL编写顺序

SELECT DISTINCT
	<select list>
FROM
	<left_table><join_condition>
WHERE
	<where_condition>
GROUP BY
	<group_by_list>
HAVING
	<having_condition>
ORDER BY
	<order_by_condition>
LIMIT
	<limit_params>

2.SQL执行顺序

FROM	<left_table>
ON	<join_condition>
<join_type>	JOIN <right_table>
WHERE	<where_condition>
GROUP BY	<group_by_list>
HAVING		<having_condition>
SELECT DISTINCT <select list>
ORDER BY	<order_by_condition>
LIMIT	<limit_params>	

3.正则表达式

示例:

#查询用户表中姓名以‘张’开头的所有信息
select * from user where name regexp '^张';

#查询用户表中姓名以‘三’结尾的所有信息
select * from user where name regexp '三$';

#查询用户表中姓名包含‘张’或者‘李’或者‘王’的所有信息
select * from user where name regexp '[张李王]';

4.常用函数

  1. 数字函数
    MySQL高级知识_第8张图片
  2. 字符串函数
    MySQL高级知识_第9张图片
  3. 日期函数
    MySQL高级知识_第10张图片
  4. 聚合函数
    MySQL高级知识_第11张图片

十五、MySQL中常用工具

1.mysql

该mysql不是指mysql服务,而是指mysql的客户端工具。
语法:

mysql [options] [database]

连接选项
参数:
-u,–user=name 指定用户名
-p,–password[=name] 指定密码
-h,–host=name 指定服务器IP或域名
-P,–port=# 指定连接端口

示例:

		mysql -h 127.0.0.1 -P 3306 -u root -p
		mysql -h127.0.0.1 -P3306 -uroot -p123456

执行选项

		-e, --execute=name		执行SQL语句并退出

此选项可以在MySQL客户端执行SQL语句,而不用连接到MySQL数据库再执行,对于一些批处理脚本,这种方式尤其方便。

示例:

		mysql -uroot -p123456 test(数据库名) -e "select * from user";

2.mysqladmin

mysaladmin是一个执行管理操作的客户端程序。可以用它来检查服务器的配置和当前状态、创建并删除数据库等。
可以通过:mysqladmin --help 指令查看帮助文档

示例:

		mysqladmin -uroot -p123456 create 'test1';
		mysqladmin -uroot -p123456 drop 'test1';
		mysqladmin -uroot -p123456 version;

3.mysqlbinlog

由于服务器生成的二进制日志文件以二进制格式保存,所以如果想要检查这些文本的文本格式,就会使用到mysqlbinlog日志管理工具。
语法:
mysqlbinlog [options] log-files1 log-files2 ……
选项:
-d, --database=name:指定数据库名称,只列出指定的数据库相关操作;
-o, --offset=#:忽略掉日志中的前n行命令;
-r, --result-file=name:将输出的文本格式日志输出到指定文件;
-s, --short-form:显示简单格式,省略掉一些信息;
–start-datetime=date1 --stop-datetime=date2:指定日期间隔内的所有日志;
–start-position=pos1 --stop-position=pos2:指定位置间隔内的所有日志。

4.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=#	指定连接端口

输出内容选项:
MySQL高级知识_第12张图片

5.mysqlimport / source

mysqlimport是客户端数据导入工具,用来导入mysqldump 加 -T 参数后导出的文本文件。

语法:

mysqlimport  [options]  db_name  textfile1

示例:

mysqlimport -uroot -p123456 test /tmp/city.txt

如果需要导入sql文件,可以使用mysql中的source指令:

source /root/tb_book.sql

6.mysqlshow

mysqlshow客户端对象查找工具,用来很快地查找存在哪些数据库、数据库中的表、表中的列或者索引。

语法:

mysqlshow [options] [db_name [table_name [col_name]]]

参数:

--count		显示数据库及表的统计信息(数据库,表 均可以不指定)
-i				显示指定数据库或者指定表的状态信息

示例:

#查询每个数据库的表的数量及表中记录的数量
mysqlshow -uroot -p123456 --count

#查询test数据库中每个表中的字段数以及行数
mysqlshow -uroot -p123456 test --count

#查询test数据库中book表的详细情况
mysqlshow -uroot -p123456 test book -i

十六、MySQL日志

在任何一种数据库中,都会有各种各样的日志,记录着数据库工作的方方面面,以帮助数据库管理员追踪数据库曾经发生过的各种事件。MySQL也不例外,在MySQL中,有4种不同的日志,分别是错误日志、二进制日志、查询日志和慢查询日志,这些日志记录着数据库在不同方面的踪迹。

1.错误日志

错误日志是MySQL中最重要的日志之一,它记录了当mysql启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时,可以首先查看此日志。
该日志是默认开启的,默认存放目录为mysql的数据目录(var/lib/mysql),默认的日志文件名为hostname.err(hostname是主机名)。

查看日志位置指令:

show variables like 'log_error%';

2.二进制日志

概述
二进制日志(BINLOG)记录了所有的DDL(数据定义语言)语句和DML(数据操纵语言)语句,但是不包括数据查询语句。此日志对于灾难时的数据恢复起着极其重要的作用,MySQL的主从复制,就是通过该binlog实现的。

二进制日志,默认情况下是没有开启的,需要到MySQL的配置文件中开启,并配置MySQL日志的格式。
配置文件位置:/usr/my.cnf
日志存放位置:配置时,给定了文件名但是没有指定路径,日志默认写入MySQL的数据目录。

#配置开启binlog日志,日志的文件前缀为mysqlbin -----> 生成的文件名如:mysqlbin.000001
log_bin=mysqlbin

#配置二进制日志的格式
binlog_format = STATEMENT

日志格式

  1. STATEMENT
    该日志格式在日志文件中记录的都是SQL语句(statement),每一条对数据进行修改的SQL都会记录在日志文件中,通过MySQL提供的mysqlbinlog工具,可以清晰的查看到每条语句的文本。主从复制的时候,从库(slave)会将日志解析为原文本,并在从库重新执行一次。

  2. ROW
    该日志格式在日志文件中记录的是每一行的数据变更,而不是记录SQL语句。比如,执行SQL语句:update tb_book set status=‘1’,如果是STATEMENT日志格式,在日志中会记录一行SQL语句;如果是ROW,由于是对全表进行更新,也就是每一行记录都会发生变更,ROW格式的日志中会记录每一行的数据变更。

  3. MIXED
    这是目前MySQL默认的日志格式,即混合了STATEMENT 和 ROW两种格式。默认情况下采用STATEMENT,但是在一些特殊情况下采用ROW来进行记录。MIXED格式能尽量利用两种模式的优点,而避开他们的缺点。

查看ROW格式日志
配置:

#配置开启binlog日志,日志的文件前缀为mysqlbin -----> 生成的文件名如:mysqlbin.000001
log_bin=mysqlbin

#配置二进制日志的格式
binlog_format = ROW

如果日志格式是ROW,直接查看数据是查看不懂的,可以在mysqlbinlog后面加上参数-vv

mysqlbinlog -vv mysqlbin.000002

删除日志
对于比较繁忙的系统,由于每天生成日志量大,这些日志如果长时间不清楚,将会占用大量的磁盘空间。下面我们将会讲解几种删除日志的常见方法:

  1. 通过Reset Master指令删除全部binlog日志,删除之后,日志编号将从xxxx.000001重新开始。

    # 删除全部日志指令
    Reset Master;
    
  2. 执行指令purge master logs to ‘mysqlbin.xxxx’,该命令将删除xxxx编号之前的所有日志;

  3. 执行指令purge master logs before ‘yyyy-mm-dd hh24:mi:ss’,该命令将删除日志为“yyyy-mm-dd hh24:mi:ss”之前产生的所有日志;

  4. 设置参数 --expire_logs_days=#,此参数的含义是设置日志的过期天数,过了指定的天数后日志将会被自动删除,这样将有利于减少DBA管理日志的工作量。配置如下:

    log_bin=mysqlbin
    binlog_format=ROW
    --expire_logs_days=3
    

3.查询日志

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

#该选项用来开启查询日志,可选值:0 或者 1;0代表关闭,1代表开启
general_log=1

#设置日志的文件名,如果没有指定,默认的文件名为host_name.log
general_log_file=file_name

在MySQL的配置文件/usr/my.cnf中配置如下内容:

#开启查询日志
general_log=1

#配置查询日志的文件名
general_log_file=mysql_query.log

4.慢查询日志

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

慢查询日志默认是关闭的。可以通过两个参数来控制慢查询日志:

#该参数用来控制慢查询日志是否开启,可取值:1 和 0,1代表开启,0代表关闭
slow_query_log=1

#该参数用来指定慢查询日志的文件名
slow_query_log_file=slow_query.log

#该选项用来配置查询的时间限制,超过这个时间将认为是慢查询,将需要进行日志记录,默认10s
long_query_time=10

十七、MySQL复制

1.概述

复制是指将主数据库的DDL和DML操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步。

MySQL支持一台主库同时向多台从库进行复制,从库同时也可以作为其他从服务器的主库,实现链状复制。

2.复制原理

MySQL的主从复制原理如下:
MySQL高级知识_第13张图片
从上层来看,复制分为三步:

  1. Master主库在事务提交时,会把数据变更作为事件Events记录在二进制日志文件Binlog中;
  2. 主库推送二进制日志文件Binlog中的日志事件到从库的中继日志Relay Log;
  3. slave重做中继日志中的事件,将改变反映它自己的数据。

3.复制优势

MySQL复制的优点主要包含以下三个方面:

  • 主库出现问题,可以快速切换到从库提供服务;
  • 可以在从库上执行查询操作,从主库中更新,实现读写分离,降低主库的访问压力;
  • 可以在从库中执行备份,以避免备份期间影响主库的服务。

4.搭建步骤

master

  1. 在master的配置文件(/usr/my.cnf)中,配置如下内容:

    # mysql 服务ID,保证整个集群环境中唯一
    server-id=1
    
    # mysql binlog日志的存储路径和文件名
    log-bin=/var/lib/mysql/mysqlbin
    
    # 错误日志,默认已经开启
    # log-err
    
    # mysql的安装目录
    #basedir
    
    # mysql的临时目录
    #tmpdir
    
    # mysql的数据存放目录
    #datadir
    
    # 是否只读,1代表只读,0代表读写
    read-only=0
    
    # 忽略的数据,指不需要同步的数据库
    binlog-ignore-db=mysql
    
    # 指定同步的数据库
    #binlog-do-db=db01
    
  2. 执行完毕之后,需要重启MySQL:

    service mysql restart;
    
  3. 创建同步数据的账户,并且进行授权操作:

    grant replication slave on *.* to 'jsuzjx'@'192.168.192.12' identified by 'jsuzjx';
    
    flush privileges;
    
  4. 查看master状态:

    show master status;
    

    字段含义:

    File:从哪个日志文件开始推送日志文件;
    Position:从哪个位置开始推送日志;
    Binlog_Ignore_DB:指定不需要同步的数据库。
    

slave

  1. 在slave端配置文件中,配置如下内容:

    # mysql服务端ID,唯一
    server-id=2
    
    # 指定binlog日志
    log-bin=/var/lib/mysql/mysqlbin
    
  2. 执行完毕之后,需要重启MySQL:

     service mysql restart;
    
  3. 执行如下指令:

     change master to master_host='192.168.192.1', master_user='jsuzjx', master_password='jsuzjx', master_log_file='mysqlbin.000001', master_log_pos=413;
    
  4. 开启同步操作:

     start slave;
    
     show slave status;
    

总结

你可能感兴趣的:(MySQL,mysql,mysql优化,数据库)