定义: 索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
优点:
缺点:
索引是在MySQL的存储引擎层中实现的,而不是在服务器层实现的。所以每种存储引擎的索引都不一定完全相同,也不是所有的存储引擎都支持所有的索引类型的。MySQL目前提供了以下4种索引:
m叉B树的特性:
B+树为B树的变种,B+树与B树的区别:
MySQL中的B+树
MySQL索引数据结构对经典的B+树进行了优化。在原B+树的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+树,提高区间访问的性能。
创建索引
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,用于全文索引
创建复合索引:
CREATE INDEX idx_name_email_status ON tb_seller(NAME,email,STATUS);
就相当于对name创建索引,对name,email创建了索引,对name,email,status创建了索引。
select * from user where name='张三' and password='123456';
视图是一种虚拟存在的表。视图并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。通俗的讲,视图就是一条select语句执行后返回的结果集。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。
视图相对于普通的表的优势主要包括以下几项:
创建视图的语法为:create view 视图名称 as SQL语句;
修改视图的语法为:alter view 视图名称 as SQL语句;
查看视图: 从MySQL5.1版本开始,使用show tables命令的时候不仅显示表的名字,同时也会显示视图的名字,而不存在单独显示视图的show views命令。同样,在使用show table status命令的时候,不但可以显示表的信息,同时也可以显示视图的信息。
删除视图: drop view 视图名称; 或者 drop view if exists 视图名称;
存储过程和函数是事先经过编译并存储在数据库中的一段SQL语句的集合,调用存储过程和函数可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。
存储过程和函数的区别在于函数必须有返回值,而存储过程没有。
函数:是一个有返回值的过程;
过程:是一个没有返回值的函数;
语法:
delimiter $
create procedure 名称()
begin
语句块;
end$
delimiter:该关键字用来声明SQL语句的分隔符,告诉MySQL解释器,该段命令是否已经结束了,MySQL是否可以执行了。默认情况下,delimiter是分号。在命令行客户端中,如果有一行命令以分号结束,那么回车后,MySQL将会执行该命令,导致报错。
语法:
call 名称();
查询db_name数据库中的所有的存储过程
select name from mysql.proc where db='数据库名称';
查询存储过程的状态信息
show procedure status;
查询某个存储过程的定义
show create procedure 数据库名.存储过程名;
drop procedure [if exists] 存储过程名称;
存储过程是可以编程的,意味着可以使用变量、表达式、控制结构,来完成比较复杂的功能。
变量
关键字: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 "总数";
触发器是与表有关的数据库对象,指在insert/update/delete之前或之后,触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性、日志记录、数据校验等操作。
使用别名OLD和NEW来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还只支持行级触发,不支持语句级触发。
insert型触发器:NEW表示将要或者已经新增的数据;
update型触发器:OLD表示修改之前的数据,NEW表示将要或已经修改后的数据;
delete型触发器:OLD表示将要或者已经删除的数据。
创建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$
drop trigger [数据库名.]触发器名;
如果没有指定数据库名,默认为当前数据库。
可以通过执行show triggers;命令查看触发器的状态、语法等信息。
show triggers;
整个MySQL Server由以下组成:
连接层
最上层是一些客户端和链接服务,包含本地sock通信和大多数基于客户端/服务端工具实现的类似于TCP/IP的通信。主要完成一些类似于连接处理、授权认证及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
服务层
第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如过程、函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定表的查询的顺序,是否利用索引等,最后生成相应的执行操作。如果是select语句,服务器还会查询内存的缓存,如果缓存空间足够大,这样在解决大量读操作的环境中能够很好的提升系统的性能。
引擎层
存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。
存储层
数据存储层,主要是将数据存储在文件系统之上,并完成与存储引擎的交互。
和其他数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎上,插件式的存储引擎架构,将查询处理和其他的系统任务以及数据的存储提取分离。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。
和大多数的数据库不同,MySQL中有一个存储引擎的概念,针对不同的存储需求可以选择最优的存储引擎。
存储引擎就是存储数据,建立索引,更新查询数据等等技术的实现方式。存储引擎是基于表的,而不是基于库的。所以存储引擎也可被称为表类型。
Oracle、SQL server等数据库只有一种存储引擎。MySQL提供了插件式的存储引擎架构。所以MySQL存在多种存储引擎,可以根据需要使用相应引擎,或者编写存储引擎。
MySQL5.0支持的存储引擎包含:InnoDB、MyISAM、BDB、MEMORY、MERGE、EXAMPLE、NDB Cluster、ARCHIVE、CSV、BLACKHOLE、FEDERATED等,其中InnoDB和BDB提供事务安全表,其他存储引擎是非事务安全表。
InnoDB:支持事务、外键、表级锁、行级锁;
MyISAM:不支持事务、行级锁、外键,支持表级锁。
InnoDB的表结构定义放在.frm,数据和索引放在.idb;
MyISAM的表结构定义放在.frm,数据放在.myd,索引放在.myi
Memory存储引擎将表的数据存放在内存中。每个MEMORY表实际对应一个磁盘文件,格式是.frm,该文件中只存储表的结构,,而其数据文件,都是存储在内存中,这样有利于数据的快速处理,提高整个表的效率。MEMORY类型的表访问非常
地快,因为他的数据是存放在内存中的,并且默认使用HASH索引,但是服务一旦关闭,表中的数据就会丢失。
MEGER存储引擎是一组MyISAM表的组合,这些MyISAM表必须结构完全相同,MERGE表本身并没有存储数据,对MERGE类型的表可以进行查询、更新、删除操作,这些操作实际上是对内部的MyISAM表进行的。
对于MERGE类型表的插入操作,是通过INSERT_METHOD子句定义插入的表,可以有3个不同的值,使用FIRST或LAST值使得插入操作被相应地应用在第一或者最后一个表上,不定义这个子句或者定义为NO,表示不能对这个MERGE表执行插入操作。
可以对MERGE表进行DROP操作,但是这个操作只是删除MERGE表的定义,对内部的表是没有任何影响的。
在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。以下是几种常用的存储引擎的使用环境。
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:慢查询的次数。
可以通过以下两种方式定位执行效率较低的SQL语句。
通过以上步骤查询到效率低的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子句或者是操作表的顺序。
explain之select_type
表示select的类型,常见的取值,如下表所示:
explain之table
展示这一行的数据是关于哪一张表的
explain之type
type显示的是访问类型,此指标较为重要,可取值为:
explain之key
explain之rows
扫描行的数量
explain之extra
其他的额外的执行计划信息,在该列展示。 可取值为:
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;
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;
当使用load命令导入数据的时候,适当的设置可以提高导入的效率。
load data local infile '路径' into table '表名' fields terminated by ',' lines terminated by '\n';
对于InnoDB类型的表,有以下几种方式可以提高导入的效率:
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');
两种排序方式:
了解了MySQL的排序方式,优化目标就清晰了:尽量减少额外的排序,通过索引直接返回有序数据。where条件和order by使用相同的索引,并且order by的顺序和索引顺序相同,并且order by的字段都是升序或者都是降序。否则肯定需要额外的操作,这样就会出现file sort。
file sort的优化:
通过创建合适的索引,能够减少file sort的出现,但是在某些情况下,条件限制不能让file sort消失,那就需要加快file sort的排序操作。对于file sort,MySQL有两种排序算法:
MySQL通过比较系统变量max_length_for_sort_data的大小和Query语句取出的字段总大小,来判定使用哪种排序算法,如果max_length_for_sort_data更大,那么使用第二种优化之后的算法;否则使用第一种。
可以适当提高sort_buffer_size和max_length_for_sort_data系统变量,来增大排序区的大小,提高排序的效率。
由于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;
MySQL4.1版本之后,开始支持SQL的子查询。这个技术可以使用select语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询是可以被更高效的连接(JOIN)替代。
对于包含or的查询子句,如果要利用索引,则or之间的每个条件列都必须用到索引,而且不能使用到复合索引;如果没有索引,则应该考虑增加索引。
建议使用union替换or
一般分页查询时,通过创建覆盖索引能够比较好地提高性能。一个常见又非常头疼的问题就是limit 2000000, 10,此时需要MySQL排序前2000010记录,仅仅返回2000000-2000010的记录,其他记录丢弃,查询排序的代价非常大。
优化思路一:
在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容。
优化思路二:
该方案适用于主键自增的表,可以把limit查询转换成某个位置的查询。例如
select * from user where id > 2000000 limit 10;
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='张三';
对于访问数据库来说,建立连接的代价是比较昂贵的,因为我们频繁的创建关闭连接,是比较耗费资源的,我们有必要建立数据库连接池,以提高访问的性能。
避免对数据进行重复检索
在编写应用代码时,需要能够理清对数据库的访问逻辑。能够一次连接就获取到结果的,就不用两次连接,这样可以大大减少对数据库无用的重复请求。
增加cache层
在应用中,我们可以在应用中增加缓存层来达到减轻数据库负担的目的。缓存层有很多种,也有很多实现方式,只要能达到降低数据库的负担又能满足应用需求就可以。
因此可以部分数据从数据库中抽取出来放到应用端以文本方式存储,或者使用框架(mybatis)提供的一级缓存/二级缓存,或者使用redis数据库来缓存数据。
负载均衡是应用中使用非常普遍的一种优化方法,它的机制就是利用某种均衡算法,将固定的负载量分布到不同的服务器上,以此来降低单台服务器的负载,达到优化的效果。
利用MySQL复制分流查询
通过MySQL的主从复制,实现读写分离,使增删改操作走主节点,查询操作走从节点,从而可以降低单台服务器的读写压力。
采用分布式数据库架构
分布式数据库架构适合大数据量、负载高的情况,它有良好的拓展性和高可用性。通过在多台服务器之间分布数据,可以实现在多台服务器之间的负载均衡,提高访问效率。
开启MySQL的查询缓存,当执行完全相同的SQL语句的时候,服务器就会直接从缓存中读取结果,当数据被修改,之前的缓存会失效,修改比较频繁的表不适合做查询缓存。
SHOW VARIABLES LIKE 'have_query_cache';
SHOW VARIABLES LIKE 'query_cache_type';
SHOW VARIABLES LIKE 'query_cache_size';
SHOW STATUS LIKE 'Qcache%';
参数的含义:MySQL的查询缓存默认是关闭的,需要手动配置参数query_cache_type来开启查询缓存。query_cache_type该参数的可取值有三个:
在/usr/my.cnf配置中,增加以下配置:
# 开启MySQL的查询缓存
query_cache_type=1
配置完毕之后,重启服务即可生效;
然后就可以在命令行执行SQL语句进行验证,执行一条比较耗时的SQL语句,然后再多执行几次,查看后面几次的执行时间;获取通过查看查询缓存的缓存命中数,来判定是否走查询缓存。
可以在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;
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独占的,如果默认值设置太大,就会造成内存浪费。
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 server是多线程结构,包括后台线程和客户服务线程。多线程可以有效利用服务器资源,提高数据库的并发性能。在MySQL中,控制并发连接和线程的主要参数包括max_connections、back_log、thread_cache_size、table_open_cache。
采用max_connections控制允许连接到MySQL数据库的最大数量,默认值是151。如果状态变量connection_errors_max_connections不为零,并且一直增长,则说明不断有连接请求因数据库连接数已达到允许最大值而失败,这是可以考虑增大max_connections的值。
MySQL最大可支持的连接数,取决于很多因素,包括给定操作系统平台的线程库的质量、内存大小、每个连接的负荷、CPU的处理速度,期望的响应时间等。在Linux平台下,性能好的服务器,支持500-1000个连接不是难事,需要根据服务器性能进行评估设定。
back_log参数控制MySQL监听TCP端口时设置的积压请求栈大小。如果MySQL的连接数达到max_connections时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即back_log,如果等待连接的数量超过back_log,将不被授予连接资源,将会报错。5.6.6版本之前默认值为50,之后的版本默认为50+(max_connections/5),但最大不超过900。
如果需要数据库在较短的时间内处理大量连接请求,可以考虑适当增大back_log的值。
该参数用来控制所有SQL语句执行线程可打开表缓存的数量,而在执行SQL语句时,每一个SQL执行线程至少要打开1个表缓存。该参数的值应该根据设置的最大连接数max_connections以及每个连接执行关联查询中涉及的表的最大数量来设定。
为了加快连接数据库的速度,MySQL会缓存一定数量的客户服务线程以备重用,通过参数thread_cache_size可控制MySQL缓存客户服务线程的数量。
该参数是用来设置InnoDB事务等待行锁的时间,默认值是50ms,可以根据需要进行动态设置。对于需要快速反馈的业务系统来说,可以将行锁的等待时间调小,以避免事务长时间挂起;对于后台运行的批量处理程序来说,可以将行锁的等待时间调大,以避免发生大的回滚操作。
锁是计算机协调多个进程或线程并发访问某一资源的机制(避免争抢)。
在数据库中,除传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
从对数据操作的粒度分:
从对数据操作的类型分:
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,此值高说明存在着较为严重的表级锁争用情况。
行锁介绍
行锁特点:偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度很小,发生锁冲突的概率降低,并发度升高。
InnoDB与MyISAM的最大不同有两点:一是支持事务;二是采用了行级锁。
InnoDB的行锁模式
InnoDB实现了以下两种类型的行锁。
对于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%';
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>
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>
示例:
#查询用户表中姓名以‘张’开头的所有信息
select * from user where name regexp '^张';
#查询用户表中姓名以‘三’结尾的所有信息
select * from user where name regexp '三$';
#查询用户表中姓名包含‘张’或者‘李’或者‘王’的所有信息
select * from user where name regexp '[张李王]';
该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";
mysaladmin是一个执行管理操作的客户端程序。可以用它来检查服务器的配置和当前状态、创建并删除数据库等。
可以通过:mysqladmin --help 指令查看帮助文档
示例:
mysqladmin -uroot -p123456 create 'test1';
mysqladmin -uroot -p123456 drop 'test1';
mysqladmin -uroot -p123456 version;
由于服务器生成的二进制日志文件以二进制格式保存,所以如果想要检查这些文本的文本格式,就会使用到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:指定位置间隔内的所有日志。
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=# 指定连接端口
mysqlimport是客户端数据导入工具,用来导入mysqldump 加 -T 参数后导出的文本文件。
语法:
mysqlimport [options] db_name textfile1
示例:
mysqlimport -uroot -p123456 test /tmp/city.txt
如果需要导入sql文件,可以使用mysql中的source指令:
source /root/tb_book.sql
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中,有4种不同的日志,分别是错误日志、二进制日志、查询日志和慢查询日志,这些日志记录着数据库在不同方面的踪迹。
错误日志是MySQL中最重要的日志之一,它记录了当mysql启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时,可以首先查看此日志。
该日志是默认开启的,默认存放目录为mysql的数据目录(var/lib/mysql),默认的日志文件名为hostname.err(hostname是主机名)。
查看日志位置指令:
show variables like 'log_error%';
概述
二进制日志(BINLOG)记录了所有的DDL(数据定义语言)语句和DML(数据操纵语言)语句,但是不包括数据查询语句。此日志对于灾难时的数据恢复起着极其重要的作用,MySQL的主从复制,就是通过该binlog实现的。
二进制日志,默认情况下是没有开启的,需要到MySQL的配置文件中开启,并配置MySQL日志的格式。
配置文件位置:/usr/my.cnf
日志存放位置:配置时,给定了文件名但是没有指定路径,日志默认写入MySQL的数据目录。
#配置开启binlog日志,日志的文件前缀为mysqlbin -----> 生成的文件名如:mysqlbin.000001
log_bin=mysqlbin
#配置二进制日志的格式
binlog_format = STATEMENT
日志格式
STATEMENT
该日志格式在日志文件中记录的都是SQL语句(statement),每一条对数据进行修改的SQL都会记录在日志文件中,通过MySQL提供的mysqlbinlog工具,可以清晰的查看到每条语句的文本。主从复制的时候,从库(slave)会将日志解析为原文本,并在从库重新执行一次。
ROW
该日志格式在日志文件中记录的是每一行的数据变更,而不是记录SQL语句。比如,执行SQL语句:update tb_book set status=‘1’,如果是STATEMENT日志格式,在日志中会记录一行SQL语句;如果是ROW,由于是对全表进行更新,也就是每一行记录都会发生变更,ROW格式的日志中会记录每一行的数据变更。
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
删除日志
对于比较繁忙的系统,由于每天生成日志量大,这些日志如果长时间不清楚,将会占用大量的磁盘空间。下面我们将会讲解几种删除日志的常见方法:
通过Reset Master指令删除全部binlog日志,删除之后,日志编号将从xxxx.000001重新开始。
# 删除全部日志指令
Reset Master;
执行指令purge master logs to ‘mysqlbin.xxxx’,该命令将删除xxxx编号之前的所有日志;
执行指令purge master logs before ‘yyyy-mm-dd hh24:mi:ss’,该命令将删除日志为“yyyy-mm-dd hh24:mi:ss”之前产生的所有日志;
设置参数 --expire_logs_days=#,此参数的含义是设置日志的过期天数,过了指定的天数后日志将会被自动删除,这样将有利于减少DBA管理日志的工作量。配置如下:
log_bin=mysqlbin
binlog_format=ROW
--expire_logs_days=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
慢查询日志记录了所有执行时间超过参数 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
复制是指将主数据库的DDL和DML操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步。
MySQL支持一台主库同时向多台从库进行复制,从库同时也可以作为其他从服务器的主库,实现链状复制。
MySQL复制的优点主要包含以下三个方面:
master
在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
执行完毕之后,需要重启MySQL:
service mysql restart;
创建同步数据的账户,并且进行授权操作:
grant replication slave on *.* to 'jsuzjx'@'192.168.192.12' identified by 'jsuzjx';
flush privileges;
查看master状态:
show master status;
字段含义:
File:从哪个日志文件开始推送日志文件;
Position:从哪个位置开始推送日志;
Binlog_Ignore_DB:指定不需要同步的数据库。
slave
在slave端配置文件中,配置如下内容:
# mysql服务端ID,唯一
server-id=2
# 指定binlog日志
log-bin=/var/lib/mysql/mysqlbin
执行完毕之后,需要重启MySQL:
service mysql restart;
执行如下指令:
change master to master_host='192.168.192.1', master_user='jsuzjx', master_password='jsuzjx', master_log_file='mysqlbin.000001', master_log_pos=413;
开启同步操作:
start slave;
show slave status;