尚硅谷MySQL数据库高级,mysql优化,数据库优化
视频地址:https://www.bilibili.com/video/BV1KW411u7vy?share_source=copy_web

一、索引优化分析

Linux CentOS 7

MySQL 5.5.48

1. 初始表

CREATE TABLE `tbl_emp` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`deptId` int(11) DEFAULT NULL,
PRIMARY KEY (`id`) ,
KEY `fk_dept_id`(`deptId`)
)ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8;CREATE TABLE `tbl_dept` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`deptName` varchar(30) DEFAULT NULL,
`locAdd` varchar(40) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8;INSERT INTO tbl_dept(deptName,locAdd) VALUES('RD', 11);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('HR', 12);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('MK', 13);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('MIS', 14);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('FD', 15);INSERT INTO tbl_emp(NAME, deptId) VALUES('z3', 1);
INSERT INTO tbl_emp(NAME, deptId) VALUES('z4', 1);
INSERT INTO tbl_emp(NAME, deptId) VALUES('z5', 1);INSERT INTO tbl_emp(NAME, deptId) VALUES('w5', 2);
INSERT INTO tbl_emp(NAME, deptId) VALUES('w6', 2);INSERT INTO tbl_emp(NAME, deptId) VALUES('s7', 3);
INSERT INTO tbl_emp(NAME, deptId) VALUES('s8', 4);
INSERT INTO tbl_emp(NAME, deptId) VALUES('s9', 51);

2. Explain(查询执行计划)

可以模拟优化器执行 SQL 查询语句,从而知道 MySQL 是如何处理 SQL 语句的。分析查询语句或是表结构的性能瓶颈

  • 表的读取顺序
  • 数据读取操作的操作类型
  • 哪些索引可以使用
  • 哪些索引被实际使用
  • 表之间的应用
  • 每张表有多少行被优化器查询
2.1 查询执行计划包含的信息:
  • id:表示查询中执行select子句或操作表的顺序

    • id相同,执行顺序由上至下
    • id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
    • id有的相同,有的不同:相同的为一组,组内从上往下顺序执行,组之间id值越大,优先级越高
  • select_type:查询的类型,主要是用于区别普通查询、联合查询、子查询等的复杂查询

    • SIMPLE:简单的select查询,不含子查询或UNION
    • PRIMARY:查询中包含任何复杂的子部分,最外层查询则被标记为PRIMARY
    • SUBQUERY:子查询中的第一个SELECT,结果不依赖于外部查询
    • DERIVED:在FROM列表中包含的子查询被标记为DERIVED(衍生)。MySQL会递归执行这些子查询,把结果放在临时表里
    • UNION:若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
    • UNION RESULT:从UNION表中获取结果的SELECT
  • table:显示这一行的数据是关于哪些表的

  • type:访问类型

    • 从最好到最坏排序:system > const > eq_ref > ref > range > index > All

    • system:表只有一行记录(等于系统表),这是const类型的特例

    • const:表示通过索引一次就找到了,const用于比较primary key或则unique索引,只匹配到一行数据

      # const:primary key只匹配到一条
      # system:表只有一条
      mysql> explain select * from (select * from tbl_emp where id = 1)t;
      +----+-------------+------------+--------+-----+-----+-----+-----+-----+-----+
      | id | select_type | table      | type   | ... | ... | ... | ... | ... | ... |
      +----+-------------+------------+--------+-----+-----+-----+-----+-----+-----+
      |  1 | PRIMARY     | <derived2> | system | ... | ... | ... | ... | ... | ... |
      |  2 | DERIVED     | tbl_emp    | const  | ... | ... | ... | ... | ... | ... |
      +----+-------------+------------+--------+-----+-----+-----+-----+-----+-----+
      
    • eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描

      类似ref,区别就是使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,

      简单来说,就是多表连接中使用primary key或者 unique key作为关联条件

    • ref:非唯一性索引扫描,返回匹配某个单独值的所有行

      mysql> explain select * from tbl_emp where deptId = 1;
      +----+-------------+------------+--------+-----+-----+-----+-----+-----+-----+
      | id | select_type | table      | type   | ... | ... | ... | ... | ... | ... |
      +----+-------------+------------+--------+-----+-----+-----+-----+-----+-----+
      |  1 | SIMPLE      | tbl_emp    | ref    | ... | ... | ... | ... | ... | ... |
      +----+-------------+------------+--------+-----+-----+-----+-----+-----+-----+
      
    • range:只检索给定范围的行,使用一个索引来选择行

      mysql> explain select * from tbl_emp where id between 1 and 3;
      +----+-------------+------------+--------+-----+-----+-----+-----+-----+-----+
      | id | select_type | table      | type   | ... | ... | ... | ... | ... | ... |
      +----+-------------+------------+--------+-----+-----+-----+-----+-----+-----+
      |  1 | SIMPLE      | tbl_emp    | range  | ... | ... | ... | ... | ... | ... |
      +----+-------------+------------+--------+-----+-----+-----+-----+-----+-----+
      
    • index:与All区别为,index类型只遍历索引树(Full Index Scan:全索引扫描)

      mysql> explain select id from tbl_emp;
      +----+-------------+------------+--------+-----+-----+-----+-----+-----+-----+
      | id | select_type | table      | type   | ... | ... | ... | ... | ... | ... |
      +----+-------------+------------+--------+-----+-----+-----+-----+-----+-----+
      |  1 | SIMPLE      | tbl_emp    | index  | ... | ... | ... | ... | ... | ... |
      +----+-------------+------------+--------+-----+-----+-----+-----+-----+-----+
      
    • all:将遍历全表以找到匹配的行(Full Table Scan:全表扫描)

  • possible_keys:

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

    查询涉及到的字段上若存在索引,则该索引将被列出。但不一定被查询实际使用

  • key:

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

    查询中若使用了覆盖索引,则该索引仅出现在key列表中,不会出现在possible_keys列表中。(覆盖索引:查询的字段与建立的复合索引的个数一一吻合

  • key_len: 表示索引中使用的字节数,key_len越小 索引效果越好

    计算:

    varchr(10)变长字段且允许NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)+2(变长字段)
    varchr(10)变长字段且不允许NULL = 10 *( character set:utf8=3,gbk=2,latin1=1)+2(变长字段)

    char(10)固定字段且允许NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)
    char(10)固定字段且不允许NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)

  • ref: 显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值

    若使用常数等值查询,显示const;若连接查询,被驱动表ref显示驱动表的关联字段。

  • rows: 根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数。

  • Extra:包含不适合在其他列中显示但十分重要的额外信息。

    • Using filesort:MySQL中无法利用索引完成的排序操作成为“文件排序”

    • Using temporary:对查询结果排序时使用临时表。常见于排序order by和分组查询group by

    • Using index:使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错!

      如果同时出现using where,表明索引被用来执行索引键值的查找;

      如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。

    • Using where:表明使用了where过滤

    • Using join buffer:使用了连接缓存

    • impossible where:where子句的值总是false

3. 单表优化

3.1 建表
CREATE TABLE IF NOT EXISTS `article`(
`id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`author_id` INT (10) UNSIGNED NOT NULL,
`category_id` INT(10) UNSIGNED NOT NULL ,
`views` INT(10) UNSIGNED NOT NULL ,
`comments` INT(10) UNSIGNED NOT NULL,
`title` VARBINARY(255) NOT NULL,
`content` TEXT NOT NULL
);insert into `article`(author_id,category_id,views,comments,title,content) values
(1,1,1,1,'1','1'),
(2,2,2,2,'2','2'),
(1,1,3,3,'3','3');
3.2 查询

查询 category_id 为 1 且 comments 大于 1 的情况下,views 最多的 article_id

select id, author_id from article where category_id = 1 and comments > 1 order by views limit 1;
+----+-----------+
| id | author_id |
+----+-----------+
|  3 |         1 |
+----+-----------+
3.3 执行计划
explain select id, author_id from article where category_id = 1 and comments > 1 order by views limit 1;
+---+-------------+--------+------+---------------+----+---------+-----+-----+----------------------------+
|id | select_type | table  | type | possible_keys |key | key_len | ref |rows | Extra                      |
+---+-------------+--------+------+---------------+----+---------+-----+-----+----------------------------+
| 1 | SIMPLE      |article | ALL  | NULL          |NULL| NULL    |NULL |   3 | Using where;Using filesort |
+---+-------------+--------+------+---------------+----+---------+-----+-----+----------------------------+
3.4 优化(无ALL、filesort)
  1. 建立索引,查询索引
create index idx_article_ccv on article(category_id, comments, views);show index from article;
  1. 查询计划,ALL优化为range,但仍存在文件内排序(Using filesort)

    原因:范围(comments > 1)后的索引导致失效

  2. 删除不太合适的索引 idx_article_ccv,创建新的索引

drop index idx_article_ccv on article;
create index idx_article_cv on article(category_id, views);
  1. 查询计划,type 为 ref,Extra 无 Using filesort,

    检索 + 排序都用到了索引

explain select id, author_id from article where category_id = 1 and comments > 1 order by views limit 1;
+----+-------------+--------+------+----------------+----------------+---------+-------+------+------------+
| id | select_type | table  | type | possible_keys  | key            | key_len | ref   | rows | Extra      |
+----+-------------+--------+------+----------------+----------------+---------+-------+------+------------+
|  1 | SIMPLE      | article| ref  | idx_article_cv | idx_article_cv | 4       | const |    2 | Using where|
+----+-------------+--------+------+----------------+----------------+---------+-------+------+------------+

4. 两表优化

5. 三表优化

6. 索引优化

CREATE TABLE staffs(
id INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(24)NOT NULL DEFAULT'' COMMENT'姓名',
`age` INT NOT NULL DEFAULT 0 COMMENT'年龄',
`pos` VARCHAR(20) NOT NULL DEFAULT'' COMMENT'职位',
`add_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT'入职时间'
)CHARSET utf8 COMMENT'员工记录表';insert into staffs(NAME,age,pos,add_time) values('z3',22,'manager',NOW());
insert into staffs(NAME,age,pos,add_time) values('July',23,'dev',NOW());
insert into staffs(NAME,age,pos,add_time) values('2000',23,'dev',NOW());alter table staffs add index idx_staffs_nameAgePos(name, age, pos);
6.1 索引失效
  1. 最好按照索引全值匹配,虽然精度越高,花费越多,但完全使用索引

  2. 最佳左前缀法则

    如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列

  3. 不在索引列上作任何操作

    计算、函数、(自动or手动)类型转换,会导致索引失效而转向全表扫描

  4. 存储引擎不能使用索引中范围条件右边的列

    在使用范围条件后,根据 key_len 得知,只使用了索引的 name 和 age

  5. 尽量使用覆盖索引(只访问索引,索引列和查询列一致),减少select *

    按需求查询数据,最好与索引一致

    Extra中出现了 using index 最好

  6. 使用 !=、<> 时无法使用索引,导致全表扫描

  7. 使用 is null,is not null 也不能使用索引

  8. like 子句以通配符 % 开头,会索引失效,导致全表扫描

  • 解决like ‘%字符串%’ 时索引不被使用的方法:覆盖索引
CREATE TABLE tbl_user(
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) DEFAULT NULL,
`age`INT(11) DEFAULT NULL,
`email` VARCHAR(20) DEFAULT NULL,
PRIMARY KEY(`id`)
)ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;insert into tbl_user(NAME,age,email) values('1aa1',21,'b@163.com');
insert into tbl_user(NAME,age,email) values('2aa2',222,'a@163.com');
insert into tbl_user(NAME,age,email) values('3aa3',265,'c@163.com');
insert into tbl_user(NAME,age,email) values('4aa4',21,'d@163.com');

执行查询计划,无论怎么查询,都是全表扫描

建立索引,执行查询:

查询索引包含的列,使用了索引

create index idx_user_nameAge on tbl_user(name, age);

6.2 一般建议

全值匹配我最爱,最左前缀要遵守;

带头大哥不能死,中间兄弟不能断;

索引列上少计算,范围之后全失效;

LIKE百分写最右,覆盖索引不写星;

不等空值还有or,索引失效要少用;

VAR引号不可丢,SQL高级也不难!

7. 面试题练习

create table test03(
id int primary key not null auto_increment,
c1 char(10),
c2 char(10),
c3 char(10),
c4 char(10),
c5 char(10)
);insert into test03(c1,c2,c3,c4,c5) values('a1','a2','a3','a4','a5');
insert into test03(c1,c2,c3,c4,c5) values('b1','b2','b3','b4','b5');
insert into test03(c1,c2,c3,c4,c5) values('c1','c2','c3','c4','c5');
insert into test03(c1,c2,c3,c4,c5) values('d1','d2','d3','d4','d5');
insert into test03(c1,c2,c3,c4,c5) values('e1','e2','e3','e4','e5');

问题:创建复合索引 idx_test03_c1234,分析以下SQL的索引使用情况

create index idx_test03_c1234 on test03(c1,c2,c3,c4);
  1. 变换条件顺序,查询优化器会将命令自动调整优化

  2. 范围条件后失效,但查询优化器优化顺序后,范围条件被调整到后面

  3. c3 作用在了排序,而不是查找,出现断层

    有无 c4 都不会使用 c4 了

    排序情况下,没有 c3 ,索引出现断层,导致无法利用索引排序,出现了 Using filesort

  4. 使用 c1 一个索引,c2, c3 用于排序

    没有按照索引顺序排序,出现 Using filesort

  5. 用 c1, c2 两个字段索引,但 c2, c3 用于排序,无 filesort

  6. 排序颠倒,导致索引失效,但是存在前置 c2 常量,所以只剩下 c3 一个排序( c2 常量,只有一个不用排序)

    排序就相当于order by c3, ‘常量’

  7. 定值、范围还是排序,一般order by是给个范围

    group by 基本上都需要进行排序,会有临时表产生

  8. 一般性建议

  • 对于单键索引,尽量选择针对当前query过滤性更好的索引
  • 在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好
  • 在选择组合索引的时候,尽量选择可以能够包含当前query中的where字句中更多字段的索引
  • 尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的

二、 查询截取分析

  1. 慢查询的开启并捕获
  2. explain+慢SQL分析
  3. show profile查询SQL在Mysql服务器里面的执行细节和生命周期情况
  4. SQL数据库服务器的参数调优

10. 查询优化

优化原则:小表驱动大表,即小的结果集驱动大的结果集

  • B表的数据集 < A表数据集,in 优于 exists

    select * from A where id in (select id from B)
    等价于
    for select id from B
    for select * from A where A.id = B.id
    
  • A表的数据集 < B表的数据集,exists 优于 in

    select * from A where exists (select 1 from B where B.id = A.id)
    等价于
    for select * from A
    for select * from B where B.id = A.id
    
  • 【注意】A表与B表的ID字段应建立索引

  • exists:

    • select * from table where exists (subquery)
      
    • 该语法可以理解为:将主查询的数据,放到子查询中做条件验证,根据验证结果(TRUE或FALSE)来决定主查询的数据结果是否得以保留。

11. order by 排序优化

尽量使用 index 排序,避免使用 filesort 排序

create table tblA(
age int,
birth timestamp not null
);insert into tblA(age,birth) values(22,now());
insert into tblA(age,birth) values(23,now());
insert into tblA(age,birth) values(24,now());create index idx_A_ageBirth on tblA(age, birth);
11.1 两个案例
  1. 案例一:不满足最佳左前缀,导致 filesort

  2. 顺序正确,但同时存在升序降序,导致不能使用索引

11.2 filesort 排序

如果不在索引列上, filesort 有两种算法:双路排序,单路排序

  • 双路排序:MySQL 4.1 前使用,即扫描两次硬盘。

    1. 读取行 id 和order by列,放到 sort buffer(排序缓存) 进行排序
    2. 扫描排序缓存,按照其中的值,重新从列表中读取对应的数据(包括其他字段)输出。
  • 单路排序:

    1. 从磁盘读取查询需要的所有列,按照order by列在 sort buffer 进行排序,然后扫描排序后的列表进行输出
  • 总体上,单路排序比双路排序好。但在 sort buffer 中,单路排序占用更多空间(取出所有字段),可能取出的数据总量超出sort_buffer 容量,导致每次只能取 sort_buffer 容量的大小,进行排序(创建tmp文件,多路合并),排完再取sort_buffer容量大小,再次排序…从而多次I/O

  • 优化策略

    1. 增大 sort_buffer_size 参数的设置
    2. 增大 max_length_for_sort_data 参数的设置
    3. 尽量不执行select *,导致 sort_buffer 容量不足
  • 小总结

    -- MySQL两种排序方式:1.文件排序 2.扫描有序索引排序
    -- MySQL可以为排序和查询使用相同的索引-- 例:index_abc(a, b, c)
    -- order by 能使用索引最左前缀
    order by a
    order by a, b
    order by a, b, c
    order by a desc, b desc, c desc-- 如果where使用索引的最左前缀定义为常量,则order by能使用索引
    where a=const order by b, c
    where a=const, b=const order by c
    where a=const and b>const order by b, c-- 不能使用索引进行排序的情况
    order by a asc, b desc, c desc  /*排序不一致,要么都升,要么都降*/
    where g=const order by b, c        /*丢失a索引*/
    where a=const order by c       /*丢失b索引*/
    where a=const order by a, d        /*d不是索引的一部分*/
    where a in(...) order by b, c   /*对于排序,多个相等这种条件,也是范围*/
    

12. group by

  • group by实质是先排序后进行分组,遵照索引建的最佳左前缀。
  • 当无法使用索引列,增大 max_length_for_sort_data 参数的设置 + 增大sort_buffer_size参数的设置。
  • where高于having,能写在where限定的条件就不要去having限定了。

13. 慢查询日志

13.1 定义
  • MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阈值的语句。具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。
  • long_query_time的默认值是10,意思是运行10秒以上的语句。
  • 由它来查看哪些SQL超出了我们的最大忍耐时间值,比如一条sql执行超过5秒钟,我们就算慢SQL,希望能收集超过5秒的sql,结合之前的explain进行全面分析。
13.2 说明
  • 默认情况下,MySQL数据库没有开启慢查询日志,需要手动设置这个参数。
  • 如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响。慢查询日志支持将日志记录写入文件。
13.3 查看及开启

  • 使用 set global slow_query_log=1 语句开启慢查询日志,只对当前数据库生效,MySQL重启后则失效。

  • 如果永久生效,必须修改配置文件 my.cnf,

    在 [mysqld] 下新增或修改参数,系统默认缺省文件 localhost-slow.log

    slow_query_log=1
    slow_query_log_file=/var/lib/mysql/xxx-slow.log
    
  • long_query_time 判断快慢的标准,默认值为10s

    可以使用命令 / my.cnf修改

    判断标准为大于而不是大于等于

    设置后没有变化,需要:①重新开启会话 ②使用以下命令

  • 案例

    重新开启会话,模拟超时SQL

    select sleep(4);
    

    查看日志

    [root@localhost ~]# cd /var/lib/mysql/
    [root@localhost mysql]# cat localhost-slow.log
    /usr/sbin/mysqld, Version: 5.5.48-log (MySQL Community Server (GPL)). started with:
    Tcp port: 3306  Unix socket: /var/lib/mysql/mysql.sock
    Time                 Id Command    Argument
    # Time: 210815 11:59:05
    # User@Host: root[root] @ localhost []
    # Query_time: 4.002550  Lock_time: 0.000000 Rows_sent: 1  Rows_examined: 0
    use db0629;
    SET timestamp=1628999945;
    select sleep(4);
  • 查询当前系统中有多少条慢查询记录:

  • 以上设置也可以直接在 my.cnf 中配置

    slow_query_log=1
    slow_query_log_file=/var/lib/mysql/xxx-slow.log
    long_query_time=3
    log_output=FILE
    
  • 日志分析工具mysqldumpslow

    在生产环境中,如果要手工分析日志,查找、分析SQL,显然是个体力活,MySQL提供了日志分析工具mysqldumpslow。

    mysqldumpslow --help
    s:是表示按照何种方式排序c:访问次数I:锁定时间r:返回记录t:查询时间al:平均锁定时间ar:平均返回记录数at:平均查询时间
    t:即为返回前面多少条的数据
    g:后边搭配一个正则匹配模式,大小写不敏感
    

    工作常用参考

    -- 得到返回记录集最多的10个SQL
    mysqldumpslow -s r -t 10 /var/lib/mysql/localhost-slow.log-- 得到访问次数最多的10个SQL
    mysqldumpslow -s c -t 10 /var/lib/mysql/localhost-slow.log-- 得到按照时间顺序排序的前10条里面含有左连接的查询语句
    mysqldumpslow -s t -t 10 -g"left join" /var/lib/mysql/localhost-slow.log-- 使用时建议结合 | 和 more 使用,否则可能爆屏
    mysqldumpslow -s r -t 10 /var/lib/mysql/localhost-slow.log | more
    

14. 批量插入

  • 建表

    create table dept(
    id int unsigned primary key auto_increment,
    deptno mediumint unsigned not null default 0,
    dname varchar(20) not null default "",
    loc varchar(13) not null default ""
    )engine=innodb default charset=GBK;CREATE TABLE emp(
    id int unsigned primary key auto_increment,
    empno mediumint unsigned not null default 0,
    ename varchar(20) not null default "",
    job varchar(9) not null default "",
    mgr mediumint unsigned not null default 0,
    hiredate date not null,
    sal decimal(7,2) not null,
    comm decimal(7,2) not null,
    deptno mediumint unsigned not null default 0
    )ENGINE=INNODB DEFAULT CHARSET=GBK;
    
  • 设置参数 log_bin_trust_function_creators

    所以当开启二进制日志后,参数 log_bin_trust_function_creators 就会生效,限制存储函数的创建、修改、调用
    它控制是否可以信任存储函数创建者,不会创建写入二进制日志引起不安全事件的存储函数。

    永久设置需要配置文件 my.cnf

  • 创建函数

    -- 函数:随机字符串
    delimiter $$
    create function ran_string(n int) returns varchar(255)
    begin
    declare chars_str varchar(100) default 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    declare return_str varchar(255) default '';
    declare i int default 0;
    while i < n do
    set return_str = concat(return_str,substring(chars_str,floor(1+rand()*52),1));
    set i=i+1;
    end while;
    return return_str;
    end $$-- 函数:随机编号
    delimiter $$
    create function rand_num() returns int(5)
    begin
    declare i int default 0;
    set i=floor(100+rand()*10);
    return i;
    end $$
  • 创建存储过程

    -- 存储过程
    delimiter $$
    create procedure insert_emp(in start int(10),in max_num int(10))
    begin
    declare i int default 0;
    set autocommit = 0;
    repeat
    set i = i+1;
    insert into emp(empno,ename,job,mgr,hiredate,sal,comm,deptno) values((start+i),ran_string(6),'salesman',0001,curdate(),2000,400,rand_num());
    until i=max_num
    end repeat;
    commit;
    end $$-- 存储过程
    delimiter $$
    create procedure insert_dept(in start int(10),in max_num int(10))
    begin
    declare i int default 0;
    set autocommit = 0;
    repeat
    set i = i+1;
    insert into dept(deptno,dname,loc) values((start+i),ran_string(10),ran_string(8));
    until i=max_num
    end repeat;
    commit;
    end $$delimiter ;
    
  • 调用存储过程

    call insert_dept(100, 10);
    call insert_emp(100001, 500000);
    

15. Show Profile

  • 是mysql提供的可以用来分析当前会话中语句执行的资源消耗情况。可以用于SQL的调优的测量

  • 默认情况下,参数处于关闭状态,并保存最近15次的运行结果

  • 分析步骤

    1. 当前的mysql版本是否支持,开启(使用前需要开启)

    2. 运行sql

      select * from emp group by id%10 limit 150000;
      select * from emp group by id%20 order by 5;
      
    3. 查看结果,show profiles; 显示从开启功能到当前时间执行的SQL

    4. 诊断SQL:show profile cpu, block io for query [上一步前面的问题SQL数字号码];

      参数(可以查询的列)

      • ALL – 显示所有的开销信息
      • BLOCK IO – 显示块IO相关开销
      • CONTEXT SWITCHES – 上下文切换相关开销
      • CPU – 显示CPU相关开销
      • IPC – 显示发送和接收相关开销信息
      • MEMORY – 显示内存相关开销信息
      • PANG FAULTS – 显示错误页面相关开销信息
      • SOURCE – 显示和Source_function,Source_file,Source_line相关的开销信息
      • SWAPS – 显示交换次数相关的开销信息
    5. 日常开发需要注意的结论(Status列)

      • converting HEAP to MyISAM:查询结果太大,内存都不够用了往磁盘上搬了

      • Creating tmp table:创建临时表

        • 拷贝数据到临时表
        • 用完再删除
      • Copying to tmp table on disk:把内存中临时表复制到磁盘,危险!!

      • locked

16. 全局查询日志

  • 配置启用

    # 在mysql的my.cnf,设置如下
    # 开启
    general_log=1# 记录日志文件路径
    general_log_file=/path/logfile# 输出格式
    log_output=FILE
    
  • 编码启用

    set global general_log=1;
    set global log_output='TABLE';
    
  • 此后,编写的所有 sql 语句,将会记录到 mysql 库的 general_log 表,查看命令

    select 8 from mysql.general_log;
    

  • 永远不要在生产环境开启这个功能!!!

三、MySQL锁机制

  • 定义:锁是计算机协调多个进程并发访问某一资源的机制

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

  • 锁的分类

    • 从对数据库操作的类型(读/写)

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

17. 三锁

  • 开销、加锁速度、死锁、粒度、并发性能
  • 只能就具体应用的特点来说那种锁更合适

18 表锁(偏读)

  • 特点:偏向MyISAM存储引擎,开销小,加锁快;无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

  • 案例分析

    • 建表

      create table mylock (
      id int not null primary key auto_increment,
      name varchar(20) default ''
      ) engine myisam;insert into mylock(name) values('a');
      insert into mylock(name) values('b');
      insert into mylock(name) values('c');
      insert into mylock(name) values('d');
      insert into mylock(name) values('e');
    • 查看表上加过的锁

      show open tables;
      

      手动增加表锁

      lock table mylock read(write), emp read(write);
      

      释放表锁

      unlock tables;
      

18.2 读锁

使用两个会话测试

session_1 session_2
给 mylock 表加读锁 连接终端
当前Session不能查询其他没有锁定的表(负债没还) 其他session可以查询该表,也可以查询其他表
当前session插入或更新锁定的表报错 其他session插入或更新锁定的表,一直等待获得锁
释放锁 session获得锁,完成操作
18.3 写锁
session_1 session_2
给 mylock 表加写锁 连接终端
当前session对加锁表的查询、更新、插入操作都可以,但不能操作其他表 其他session对锁定表的查询被阻塞,需要等待锁的释放
释放锁 session2获得锁,查询返回结果
18.3 案例结论

MyISAM 在执行查询语句(select)前,会自动给涉及的的所有表加读锁,在执行增删改操作前,会自定给涉及的表加写锁

MysqL的表级锁有两种模式:

  • 表共享读锁(Table Read Lock)
  • 表独占写锁(Table Write Lock)
锁类型 可否兼容 读锁 写锁
读锁
写锁

结论:对MyISAM表进行操作,会有以下情况

  1. 对 MyISAM 表的读操作(加读锁),不会阻塞其他进程对同一表的读请求,但会阻塞其他进程对同一表的写请求。只有读锁释放后,才会执行其他进程的写操作
  2. 对 MyISAM 表的写操作(加写锁),会阻塞其他进程对同一表的读和写操作,事由当写锁释放后,才会执行其他进程的读和写操作。

简而言之,就是读锁会阻塞写,但是不会阻塞读。而写锁则会把读和写都阻塞。

18.4 表锁分析
  • 查看被加锁的表:show open tables;
  • 分析表锁定:可以通过检查 table_locks_waitedtable_locks_immediate 状态变量来分析系统上的表锁定。
    • show status like ‘table%’;
      

    • 这里有两个状态变量记录MySQL内部表级锁定的情况,两个变量的说明如下:

      • Table_locks_immediate:产生表级锁定的次数,表示可以立即获取锁的查询次数,每立即获取锁值加1;
      • Table_locks_waited:出现表级锁定争用而发生等待的次数(不能立即获取锁的次数,每等待一次锁值加1),此值高则说明存在着较严重的表级锁争用情况。
    • 此外,MyISAM的读写锁调度是写优先,这也是MyISAM不适合做写为主表的引擎。因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞。

19 行锁(偏写)

  • 偏向 Innodb 存储引擎,开销大,加锁慢;会出现死锁;

  • 锁定粒度小,发生锁冲突的概率最低,并发度也最高。

  • Innodb与MyISAM的最大不同有两点:

    • 支持事务(TRANSACTION)

    • 采用了行级锁

      事务(Transaction)及其ACID属性:

      原子性(Atomicity):事务是一个原子操作单元,对数据的修改,要么都执行,要么不执行;

      一致性(Consistent):在事务开始之前和事务结束以后,数据库的完整性没有被破坏,即事务结束时,内部数据结构都必须是正确的;

      隔离性(Isolation):数据库系统提供隔离机制,保证并发执行的事务互不干扰,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。

      持久性(Durable):事务完成之后,对于数据的修改是永久的。

  • 并发事务处理的问题

    • 更新丢失(Lost Update)

      两个事务操作同一行,基于初始值修改,由于不知道对方存在,就发生丢失更新,最后的修改覆盖其他事务的修改。

    • 脏读(Dirty Reads)

      事务A修改一条数据,在提交前,事务B读取这条数据并修改,事务A回滚,则事务B读取的数据无效,发生脏读。

    • 不可重复读(Non-Repeatable Reads)

      事务A两次读取数据到的数据,发生了改变、被删除(事务A读取到了事务B已经提交的修改数据,不符合隔离性。)

    • 幻读(Phantom Reads)

      事务A按照同一条件查询出了新数据。(事务A读取到了事务B提交的新增数据,不符合隔离性)

  • 事务隔离级别

    事务隔离级别 读数据一致性 脏读 不可重复读 幻读
    未提交读(read-uncommitted) 最底级别,保证不读取物理上损坏的数据
    已提交读(read-committed) 语句级
    可重复读(repeatable-read) 事务级
    串行化(serializable) 最高级别,事务级

    事务隔离级别越高,并发副作用越小,代价越大

    查看当前数据库隔离级别

    show variables like 'tx_isolation';
    

  • 案例分析

    • 建表

      create table test_innodb_lock(a int(11),b varchar(16))engine=innodb;insert into test_innodb_lock values(1,'b2');
      insert into test_innodb_lock values(3,'3');
      insert into test_innodb_lock values(4,'4000');
      insert into test_innodb_lock values(5,'5000');
      insert into test_innodb_lock values(6,'6000');
      insert into test_innodb_lock values(7,'7000');
      insert into test_innodb_lock values(8,'8000');
      insert into test_innodb_lock values(9,'9000');
      insert into test_innodb_lock values(1,'b1');-- a,b列建立单值索引(区别于复合索引)
      create index test_innodb_a_ind on test_innodb_lock(a);
      create index test_innodb_lock_b_ind on test_innodb_lock(b);select * from test_innodb_lock;
19.1 行锁案例
session_1 session_2
关闭自动提交 关闭自动提交
更新一条记录,并查询(执行commit提交数据) 查询同一个表,(执行commit后数据才一致)
更新不提交(不执行commit) 被阻塞,只能等待
提交commit(查询结果40002) 解除阻塞,更新成功(查询结果40003)
提交commit(两边查询结果都40003)
session_1更新第4条 session_2更新第9条
操作不同行,没有冲突
19.2 行锁升级为表锁
  • 使用强制类型转换,导致索引失效
session_1 session_2
修改第4条,导致索引失效 升级为表锁,产生阻塞
提交 阻塞解除,完成操作
查询 提交,查询
19.3 间隙锁危害
session_1 session_2
当前表内没有 a = 2 的数据,但在 a 列有单值索引
修改数据,成功,没有问题 按照行锁原则,操作其他行不会影响,但是出现阻塞
commit 提交修改 阻塞解除,完成插入
提交新增
  • 间隙锁

    当用范围条件而不是相等条件去索引数据,并请求共享或排他锁时,InnoDB引擎会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录(a = 2),叫做“间隙(GAP)”。InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。

  • 危害

    • Query执行过程中通过范围查找的话,会锁定整个范围内所有的索引键值,即使这个键值并不存在。
    • 间隙锁致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值(a = 2)也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能造成很大的危害。

20 面试题

如何锁定一行(不需设置 autocommit = 0)

session_1 session_2
设置起点,执行语句后缀 for update
提交

21. 案例结论

  • 结论

    • InnoDB存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会更高一些,但是在整体并发处理能力方面要远远优于 MyISAM 的表级锁定的。当系统并发量较高的时候,InnoDB的整体性能和MyISAM相比就会有比较明显的优势了。
    • InnoDB的行级锁定同样也有其脆弱的一面,当我们使用不当的时候,可能会让InnoDB的整体性能表现不仅不能比MyISAM高,甚至可能会更差。
  • 行锁分析

    • 通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况

      show status like ‘innodb_row_lock%’;
      

    • 状态量的说明

      • Innodb_row_lock_current_waits:当前正在等待锁定的数量;
      • innodb_row_lock_time:从系统启动到现在锁定总时间长度;
      • innodb_row_lock_time_avg:每次等待所花平均时间;
      • innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花的时间;
      • innodb_row_lock_waits:系统启动后到现在总共等待的次数。
    • 比较重要的是

      • innodb_row_lock_time_avg(等待平均时长)

      • innodb_row_lock_waits(等待总次数)

      • innodb_row_lock_time(等待总时长)

        这三项,尤其是当等待总次数很高,而且等待平均时长也不小的时候,需要分析系统中为什么会有如此多的等待,然后根据分析结果着手制定优化计划

    • 优化建议

      • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
      • 合理设计索引,尽量缩小锁的范围。
      • 尽可能减少索引条件,避免间隙锁
      • 尽量控制事务大小,减少锁定资源量和时间长度。
      • 尽可能低级别事务隔离

MySQL数据库优化笔记相关推荐

  1. MySQL数据库学习笔记(九)----JDBC的ResultSet接口(查询操作)、PreparedStatement接口重构增删改查(含SQL注入的解释)...

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  2. 【转载】运维角度浅谈MySQL数据库优化

     运维角度浅谈MySQL数据库优化 2015-06-02 14:22:02 标签:mysql优化   mysql分库分表分区 mysql读写分离 mysql主从复制 原创作品,允许转载,转载时请务必以 ...

  3. linux数据库创建score表,MySQL数据库学习笔记

    MySQL数据库学习笔记phpma (实验环境:Redhat9.0,MySQL3.23.54) 纲要: 一,连接MySQL phpma 二,MySQL管理与授权 三,数据库简单操作 四, 数据库备份 ...

  4. mysql数据库优化课程---16、mysql慢查询和优化表空间

    mysql数据库优化课程---16.mysql慢查询和优化表空间 一.总结 一句话总结: a.慢查询的话找到存储慢查询的那个日志文件 b.优化表空间的话可以用optimize table sales; ...

  5. MySQL数据库学习笔记(十二)----开源工具DbUtils的使用(数据库的增删改查)

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  6. mysql数据库优化课程---15、mysql优化步骤(mysql中最常用最立竿见影的优化是什么)...

    mysql数据库优化课程---15.mysql优化步骤(mysql中最常用最立竿见影的优化是什么) 一.总结 一句话总结:索引优化最立竿见影 索引优化:不然有多少行要扫描多少次,1亿行大概是5到10分 ...

  7. mysql数据库优化大全_MySQL数据库优化技巧大全

    简介: MySQL数据库优化技巧大全 MySQL优化三大方向 ① 优化MySQL所在服务器内核(此优化一般由运维人员完成). ② 对MySQL配置参数进行优化(my.cnf)此优化需要进行压力测试来进 ...

  8. mysql数据库优化课程---6、mysql结构化查询语言有哪些

    mysql数据库优化课程---6.mysql结构化查询语言有哪些 一.总结 一句话总结:主要分为四类 1.DCL 数据控制语言 1)grant 2)commit 3)rollback 2.DDL 数据 ...

  9. MySQL数据库优化技巧大全

    简介:MySQL数据库优化技巧大全 MySQL优化三大方向 ① 优化MySQL所在服务器内核(此优化一般由运维人员完成). ② 对MySQL配置参数进行优化(my.cnf)此优化需要进行压力测试来进行 ...

最新文章

  1. SSAS使用时间智能解决本年累计、同比、环比【转载】
  2. winx官方站点改版了!
  3. C#中Socket通信编程的同步实现
  4. 可以自定义模板的ide_将IDE检查应用于自定义Java批注
  5. mui ajax 文件上传,MUI的图片上传和压缩
  6. 算法导论 思考题1-1
  7. fgetc和fputc函数
  8. java中ejb项目_创建EJB项目
  9. C++入门教程(23)深度优先,广度优先(连连看判断是否可以消除)
  10. 西门子触摸屏脚本程序_西门子PLC触摸屏如何编写程序
  11. mysql 完整卸载教程_彻底卸载MySQL数据库教程
  12. 新东方托福词汇(List 16 ~ List 20)
  13. 用html制作四种九九乘法表,JavaScript制作九九乘法表
  14. python画图系列整理
  15. ORACLE ERP consolidation流程(二)
  16. 作为APIcaller识别发票信息
  17. 经典语录,至理名言,人生百态
  18. qpython androidhelper_安卓运行Python的神器:QPython
  19. 机器学习定义及基本术语(根据周志华的《机器学习》概括)
  20. mysql 查询数据库ip_如何查看连接MYSQL数据库的IP信息

热门文章

  1. 当PPP遇上智慧城市
  2. 计算机 游戏72攻略,探灵游戏攻略大全 探灵全关卡通关攻略
  3. 什么是威胁情报(Threat Intelligence)
  4. 智慧工程安监物联网+云平台解决方案-最新全套文件
  5. geoserver自定义插件_GIS篇 GeoServer个人笔记
  6. 马蜂窝容器化平台前端赋能实践
  7. 【直播预约】数据库OBCP认证全面升级公开课
  8. 开启联想电脑管家电池养护模式的操作步骤
  9. QT4.8.7 打开Mupdf
  10. 武忠祥每日一题 (幂级数的收敛区间和收敛域)