Featured image of post Mysql高级

Mysql高级

Mysql高级学习文档

mysql的架构介绍

mysql简介
mysqllinux版安装
mysql配置文件
mysql逻辑架构介绍
mysql存储引擎

索引优化分析

性能下降sql慢是执行时间长还是等待时间长
常见通用的join查询
索引简介
性能分析
索引优化

查询截取分析

查询优化
慢查询日志
批量数据脚本
show profile
全局查询日志

mysql锁机制

锁的分类

主从复制

主从配置
mysql简介

高级mysql 完整的mysql优化

  • mysql内核
  • sql优化攻城狮
  • mysql服务器的优化
  • 各种参数常量设定
  • 查询语句优化
  • 主从复制
  • 软硬件升级
  • 容灾备份
  • sql编程
mysqllinux版的安装

image-20211012195826053
image-20211012195826053

#查看当前ubuntu版本
yourtreedad@yourtreedad:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.3 LTS
Release:        20.04
Codename:       focal
yourtreedad@yourtreedad:~$

准备工作

#切换到root账户
yourtreedad@yourtreedad:~$ sudo passwd root
New password:
Retype new password:
passwd: password updated successfully
yourtreedad@yourtreedad:~$ su root
Password:
root@yourtreedad:/home/yourtreedad#

#查看电脑里有没有mysql
root@yourtreedad:/home/yourtreedad# service mysql status
 * MySQL is stopped.
root@yourtreedad:/home/yourtreedad# mysql --version
mysql  Ver 8.0.26-0ubuntu0.20.04.2 for Linux on x86_64 ((Ubuntu))
root@yourtreedad:/home/yourtreedad# service mysql start
 * Starting MySQL database server mysqld                                                 su: warning: cannot change directory to /nonexistent: No such file or directory

#那就彻底卸载了
root@yourtreedad:/home/yourtreedad# sudo apt-get remove mysql-*
root@yourtreedad:/home/yourtreedad# sudo rm -rf /etc/mysql/
root@yourtreedad:/home/yourtreedad# dpkg -l |grep ^rc|awk '{print $2}' |sudo xargs dpkg -P
dpkg: error: --purge needs at least one package name argument
#如果出现这个错误 dpkg: error: --purge needs at least one package name argument,说明已经清空完毕了

#全部删除了
root@yourtreedad:/home/yourtreedad# service mysql status
mysql: unrecognized service
root@yourtreedad:/home/yourtreedad# mysql --version
bash: /usr/bin/mysql: No such file or directory
root@yourtreedad:/home/yourtreedad#

#更新到最新 安装rpm
root@yourtreedad:/opt# apt-get update
root@yourtreedad:/home/yourtreedad# apt-get upgrade
root@yourtreedad:/home/yourtreedad# apt-get install rpm

#使用rpm判断当前系统是否安装过mysql
root@yourtreedad:/home/yourtreedad# rpm -qa|grep -i mysql

#安装mysql
root@yourtreedad:/opt# sudo apt-get install mysql-server mysql-client

#查看是否安装成功
root@yourtreedad:/# id yourtreedad
uid=1000(yourtreedad) gid=1000(yourtreedad) groups=1000(yourtreedad),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),117(netdev)
root@yourtreedad:/# cat /etc/passwd|grep mysql
mysql:x:112:119:MySQL Server,,,:/var/lib/mysql/:/bin/false
root@yourtreedad:/# cat /etc/group|grep mysql
mysql:x:119:
root@yourtreedad:/# mysqladmin --version
mysqladmin  Ver 8.0.26-0ubuntu0.20.04.3 for Linux on x86_64 ((Ubuntu))
root@yourtreedad:/#

root@yourtreedad:/# service mysql start

root@yourtreedad:/# ps -ef|grep mysql
mysql    18675     1  0 21:03 ?        00:00:00 /bin/sh /usr/bin/mysqld_safe
mysql    18822 18675  0 21:03 ?        00:00:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --log-error=/var/log/mysql/error.log --pid-file=yourtreedad.pid
root     19055 16743  0 21:14 tty1     00:00:00 grep --color=auto mysql

https://www.cnblogs.com/duolamengxiong/p/13650684.html

mysql启停和自动运行
#查看运行时间
root@yourtreedad:/home/yourtreedad# top
top - 21:17:30 up  1:21,  0 users,  load average: 0.52, 0.58, 0.59
Tasks:   8 total,   1 running,   7 sleeping,   0 stopped,   0 zombie
%Cpu(s):  4.0 us,  2.9 sy,  0.0 ni, 92.7 id,  0.0 wa,  0.4 hi,  0.0 si,  0.0 st
MiB Mem :  32674.8 total,  21949.1 free,  10501.7 used,    224.0 buff/cache
MiB Swap:  38400.2 total,  38322.8 free,     77.3 used.  22042.5 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
    1 root      20   0    8944    328    288 S   0.0   0.0   0:00.18 init
16725 root      20   0    8952    232    184 S   0.0   0.0   0:00.00 init
16726 yourtre+  20   0   18096   3588   3484 S   0.0   0.0   0:00.13 bash
18675 mysql     20   0   10656    812    780 S   0.0   0.0   0:00.10 mysqld_safe
18822 mysql     20   0 2603072 299920  20628 S   0.0   0.9   0:00.75 mysqld
19087 root      20   0   18412   2592   2568 S   0.0   0.0   0:00.03 su
19088 root      20   0   17008   2396   2304 S   0.0   0.0   0:00.07 bash
19201 root      20   0   18924   2152   1528 R   0.0   0.0   0:00.04 top

#直接mysql 就进来了
root@yourtreedad:/home/yourtreedad# mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 24

mysql默认没有密码,所以我们这里咩有输入密码就直接连上了

设置登陆密码

root@yourtreedad:/home/yourtreedad# /usr/bin/mysqladmin -u root password 123456
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
Warning: Since password will be sent to server in plain text, use ssl connection to ensure password safety.

image-20211012213458570
image-20211012213458570

mysql> create database student ;
Query OK, 1 row affected (0.01 sec)

mysql> exit
Bye
root@yourtreedad:/var/lib/mysql# ls -l
total 193956
-rw-r----- 1 mysql mysql   196608 Oct 12 21:33 '#ib_16384_0.dblwr'
-rw-r----- 1 mysql mysql  8585216 Oct 12 20:19 '#ib_16384_1.dblwr'
drwxr-x--- 1 mysql mysql      512 Oct 12 21:03 '#innodb_temp'
-rw-r----- 1 mysql mysql       56 Oct 12 20:19  auto.cnf
-rw-r----- 1 mysql mysql 12582912 Oct 12 21:33  ibdata1
-rw-r----- 1 mysql mysql 12582912 Oct 12 21:03  ibtmp1
drwxr-x--- 1 mysql mysql      512 Oct 12 20:19  mysql
-rw-r----- 1 mysql mysql 25165824 Oct 12 21:33  mysql.ibd
drwxr-x--- 1 mysql mysql      512 Oct 12 21:33  student
drwxr-x--- 1 mysql mysql      512 Oct 12 20:19  sys

修改配置文件位置

image-20211012213843009
image-20211012213843009

弄好了重启mysql

mysql> insert into user values (2,'张三');
Query OK, 1 row affected (0.00 sec)

mysql> select * from user ;
+----+--------+
| id | name   |
+----+--------+
|  1 | z3     |
|  2 | 张三   |
+----+--------+
2 rows in set (0.00 sec)

#有时候字符集没弄好 要弄成utf-8
mysql> show variables like '%char%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8mb4                    |
| character_set_connection | utf8mb4                    |
| character_set_database   | utf8mb4                    |
| character_set_filesystem | binary                     |
| character_set_results    | utf8mb4                    |
| character_set_server     | utf8mb4                    |
| character_set_system     | utf8mb3                    |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.01 sec)

修改字符集和数据存储路径

必须在字符集修改之后才能支持中文,之前的数据库好像不行

mysql配置文件
  • 二进制日志 log - bin
    • 主从复制
  • 错误日志
    • mysqlerror.error
  • 查询日志log
    • 默认关闭,记录查询的sql语句,如果开启会减低mysql的整体性能
  • 数据文件
    • 两个系统不一致
      • windows mysql5.0\data目录下有很多库
      • linux 默认路径 /var/lib/mysql
    • frm文件 存放表结构
    • myd文件 存放表数据
    • myi文件 存放表索引
mysql逻辑架构介绍

image-20211014205109147
image-20211014205109147

  • 第一层:连接层

最上层是一些客户端和连接服务,包含本地sock通信和大多数基于客户端/服务端工具实现的类似于tcp/ip的通信.主要完成一些类似于连接处理/授权认证及相关的安全方案.在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程.同样在该层上可以实现基于ssl的安全连接.服务器也会为安全接入的每个客户端验证它所具有的操作权限.

  • 第二层:数据库连接池 备份恢复安全集群容灾 存储过程 视图 触发器 解析器 优化器 缓存

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

  • 第三层:可插拔存储引擎

存储引擎层,存储引擎真正的负责了mysql中数据的存储和提取,服务器通过api与存储引擎进行通信.不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取.后面介绍myisam和innodb

  • 第四层:文件系统和文件日志

数据存储层,主要是将数据存储在运行于裸设备的文件系统上,并完成与存储引擎的交互

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

mysql存储引擎
mysql> show engines ;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                        | Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |
| MRG_MYISAM         | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |
| CSV                | YES     | CSV storage engine                                             | NO           | NO   | NO         |
| FEDERATED          | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                             | NO           | NO   | NO         |
| MyISAM             | YES     | MyISAM storage engine              

#查看存储引擎
mysql> show variables like '%storage_engine%' ;
+---------------------------------+-----------+
| Variable_name                   | Value     |
+---------------------------------+-----------+
| default_storage_engine          | InnoDB    |
| default_tmp_storage_engine      | InnoDB    |
| disabled_storage_engines        |           |
| internal_tmp_mem_storage_engine | TempTable |
+---------------------------------+-----------+

image-20211014211207589
image-20211014211207589

image-20211014211324759
image-20211014211324759

索引优化

优化分析

性能下降sql慢/执行时间长/等待时间长

  • 查询语句写的烂
  • 索引失效
    • 单值
    • 复合
#创建索引
mysql> create index idx_beauty_name on beauty(name);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> create index idx_beauty_name_sex on beauty(name,sex);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

#查看索引
mysql> show index from beauty ;
+--------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table  | Non_unique | Key_name            | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+--------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| beauty |          0 | PRIMARY             |            1 | id          | A         |          13 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| beauty |          1 | idx_beauty_name     |            1 | name        | A         |          12 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| beauty |          1 | idx_beauty_name_sex |            1 | name        | A         |          12 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| beauty |          1 | idx_beauty_name_sex |            2 | sex         | A         |          12 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
+--------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
  • 关联查询太多join(设计缺陷或不得已的需求)
  • 服务器调优以各个参数设置(缓冲/线程数)
常见的join查询
  • sql的执行顺序

    • 手写 select from join on where group by having order by limit
    • 机读 from on join where group by having select dinstinct order by limit

    image-20211014213300494
    image-20211014213300494

  • join 图

image-20211014213503395
image-20211014213503395

image-20211014213909722
image-20211014213909722

mysql> 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 DEFAULT CHARSET=utf8;
Query OK, 0 rows affected, 2 warnings (0.02 sec)

mysql>   CREATE TABLE tbl_emp (
    -> id INT(11) NOT NULL AUTO_INCREMENT,
    -> name VARCHAR(20) DEFAULT NULL,
    -> deptld INT(11) DEFAULT NULL,
    -> PRIMARY KEY(id),
    -> KEY fk_dept_id(deptld)
    -> #CONSTRAINT fk_dept_id FOREIGNKEY(deptld) REFERENCES tbl_dept(id)
    -> )ENGINE INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Query OK, 0 rows affected, 3 warnings (0.02 sec)

mysql> select * from tbl_emp ;
+----+------+--------+
| id | name | deptld |
+----+------+--------+
|  1 | z3   |      1 |
|  2 | z4   |      1 |
|  3 | z5   |      1 |
|  4 | w5   |      2 |
|  5 | w6   |      2 |
|  6 | s7   |      3 |
|  7 | s8   |      4 |
|  8 | s9   |     51 |
+----+------+--------+
8 rows in set (0.00 sec)

mysql> select * from tbl_dept ;
+----+----------+--------+
| id | deptName | locAdd |
+----+----------+--------+
|  1 | RD       | 11     |
|  2 | HR       | 12     |
|  3 | MK       | 13     |
|  4 | MIS      | 14     |
|  5 | FD       | 15     |
+----+----------+--------+
5 rows in set (0.00 sec)

#内连接 就么有8员工  5部门
mysql> select e.* , d.* from tbl_emp e join tbl_dept d on e.deptld = d.id ;
+----+------+--------+----+----------+--------+
| id | name | deptld | id | deptName | locAdd |
+----+------+--------+----+----------+--------+
|  1 | z3   |      1 |  1 | RD       | 11     |
|  2 | z4   |      1 |  1 | RD       | 11     |
|  3 | z5   |      1 |  1 | RD       | 11     |
|  4 | w5   |      2 |  2 | HR       | 12     |
|  5 | w6   |      2 |  2 | HR       | 12     |
|  6 | s7   |      3 |  3 | MK       | 13     |
|  7 | s8   |      4 |  4 | MIS      | 14     |
+----+------+--------+----+----------+--------+

#左外连接 没有5部门
mysql> select e.* , d.* from tbl_emp e left join tbl_dept d on e.deptid = d.id ;
+----+------+--------+------+----------+--------+
| id | name | deptid | id   | deptName | locAdd |
+----+------+--------+------+----------+--------+
|  1 | z3   |      1 |    1 | RD       | 11     |
|  2 | z4   |      1 |    1 | RD       | 11     |
|  3 | z5   |      1 |    1 | RD       | 11     |
|  4 | w5   |      2 |    2 | HR       | 12     |
|  5 | w6   |      2 |    2 | HR       | 12     |
|  6 | s7   |      3 |    3 | MK       | 13     |
|  7 | s8   |      4 |    4 | MIS      | 14     |
|  8 | s9   |     51 | NULL | NULL     | NULL   |
+----+------+--------+------+----------+--------+

#右外连接 没有8员工
mysql> select e.* , d.* from tbl_emp e right join tbl_dept d on e.deptid = d.id ;
+------+------+--------+----+----------+--------+
| id   | name | deptid | id | deptName | locAdd |
+------+------+--------+----+----------+--------+
|    1 | z3   |      1 |  1 | RD       | 11     |
|    2 | z4   |      1 |  1 | RD       | 11     |
|    3 | z5   |      1 |  1 | RD       | 11     |
|    4 | w5   |      2 |  2 | HR       | 12     |
|    5 | w6   |      2 |  2 | HR       | 12     |
|    6 | s7   |      3 |  3 | MK       | 13     |
|    7 | s8   |      4 |  4 | MIS      | 14     |
| NULL | NULL |   NULL |  5 | FD       | 15     |
+------+------+--------+----+----------+--------+
 
#只有左边 只有8员工
mysql> select e.* , d.* from tbl_emp e left join tbl_dept d on e.deptid = d.id where d.id is null ;
+----+------+--------+------+----------+--------+
| id | name | deptid | id   | deptName | locAdd |
+----+------+--------+------+----------+--------+
|  8 | s9   |     51 | NULL | NULL     | NULL   |
+----+------+--------+------+----------+--------+

#只有右边 只有5部门
mysql> select e.* , d.* from tbl_emp e right join tbl_dept d on e.deptid = d.id where e.deptid is null ;
+------+------+--------+----+----------+--------+
| id   | name | deptid | id | deptName | locAdd |
+------+------+--------+----+----------+--------+
| NULL | NULL |   NULL |  5 | FD       | 15     |
+------+------+--------+----+----------+--------+
1 row in set (0.00 sec)

#全连接 都得有 mysql不支持outer join
mysql> select e.* , d.* from tbl_emp e outer join tbl_dept d on e.deptid = d.id;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'outer join tbl_dept d on e.deptid = d.id' at line 1

#可以这么做  union默认去重  union all 就不会去重 
mysql> select e.* , d.* from tbl_emp e left join tbl_dept d on e.deptid = d.id union select e.* , d.* from tbl_emp e right join tbl_dept d on e.deptid = d.id;
+------+------+--------+------+----------+--------+
| id   | name | deptid | id   | deptName | locAdd |
+------+------+--------+------+----------+--------+
|    1 | z3   |      1 |    1 | RD       | 11     |
|    2 | z4   |      1 |    1 | RD       | 11     |
|    3 | z5   |      1 |    1 | RD       | 11     |
|    4 | w5   |      2 |    2 | HR       | 12     |
|    5 | w6   |      2 |    2 | HR       | 12     |
|    6 | s7   |      3 |    3 | MK       | 13     |
|    7 | s8   |      4 |    4 | MIS      | 14     |
|    8 | s9   |     51 | NULL | NULL     | NULL   |
| NULL | NULL |   NULL |    5 | FD       | 15     |
+------+------+--------+------+----------+--------+
9 rows in set (0.00 sec)

#不要join重合的部分 要另外两半的
mysql> select e.* , d.* from tbl_emp e left join tbl_dept d on e.deptid = d.id where d.id is null union select e.* ,d.* from tbl_emp e right join tbl_dept d on e.deptid = d.id where e.deptid is null ;
+------+------+--------+------+----------+--------+
| id   | name | deptid | id   | deptName | locAdd |
+------+------+--------+------+----------+--------+
|    8 | s9   |     51 | NULL | NULL     | NULL   |
| NULL | NULL |   NULL |    5 | FD       | 15     |
+------+------+--------+------+----------+--------+
2 rows in set (0.00 sec)
索引简介
  • 是什么

mysql官方对索引的定义为:索引index是帮助mysql高效获取数据的数据结构.可以得到索引的本质:索引是数据结构.

如果要查mysql这个单词,我们肯定需要定位到m字母,然后往下找到y字母,再找到剩下的sql

如果没有索引,那么你可能需要a–z,如果我想找到java开头的单词呢,或者oracle开头的单词呢

你可以简单理解为 排好序的快速查找数据结构

在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法.这种数据结构,就是索引.下图就是一种可能的索引方式示例:

image-20211014224606584
image-20211014224606584

所以说索引是一种数据结构,如果要查7这本书,正常的遍历要第七次才能找到,时间复杂度为n,索引是利用平衡二叉树,或者B+树,也就是说不一定二叉可以多叉,这样至多log2n+1次就能索引到,故而提高了效率

一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上

索引不太适合删除和修改多的场景,因为删除数据的同时要删除索引,修改数据的同时要修改索引,索引为何会失效

我们常说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉的)结构组织的索引.其中聚集索引,次要索引,覆盖索引,符合索引,前缀索引,唯一索引默认都是使用B+树索引,统称索引.当然,除了B+树这种类型的索引之外,还有哈希索引(hash index)等.

  • 优势
    • 类似大学图书馆建书索引,提高数据检索的效率,降低数据库的IO成本
    • 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗
  • 劣势
    • 实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的
    • 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行insert/update/delete.因为更新表时,mysql不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息
    • 索引只是提高效率的一个因素,如果你的mysql有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询
mysql索引分类
  • 单值索引
    • 即一个索引只包含单个列,一个表可以有多个单列索引
  • 唯一索引
    • 索引列的值必须唯一,但允许空值
  • 复合索引
    • 即一个索引包含多个列
  • 基本语法
    • 创建
      • create [unique] index indexName on tableName(columnName,…) ;
      • alter tableName add [unique] index [indexName] on (columnName(length)) ;
    • 删除
      • drop index [indexName] on tableName ;
    • 查看
      • show index from tableName ;
    • 使用alter命令
      • alter table tbl_name add primary key (column_list) ; 该语句添加一个主键,这意味这索引值必须是唯一的,且不能为null
      • alter table tbl_name add unique index_name (column_list) ; 这条语句创建索引的值必须是唯一的,除了null之外,null可能会出现多次
      • alter table tbl_name add index index_name (column_list) ; 添加普通索引,索引值可出现多次
      • alter table tbl_name add fulltext index_name (column_list) ; 该语句指定了索引为fulltext,用于全文索引
#主键外键唯一都是索引 我们这边讲的是那种普通索引
mysql> show index from tbl_emp ;
+---------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table   | Non_unique | Key_name   | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tbl_emp |          0 | PRIMARY    |            1 | id          | A         |           8 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| tbl_emp |          1 | fk_dept_id |            1 | deptid      | A         |           5 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
+---------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
2 rows in set (0.02 sec)
mysql索引的结构
  • Btree索引
    • 索引原理
  • hash索引
  • full-text全文索引
  • R-Tree索引

image-20211016093656515
image-20211016093656515

初始化介绍

一颗B+树,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含了几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1P2P3

P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块

真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99

非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在与数据表中

查找过程

如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比于磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存中,发生了第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO

真实情况

3层的b+树可以表示上百万条的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次IO,显然成本非常非常高

哪些情况需要创建索引
  • 主键自动建立唯一索引
  • 频繁作为查询条件的字段应该创建索引
  • 查询中与其他表关联的字段,外键关系应该建立索引
  • 频繁更新的字段不适合创建索引
    • 因为每次更新不单单是更新了记录还会更新索引
  • where条件里用不到的字段不创建索引
  • 单值/复合索引的选择问题
    • 高并发下倾向创建组合索引
  • 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
  • 查询中统计或者分组字段
哪些情况不要创建索引
  • 表记录太少
  • 经常增删改的表
    • 提高了查询速度,同时却会降低更新表的速度,如对表进行insert/update和delete,因为更新表时,mysql不仅要保存数据,还要保存一下索引文件
  • 数据重复且分布均匀的表字段,因此应该只为最经常查询和最经常排序的数据列创建索引.
    • 注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果

image-20211016095439966
image-20211016095439966

性能分析
msyql query optimizer 查询优化器

image-20211016095805682
image-20211016095805682

mysql常见瓶颈
  • CPU cpu在饱和的时候一般发生在数据装入内存或从磁盘上读取数据的时候
  • IO 磁盘IO瓶颈发生在装入数据远大于内存容量的时候
  • 服务器硬件的性能瓶颈: top free iostat vmstat 来查看系统的性能状态
  • explain
Explain
  • 是什么(查看执行计划)
    • 使用explain关键字可以模拟优化器执行sql查询语句,从而知道mysql是如何处理你的sql语句的.分析你的查询语句或是表结构的性能瓶颈
    • 官网介绍
  • 能干嘛
    • 表的读取顺序
    • 数据读取操作的操作类型
    • 哪些索引可以使用
    • 哪些索引被实际使用
    • 表之间的引用
    • 每张表有多少行被优化器查询
  • 怎么玩
    • Explain + SQL语句
    • 执行计划包含的信息
      • 表头 id select_type table partitions type possible_keys key ken_len ref rows filtered extra
  • 各字段解释
    • id
      • select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
      • 三种情况
        • id相同,执行顺序由上到下
          image-20211016101510820
          image-20211016101510820
        • id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
          image-20211016101830835
          image-20211016101830835
        • id相同不同,同时存在
          image-20211016102234339
          image-20211016102234339
    • select_type
      • 有哪些 simple primary subquery dirived union union result
      • 查询的类型,主要是用于区别普通查询/联合查询/子查询等的复杂查询
        • simple 简单的select查询,查询中不包含子查询或者union
        • primary 查询中若包含任何复杂的子部分,最外层查询则被标记为
        • subquery 在select或where列表中包含了子查询
        • derived 在from列表中包含的子查询被标记为derived(衍生) mysql会递归执行这些子查询,把结果放在临时表里
        • union 若第二个select出现在union之后,则被标记为union;若union包含在from子句的子查询中,外层select 将被标记为derived(很正常嘛,union之后的结果作为临时表 所以被标记为derived)
        • union result 从union表获取结果的select
    • table 显示这一行的数据是关于那些表的
    • type
      • all index range ref eq_ref const,system null
      • 访问类型排列 显示查询使用了何种类型,最好到最差依次是 syste>const>eq_ref>ref>range>index>all
        image-20211016110536099
        image-20211016110536099
        • system 表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计
        • const 表示通过索引一次就找到了,const用于比较primary key 或者 unique索引,因为只匹配了一行数据,所以很快,如将主键置于where列表中,mysql就能将该查询转换为一个常量
          image-20211016110923813
          image-20211016110923813
        • eq_ref 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配.常用语主键或唯一索引扫描
          image-20211016112628359
          image-20211016112628359
        • ref 非唯一性索引扫描,返回匹配某个单独值的所有行.本质上也是一种索引访问,他返回所有匹配某个单独值的行,然而,她可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体.
          image-20211016113043870
          image-20211016113043870
        • range 只检索给定范围的行,使用一个索引来选择行. key列显示使用了哪个索引,一般就是在你的where语句中出现了between < > in 等的查询,这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束于另一点,不用扫描全部索引
          image-20211016113701811
          image-20211016113701811
        • index (full index san) index与all的区别为index类型只遍历索引树.这通常比all快,因为索引文件比数据文件小 也就是说虽然all和index都是读全表,但index是从索引中读取的,而all是从硬盘中读取的
          image-20211016114028963
          image-20211016114028963
        • all (full table scan) 将遍历全表以找到匹配的行
          image-20211016114058619
          image-20211016114058619
        • **注意:**一般来说,得保证查询至少打到range级别,最好能够达到ref
    • possible_keys
      • 用来判断索引用上没,索引失效没 显示可能应用在这张表中的索引,一个或多个.查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用
    • key
      • 实际使用的索引.如果为null,则没有使用索引
      • 查询中若使用了覆盖索引,则该索引仅出现在key列表中
    • key_len
      • 表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度.在不损失精确性的情况下,长度越短越好
      • key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的 同样的查询条件下,精度越小越好
        image-20211016145227350
        image-20211016145227350
    • ref 显示索引的哪一列被使用了,如果可能的话,是一个常数.那些列或常量被用于查找索引列上的值
      image-20211016145926736
      image-20211016145926736
    • rows 根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
      image-20211016150445129
      image-20211016150445129
    • extra 包含不适合在其他列中显示但十分重要的额外信息
extra信息
  • using filesort 说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取 mysql中无法利用索引完成的排序操作称为文件排序 出现这个东西不好 如果可以尽快优化
    image-20211016151320887
    image-20211016151320887
  • using temporary 使用了临时表保存中间结果,mysql在对查询结果排序时使用临时表.常见于排序order by和分组查询group by 使用临时表会损伤数据库性能 用完还要回收 这个很不好哦
    image-20211016151757718
    image-20211016151757718
  • using index
    • 表示相应的select操作中使用了覆盖索引covering index , 避免了访问了表的的数据行,效率不错!
    • 如果同时出现using where ,表明索引被用来执行索引键值的查找
    • 如果没有同时出现using where , 表明索引用来读取数据而非执行查找动作
    • 覆盖索引 建了3个索引,查的也是3个 全覆盖
      image-20211016152608867
      image-20211016152608867
      image-20211016152333029
      image-20211016152333029
  • using where 表明使用了where条件
  • using join buffer 表明使用了连接缓存
  • impossible where 表明where子句的值总是false , 不能用来获取任何元组
    image-20211016152746052
    image-20211016152746052
  • select tables optimized away
    • 在没有groupby子句的情况下,基于索引优化min/max操作或者对于myisam存储引擎优化count(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化.
  • select distinct 优化distinct操作,在找到第一匹配的元组后立即停止查找同样值的操作
#使用explain关键字 进行性能分析
mysql>  explain select * from tbl_emp ;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | tbl_emp | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    8 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)

mysql> explain select * from tbl_emp a left join tbl_dept b on a.deptId = b.id union select * from tbl_emp a right join tbl_dept b on a.deptid = b.id ;
+----+--------------+------------+------------+--------+---------------+------------+---------+----------------+------+----------+-----------------+
| id | select_type  | table      | partitions | type   | possible_keys | key        | key_len | ref            | rows | filtered | Extra           |
+----+--------------+------------+------------+--------+---------------+------------+---------+----------------+------+----------+-----------------+
|  1 | PRIMARY      | a          | NULL       | ALL    | NULL          | NULL       | NULL    | NULL           |    8 |   100.00 | NULL            |
|  1 | PRIMARY      | b          | NULL       | eq_ref | PRIMARY       | PRIMARY    | 4       | girls.a.deptid |    1 |   100.00 | NULL            |
|  2 | UNION        | b          | NULL       | ALL    | NULL          | NULL       | NULL    | NULL           |    5 |   100.00 | NULL            |
|  2 | UNION        | a          | NULL       | ref    | fk_dept_id    | fk_dept_id | 5       | girls.b.id     |    1 |   100.00 | NULL            |
| NULL | UNION RESULT | <union1,2> | NULL       | ALL    | NULL          | NULL       | NULL    | NULL           | NULL |     NULL | Using temporary |
+----+--------------+------------+------------+--------+---------------+------------+---------+----------------+------+----------+-----------------+
5 rows in set, 1 warning (0.00 sec)
热身case

image-20211016153657227
image-20211016153657227

image-20211016153809882
image-20211016153809882

索引优化
  • 索引分析

    • 单表

    • 两表

    • 三表

  • 索引失效(应该避免)

  • 一般性建议

#建表 单表示例
mysql> 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
    -> );
Query OK, 0 rows affected, 5 warnings (0.03 sec)

mysql>
mysql> 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');
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from article;
+----+-----------+-------------+-------+----------+--------------+---------+
| id | author_id | category_id | views | comments | title        | content |
+----+-----------+-------------+-------+----------+--------------+---------+
|  1 |         1 |           1 |     1 |        1 | 0x31         | 1       |
|  2 |         2 |           2 |     2 |        2 | 0x32         | 2       |
|  3 |         1 |           1 |     3 |        3 | 0x33         | 3       |
+----+-----------+-------------+-------+----------+--------------+---------+
3 rows in set (0.00 sec)

#性能分析  type是all 说明是全表扫描
#显然 using filesort 也是最坏的情况 发生了文件内排序 
mysql> explain select id,author_id from article where category_id = 1 and comments >1 order by views desc limit 1 ;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
|  1 | SIMPLE      | article | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where; Using filesort |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
1 row in set, 1 warning (0.00 sec)

#如果创建的索引是ccv  type 变成了range extra还是filesort 好了点
mysql> create index idx_ccv on article(category_id,comments,views) ;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select id,author_id from article where category_id = 1 and comments >1 order by views desc limit 1 ;
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+---------------------------------------+
| id | select_type | table   | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra                                 |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+---------------------------------------+
|  1 | SIMPLE      | article | NULL       | range | idx_ccv       | idx_ccv | 8       | NULL |    1 |   100.00 | Using index condition; Using filesort |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+---------------------------------------+
1 row in set, 1 warning (0.00 sec)

#删除索引
mysql> drop index idx_ccv on article ;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

image-20211016171713970
image-20211016171713970

mysql> create index idx_cv on article(category_id,views) ;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

#把范围的去掉
mysql> explain select id,author_id from article where category_id = 1 and comments >1 order by views desc limit 1 ;
+----+-------------+---------+------------+------+---------------+--------+---------+-------+------+----------+----------------------------------+
| id | select_type | table   | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra                            |
+----+-------------+---------+------------+------+---------------+--------+---------+-------+------+----------+----------------------------------+
|  1 | SIMPLE      | article | NULL       | ref  | idx_cv        | idx_cv | 4       | const |    2 |    33.33 | Using where; Backward index scan |
+----+-------------+---------+------------+------+---------------+--------+---------+-------+------+----------+----------------------------------+
1 row in set, 1 warning (0.00 sec)
#两表 
mysql> CREATE TABLE IF NOT EXISTS `class`(
    -> `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    -> `card` INT(10) UNSIGNED NOT NULL,
    -> PRIMARY KEY(`id`)
    -> );
Query OK, 0 rows affected, 2 warnings (0.03 sec)

mysql> CREATE TABLE IF NOT EXISTS `book`(
    -> `bookid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    -> `card` INT(10) UNSIGNED NOT NULL,
    -> PRIMARY KEY(`bookid`)
    -> );
Query OK, 0 rows affected, 2 warnings (0.01 sec)

#分析 都是全表扫描 很几把恐怖
mysql> explain select * from book b right join class c on b.card = c.card ;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                      |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+
|  1 | SIMPLE      | c     | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   20 |   100.00 | NULL                                       |
|  1 | SIMPLE      | b     | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   20 |   100.00 | Using where; Using join buffer (hash join) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

#优化 先给右连接的左表定索引 同理 或者给左连接的右表定索引
mysql> alter table book add index Y(card) ;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

#type变成ref了 有点意思 using index 很好 
mysql> explain select * from book b right join class c on b.card = c.card ;
+----+-------------+-------+------------+------+---------------+------+---------+--------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref          | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+--------------+------+----------+-------------+
|  1 | SIMPLE      | c     | NULL       | ALL  | NULL          | NULL | NULL    | NULL         |   20 |   100.00 | NULL        |
|  1 | SIMPLE      | b     | NULL       | ref  | Y             | Y    | 4       | girls.c.card |    1 |   100.00 | Using index |
+----+-------------+-------+------------+------+---------------+------+---------+--------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

#不知道给左边添加索引好还是给右边添加好 所以再试试
mysql> drop index Y on book ;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

#添加索引
mysql> alter table class add index Y(card) ;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

#发现没有用 为啥呢 因为右连接 没有where的话右边是肯定是全都有的 加了索引有啥用啊
mysql> explain select * from book b right join class c on b.card = c.card ;
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+--------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                      |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+--------------------------------------------+
|  1 | SIMPLE      | c     | NULL       | index | NULL          | Y    | 4       | NULL |   20 |   100.00 | Using index                                |
|  1 | SIMPLE      | b     | NULL       | ALL   | NULL          | NULL | NULL    | NULL |   20 |   100.00 | Using where; Using join buffer (hash join) |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+--------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

#删除索引
mysql> drop index Y on class ;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

image-20211016174325936
image-20211016174325936

#三表
mysql> CREATE TABLE IF NOT EXISTS `phone`(
    -> `phoneid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    -> `card` INT(10) UNSIGNED NOT NULL,
    -> PRIMARY KEY(`phoneid`)
    -> )ENGINE=INNODB;
Query OK, 0 rows affected, 2 warnings (0.02 sec)

#都是all 很差劲 20 
mysql> explain select * from class left join book on class.card = book.card left join phone on phone.card = book.card ;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                      |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+
|  1 | SIMPLE      | class | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   20 |   100.00 | NULL                                       |
|  1 | SIMPLE      | book  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   20 |   100.00 | Using where; Using join buffer (hash join) |
|  1 | SIMPLE      | phone | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   20 |   100.00 | Using where; Using join buffer (hash join) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+
3 rows in set, 1 warning (0.00 sec)

mysql> alter table phone add index z(card);
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> create index y on book(card) ;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select * from class left join book on class.card = book.card left join phone on phone.card = book.card ;
+----+-------------+-------+------------+------+---------------+------+---------+------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref              | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------------------+------+----------+-------------+
|  1 | SIMPLE      | class | NULL       | ALL  | NULL          | NULL | NULL    | NULL             |   20 |   100.00 | NULL        |
|  1 | SIMPLE      | book  | NULL       | ref  | y             | y    | 4       | girls.class.card |    1 |   100.00 | Using index |
|  1 | SIMPLE      | phone | NULL       | ref  | z             | z    | 4       | girls.book.card  |    1 |   100.00 | Using index |
+----+-------------+-------+------------+------+---------------+------+---------+------------------+------+----------+-------------+
3 rows in set, 1 warning (0.00 sec)

image-20211016193542297
image-20211016193542297

索引失效(应该避免)
  • 全值匹配我最爱

  • 最佳左前缀法则

    • 如果索引了多列,要遵守最左前缀法则.指的是查询从索引的最左前列开始并且不跳过索引中的列
      image-20211016195626235
      image-20211016195626235
  • 不在索引列上做任何操作(计算/函数/自动手动类型转换),会导致索引失效而转向全表扫描

    • 少计算

      #对索引列进行函数转换后会导致索引失效的问题
      mysql> explain select * from staffs where left(name,4)= 'July' ;
      +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
      | id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
      +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
      |  1 | SIMPLE      | staffs | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |   100.00 | Using where |
      +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
      1 row in set, 1 warning (0.00 sec)
      
  • 存储引擎不能使用索引中范围条件右边的列

    • 范围后面的索引都失效了 between and / in / like
    #都用到了 三个索引 140的长度
    mysql> explain select * from staffs where name = 'July' and age =25 and pos = 'dev' ;
    +----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
    | id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref               | rows | filtered | Extra |
    +----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
    |  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 140     | const,const,const |    1 |   100.00 | NULL  |
    +----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
    1 row in set, 1 warning (0.00 sec)
    
    #用到了两个 因为范围后的索引失效了 78长度 说明第二个用到了
    mysql> explain select * from staffs where name = 'July' and age >25 and pos = 'dev' ;
    +----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
    | id | select_type | table  | partitions | type  | possible_keys         | key                   | key_len | ref  | rows | filtered | Extra                 |
    +----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
    |  1 | SIMPLE      | staffs | NULL       | range | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 78      | NULL |    1 |    33.33 | Using index condition |
    +----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
    1 row in set, 1 warning (0.00 sec)
    
  • 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *

    #查找所有字段
    mysql> explain select * from staffs where name = 'July' and age =25 and pos = 'dev' ;
    +----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
    | id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref               | rows | filtered | Extra |
    +----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
    |  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 140     | const,const,const |    1 |   100.00 | NULL  |
    +----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
    1 row in set, 1 warning (0.00 sec)
    
    #就只查找索引 索引覆盖 extra 变成 using index 是个好兆头
    mysql> explain select name , age , pos from staffs where name = 'July' and age =25 and pos='manager' ;
    +----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------------+
    | id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref               | rows | filtered | Extra       |
    +----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------------+
    |  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 140     | const,const,const |    1 |   100.00 | Using index |
    +----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------------+
    1 row in set, 1 warning (0.00 sec)
    

    image-20211016201217849
    image-20211016201217849

  • mysql在使用不等于!= 或者 <>的时候无法使用索引 , 会导致全表扫描

image-20211016201756985
image-20211016201756985

  • is null , is not null 也无法使用索引

image-20211016202018668
image-20211016202018668

  • like 以通配符开头 (’%abc…’) mysql索引失效会变成全表扫描的操作

    • 只有把%写在右边才能避免索引失效
    #索引失效
    mysql> explain select * from staffs where name like '%July%' ;
    +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
    | id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | staffs | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
    +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
    1 row in set, 1 warning (0.00 sec)
    
    #放在左边也失效  毕竟不全表扫怎么知道是否找全了
    mysql> explain select * from staffs where name like '%July' ;
    +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
    | id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | staffs | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
    +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
    1 row in set, 1 warning (0.00 sec)
    
    #没有失效 
    mysql> explain select * from staffs where name like 'July%' ;
    +----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
    | id | select_type | table  | partitions | type  | possible_keys         | key                   | key_len | ref  | rows | filtered | Extra                 |
    +----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
    |  1 | SIMPLE      | staffs | NULL       | range | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | NULL |    1 |   100.00 | Using index condition |
    +----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
    1 row in set, 1 warning (0.00 sec)
    
    • 问题是解决like’%字符%‘时索引不失效???
    mysql> 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;
    Query OK, 0 rows affected, 3 warnings (0.03 sec)
    
    #用覆盖索引来解决
    mysql> create index idx_user_nameAge on tbl_user(name,age) ;
    Query OK, 0 rows affected (0.02 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    
    #index了 说明用到了
    mysql> explain select name , age from tbl_user where name like '%aa%' ;
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    | id | select_type | table    | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    |  1 | SIMPLE      | tbl_user | NULL       | index | NULL          | idx_user_nameAge | 68      | NULL |    4 |    25.00 | Using where; Using index |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    1 row in set, 1 warning (0.00 sec)
    
    #index了 说明用到了
    mysql> explain select id from tbl_user where name like '%aa%';
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    | id | select_type | table    | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    |  1 | SIMPLE      | tbl_user | NULL       | index | NULL          | idx_user_nameAge | 68      | NULL |    4 |    25.00 | Using where; Using index |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    1 row in set, 1 warning (0.00 sec)
    
    #index了 说明用到了
    mysql> explain select name from tbl_user where name like '%aa%';
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    | id | select_type | table    | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    |  1 | SIMPLE      | tbl_user | NULL       | index | NULL          | idx_user_nameAge | 68      | NULL |    4 |    25.00 | Using where; Using index |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    1 row in set, 1 warning (0.00 sec)
    
    #index了 说明用到了
    mysql> explain select age from tbl_user where name like '%aa%';
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    | id | select_type | table    | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    |  1 | SIMPLE      | tbl_user | NULL       | index | NULL          | idx_user_nameAge | 68      | NULL |    4 |    25.00 | Using where; Using index |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    1 row in set, 1 warning (0.00 sec)
    
    #index了 说明用到了
    mysql> explain select id,name from tbl_user where name like '%aa%';
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    | id | select_type | table    | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    |  1 | SIMPLE      | tbl_user | NULL       | index | NULL          | idx_user_nameAge | 68      | NULL |    4 |    25.00 | Using where; Using index |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    1 row in set, 1 warning (0.00 sec)
    
    #index了 说明用到了
    mysql> explain select id,name,age from tbl_user where name like '%aa%';
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    | id | select_type | table    | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    |  1 | SIMPLE      | tbl_user | NULL       | index | NULL          | idx_user_nameAge | 68      | NULL |    4 |    25.00 | Using where; Using index |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    1 row in set, 1 warning (0.00 sec)
    
    #index了 说明用到了
    mysql> explain select id,name,age from tbl_user where name like '%aa%';
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    | id | select_type | table    | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    |  1 | SIMPLE      | tbl_user | NULL       | index | NULL          | idx_user_nameAge | 68      | NULL |    4 |    25.00 | Using where; Using index |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    1 row in set, 1 warning (0.00 sec)
    
    #index了 说明用到了
    mysql> explain select name,age from tbl_user where name like '%aa%';
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    | id | select_type | table    | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    |  1 | SIMPLE      | tbl_user | NULL       | index | NULL          | idx_user_nameAge | 68      | NULL |    4 |    25.00 | Using where; Using index |
    +----+-------------+----------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
    1 row in set, 1 warning (0.00 sec)
    
    #没有覆盖到索引  索引失效
    mysql> explain select * from tbl_user where name like '%aa%' ;
    +----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
    | id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | tbl_user | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    4 |    25.00 | Using where |
    +----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
    1 row in set, 1 warning (0.00 sec)
    
    #没有完全覆盖 索引失效
    mysql> explain select id,name,age,email from tbl_user where name like '%aa%' ;
    +----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
    | id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | tbl_user | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    4 |    25.00 | Using where |
    +----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
    1 row in set, 1 warning (0.00 sec)
    
  • 字符串不加单引号索引失效

mysql> select * from staffs where name = '2000' ;
+----+------+-----+-----+---------------------+
| id | NAME | age | pos | add_time            |
+----+------+-----+-----+---------------------+
|  3 | 2000 |  23 | dev | 2021-10-16 19:38:15 |
+----+------+-----+-----+---------------------+
1 row in set (0.00 sec)

#会发生隐式转换
mysql> select * from staffs where name =2000 ;
+----+------+-----+-----+---------------------+
| id | NAME | age | pos | add_time            |
+----+------+-----+-----+---------------------+
|  3 | 2000 |  23 | dev | 2021-10-16 19:38:15 |
+----+------+-----+-----+---------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from staffs where name ='2000' ;
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

#和第三个吻合  隐式自动转换 自动转换会导致索引失效
mysql> explain select * from staffs where name =2000 ;
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys         | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
1 row in set, 3 warnings (0.00 sec)
  • 少用or , 用它来连接时会索引失效

image-20211017095517319
image-20211017095517319

#建表
mysql> 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 '员工记录表';
Query OK, 0 rows affected, 1 warning (0.02 sec)

#添加索引
mysql> ALTER TABLE staffs ADD INDEX idx_staffs_nameAgePos(name, age, pos);
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

#查看索引
mysql>  show index from staffs ;
+--------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table  | Non_unique | Key_name              | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+--------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| staffs |          0 | PRIMARY               |            1 | id          | A         |           2 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| staffs |          1 | idx_staffs_nameAgePos |            1 | NAME        | A         |           3 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| staffs |          1 | idx_staffs_nameAgePos |            2 | age         | A         |           3 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| staffs |          1 | idx_staffs_nameAgePos |            3 | pos         | A         |           3 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
+--------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+

#挺好的哈
mysql> explain select * from staffs where name = 'July' ;
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

#两个条件  key_len越来越大
mysql> explain select * from staffs where name = 'July'and age = 25;
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref         | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 78      | const,const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

#三个条件
mysql> explain select * from staffs where name = 'July' and age =25 and pos = 'dev' ;
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref               | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 140     | const,const,const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

#问题来了 没有name 只用age和pos的时候变成了全表扫描 索引失效
mysql> explain select * from staffs where age =23 and pos = 'dev' ;
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

#没有name 只用pos变成了全表扫描 索引失效
mysql> explain select * from staffs where  pos = 'dev' ;
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

#必须要有name 带头大哥不能死 最佳左前缀法则
mysql> explain select * from staffs where  name = 'July' ;
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

#虽然用到索引了 但是从索引的长度可以看出 只用了name这个索引 因为中间的age丢了 
mysql> explain select * from staffs where name = 'July' and pos = 'dev' ;
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 |    33.33 | Using index condition |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

image-20211017095823171
image-20211017095823171

image-20211017100150557
image-20211017100150557

可能有点小问题,到时候勘误一下

练习一下

mysql> 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)
    -> );
Query OK, 0 rows affected (0.05 sec)


mysql> select * from test03;
+----+------+------+------+------+------+
| id | c1   | c2   | c3   | c4   | c5   |
+----+------+------+------+------+------+
|  1 | a1   | a2   | a3   | a4   | a5   |
|  2 | b1   | b2   | b3   | b4   | b5   |
|  3 | c1   | c2   | c3   | c4   | c5   |
|  4 | d1   | d2   | d3   | d4   | d5   |
|  5 | e1   | e2   | e3   | e4   | e5   |
+----+------+------+------+------+------+

mysql> create index idx_test03_c1234 on test03(c1,c2,c3,c4);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

#用到了
mysql> explain select * from test03 where c1 ='a1' ;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 31      | const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

#索引在逐渐递增 key_len
mysql> explain select * from test03 where c1 ='a1' and c2='a2' ;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref         | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 62      | const,const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

#mysql会自我优化 优化常量的查询顺序
mysql> explain select * from test03 where c2='a2' and c1='a1' ;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref         | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 62      | const,const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

#索引在逐渐递增 key_len
mysql> explain select * from test03 where c1 ='a1' and c2='a2'and c3='a3' ;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref               | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 93      | const,const,const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from test03 where c1 ='a1' and c2='a2'and c3='a3' and c4='a4' ;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref                     | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------------+------+----------+-------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 124     | const,const,const,const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

#mysql会自我优化 优化常量的查询顺序
mysql> explain select * from test03 where c1 ='a1' and c2='a2'and c4='a4' and c3='a3' ;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref                     | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------------+------+----------+-------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 124     | const,const,const,const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

#范围后面全失效
mysql> explain select * from test03 where c1 ='a1' and c2='a2'and c3>'a3' and c4='a4' ;
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+-----------------------+
| id | select_type | table  | partitions | type  | possible_keys    | key              | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | test03 | NULL       | range | idx_test03_c1234 | idx_test03_c1234 | 93      | NULL |    1 |    20.00 | Using index condition |
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

#我觉得是4 查询顺序会被mysql优化
mysql> explain select * from test03 where c1 ='a1' and c2='a2'and c4>'a4' and c3='a3' ;
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+-----------------------+
| id | select_type | table  | partitions | type  | possible_keys    | key              | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | test03 | NULL       | range | idx_test03_c1234 | idx_test03_c1234 | 124     | NULL |    1 |   100.00 | Using index condition |
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

#c3没有统计到里面 用于排序了而不是查找 c4就失效了呗
mysql> explain select * from test03 where c1 ='a1' and c2='a2'and c4='a4' order by c3 ;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-----------------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref         | rows | filtered | Extra                 |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-----------------------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 62      | const,const |    1 |    20.00 | Using index condition |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

#和上面的没什么区别 c3用去排序了 所以c4就失效了呗
mysql> explain select * from test03 where c1 ='a1' and c2='a2' order by c3 ;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref         | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 62      | const,const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

#用到了索引 a1  a2 但是抓到数据之后,由于索引设置的是a1a2a3a4默认的排序是a1a2a3a4但是你这边直接
#开始用a4开始排 你还是人吗 这样抓到数据之后还需要对数据进行文件内部的排序才能展示给大家 
mysql> explain select * from test03 where c1 ='a1' and c2='a2' order by c4 ;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+----------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref         | rows | filtered | Extra          |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+----------------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 62      | const,const |    1 |   100.00 | Using filesort |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+----------------+
1 row in set, 1 warning (0.00 sec)

#用到了索引 a1 但是排序按照a2 a3排序 不会导致索引失效
mysql> explain select * from test03 where c1 ='a1' and c5='a5' order by c2,c3 ;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref   | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 31      | const |    1 |    20.00 | Using where |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------------+

#用到了索引a1 但是排序按照a3 a2排序 不会导致索引失效 但是检索到数据之后,排序不是索引默认的排序规则a1 a2 a3 而是a3 a2 所以检索到后又按照文件内排序 拍了一遍  Using filesort
mysql> explain select * from test03 where c1 ='a1' and c5='a5' order by c3,c2 ;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref   | rows | filtered | Extra                       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 31      | const |    1 |    20.00 | Using where; Using filesort |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------------+

#没有问题哈
mysql> explain select * from test03 where c1 ='a1' and c2='a2' order by c2,c3;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref         | rows | filtered | Extra |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 62      | const,const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

#也没有问题哈 索引就给了1234
mysql> explain select * from test03 where c1 ='a1' and c2='a2' and c5='c5' order by c2,c3;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref         | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 62      | const,const |    1 |    20.00 | Using where |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)


#不是说order by 的顺序是要和索引建立的顺序保持一致吗 为啥不会出现文件内排序呢 那是因为c2=a2出现在where中,相当于你order by的排序已经没有用处了
mysql> explain select * from test03 where c1 ='a1' and c2='a2' and c5='c5' order by c3,c2;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref         | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 62      | const,const |    1 |    20.00 | Using where |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

#这个没问题哈 c3 c2 会导致文件内排序
mysql> explain select * from test03 where c1 ='a1' and c5='c5' order by c3,c2;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref   | rows | filtered | Extra                       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 31      | const |    1 |    20.00 | Using where; Using filesort |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------------+
1 row in set, 1 warning (0.00 sec)

#索引没有失效 但是只用到了一个 没有发生文件内排序
mysql> explain select * from test03 where c1='a1' and c4= 'a4' group by c2,c3 ;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 31      | const |    1 |    20.00 | Using index condition |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

#索引没问题 使用了部分索引 但是分组的时候 Using temporary 十死无生啊 分组前必排序 
mysql> explain select * from test03 where c1='a1' and c4= 'a4' group by c3,c2 ;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+----------------------------------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref   | rows | filtered | Extra                                  |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+----------------------------------------+
|  1 | SIMPLE      | test03 | NULL       | ref  | idx_test03_c1234 | idx_test03_c1234 | 31      | const |    1 |    20.00 | Using index condition; Using temporary |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+----------------------------------------+
1 row in set, 1 warning (0.00 sec)

总结

定值 / 范围 还是排序 , 一般order by 是给个范围

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

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

image-20211018194551515
image-20211018194551515

like后面跟着常量的话就没什么问题能做索引也能跳板

优化总结口诀

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

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

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

like百分写最右, 覆盖索引不写*

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

var引号不可丢,sql高级也不难

查询截取分析
  • 查询优化
  • 慢查询日志
  • 批量数据脚本
  • show profile
  • 全局查询日志
分析步骤
  1. 观察,至少跑一天,看看生产的慢sql情况
  2. 开启慢查询日志,设置阙值,比如超过5秒钟的就是慢sql,并将它抓取出来
  3. explain + 慢sql分析
  4. show profile
  5. 运维经理或dba,进行sql数据库服务器的参数调优
总结

0

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

永远小表驱动大表

类似于嵌套循环nested loop

orderby关键字优化

group by关键字优化

image-20211018201006527
image-20211018201006527

很好分析嘛,如果B数据少于A,那么用B驱动,B在右边那就是先查B,因为B先查,查到的是小表,这样就小表驱动大表了; 如果B数据多余A,那么用A驱动,B在右边,那就是用exists,在B的查询里套上子查询,这样先查的B的同时需要先查A(这样才知道A.id,B才能查),这样就变成小表驱动大表了

image-20211018201111638
image-20211018201111638

mysql> select * from tbl_emp where deptid in (select id from tbl_dept ) ;
+----+------+--------+
| id | name | deptid |
+----+------+--------+
|  1 | z3   |      1 |
|  2 | z4   |      1 |
|  3 | z5   |      1 |
|  4 | w5   |      2 |
|  5 | w6   |      2 |
|  6 | s7   |      3 |
|  7 | s8   |      4 |
+----+------+--------+
7 rows in set (0.00 sec)

#两种最终效果是一样的 但是得考虑哪个是小表哪个是大表 需要小表驱动大表 那就考虑使用哪一种
mysql> select * from tbl_emp where exists (select 1 from tbl_dept where tbl_emp.deptid = tbl_dept.id);
+----+------+--------+
| id | name | deptid |
+----+------+--------+
|  1 | z3   |      1 |
|  2 | z4   |      1 |
|  3 | z5   |      1 |
|  4 | w5   |      2 |
|  5 | w6   |      2 |
|  6 | s7   |      3 |
|  7 | s8   |      4 |
+----+------+--------+
7 rows in set (0.00 sec)
orderby 关键字优化
  • orderby子句,尽量使用index方式排序,避免使用filesort方式排序
    • mysql支持两种方式的排序,filesort和index,index效率高,它指ysql扫描索引本身完成排序,filesort方式效率低
  • order by 满足两种情况,会使用index方式排序
    • order by 语句使用索引最左前列
    • 使用where子句和order by子句条件列满足索引最左前列
  • 尽可能在索引列上完成排序操作,遵照索引建的最佳左前缀原则
  • 如果不在索引列上,filesort有两种算法:mysql就要启动双路排序和单路排序
    • 双路排序
      • 4.1之前使用双路排序,扫描两次磁盘,最终得到数据,读取行指针和order by 列, 对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取
      • 从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段
    • 单路排序
      • 取一批数据,要对磁盘进行两次扫描,众所周知,IO很耗时,在4.1之后,出现了第二种的改进算法,就是单路排序
      • 从磁盘读取查询需要的所有列,按照orderby列在buffer对他们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据.并且把随机IO变成了顺序IO,但是它会使用更多的空间,因为它把每一行都保存在内存中了.
      • image-20211018204727314
        image-20211018204727314
  • 优化策略
    • 增大sort_buffer_size参数设置
    • 增大max_length_for_sort_data参数的设置
    • why
    • image-20211018205003059
      image-20211018205003059
mysql> CREATE TABLE tblA(
    -> #id int primary key not null autp_increment,
    -> age int,
    -> birth timestamp not null
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql> CREATE INDEX idx_A_ageBirth on tblA(age,birth);
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

image-20211018203216016
image-20211018203216016

默认升序 打乱了就会发生filesort

image-20211018203523533
image-20211018203523533

总结

image-20211018205452929
image-20211018205452929

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

mysql慢查询日志

image-20211018205757207
image-20211018205757207

image-20211018205824359
image-20211018205824359

  • 如何查看是否开启与如何开启

    mysql> show variables like '%slow_query_log%';
    +---------------------+--------------------------+
    | Variable_name       | Value                    |
    +---------------------+--------------------------+
    | slow_query_log      | ON                       |
    | slow_query_log_file | LAPTOP-FKVMDBSI-slow.log |
    +---------------------+--------------------------+
    2 rows in set, 1 warning (0.01 sec)
    
    #只对当前数据库生效
    mysql> set global slow_query_log = 1 ;
    Query OK, 0 rows affected (0.00 sec)
    

    image-20211018210231026
    image-20211018210231026

  • 什么样的会记录到慢查询日志里面呢?

mysql> show variables like '%long_query_time%';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
1 row in set, 1 warning (0.00 sec)

image-20211018210404815
image-20211018210404815

  • 设置慢查询时间阙值

    mysql> set global long_query_time = 3 ;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> show variables like '%long_query_time%';
    +-----------------+-----------+
    | Variable_name   | Value     |
    +-----------------+-----------+
    | long_query_time | 10.000000 |
    +-----------------+-----------+
    1 row in set, 1 warning (0.00 sec)
    

    image-20211018210539898
    image-20211018210539898

    image-20211018210653124
    image-20211018210653124

  • 去哪儿找慢sql呀?

#超过3s了 应该会记录到某个日志里对不对 
mysql> select sleep(4);
+----------+
| sleep(4) |
+----------+
|        0 |
+----------+
1 row in set (4.01 sec)	

image-20211018211230665
image-20211018211230665

  • 查看有多少条慢查询的sql
mysql> show global status like 'slow_queries%' ;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Slow_queries  | 2     |
+---------------+-------+
1 row in set (0.00 sec)
  • 配置版 永久生效

image-20211018212354576
image-20211018212354576

  • 日志分析工具mysqldumpslow
    • 查看帮助信息
      • s 标识按照何种方式排序
      • c 访问次数
      • l 锁定时间
      • r 返回记录
      • t 查询时间
      • al 平均锁定时间
      • ar 平均返回记录数
      • at 平均查询时间
      • t 返回前面多少条的数据
      • g 后边搭配一个正则匹配模式,大小写不敏感的

image-20211018212608969
image-20211018212608969

image-20211018213827239
image-20211018213827239

批量插入数据
#准备工作
mysql> create database bigdata ;
Query OK, 1 row affected (0.01 sec)

mysql> use bigdata ;
Database changed
mysql> CREATE TABLE `dept` (
    ->   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    ->   `deptno` mediumint(8) unsigned NOT NULL DEFAULT '0',
    ->   `dname` varchar(20) NOT NULL DEFAULT '',
    ->   `loc` varchar(13) NOT NULL DEFAULT '',
    ->   PRIMARY KEY (`id`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=GBK;
Query OK, 0 rows affected, 2 warnings (0.03 sec)

mysql>
mysql> CREATE TABLE `emp` (
    ->   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    ->   `empno` mediumint(8) unsigned NOT NULL DEFAULT '0',/*编号*/
    ->   `ename` varchar(20) NOT NULL DEFAULT '',/*名字*/
    ->   `job` varchar(9) NOT NULL DEFAULT '',/*工作*/
    ->   `mgr` mediumint(8) unsigned NOT NULL DEFAULT '0',/*上级编号*/
    ->   `hiredate` date NOT NULL,/*入职时间*/
    ->   `sal` decimal(7,2) NOT NULL,/*薪水*/
    ->   `comm` decimal(7,2) NOT NULL,/*红利*/
    ->   `deptno` mediumint(8) unsigned NOT NULL DEFAULT '0',/*部门编号*/
    ->   PRIMARY KEY (`id`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=GBK;
Query OK, 0 rows affected, 4 warnings (0.02 sec)

mysql> show variables like 'log_bin_trust_function_creators' ;
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| log_bin_trust_function_creators | OFF   |
+---------------------------------+-------+
1 row in set, 1 warning (0.00 sec)

mysql> set global log_bin_trust_function_creators = 1 ;
Query OK, 0 rows affected (0.00 sec)

#重启会失效  如果永久就修改配置文件
mysql> show variables like 'log_bin_trust_function_creators' ;
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| log_bin_trust_function_creators | ON    |
+---------------------------------+-------+
1 row in set, 1 warning (0.00 sec)

#创建函数随机产生字符串和部门编号
mysql> delimiter $$
mysql> create function rand_string (n int) returns varchar (255)
    -> begin
    -> declare chars_str varchar(100) default 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
    -> 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 $$
Query OK, 0 rows affected (0.02 sec)

mysql> create function rand_num() returns int(5)
    -> begin
    -> declare i int default 0 ;
    -> set i = floor(100+rand()*10);
    -> return i  ;
    -> end $$
Query OK, 0 rows affected, 1 warning (0.01 sec)

#创建存储过程 并使用函数
mysql> 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),rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand_num());
    -> until i = max_num
    -> end repeat ;
    -> commit ;
    -> end $$
Query OK, 0 rows affected, 2 warnings (0.01 sec)

mysql> DELIMITER $$
mysql> 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),rand_string(10), rand_string(8));
    -> UNTIL i = max_num
    -> END REPEAT;
    -> COMMIT;
    -> END $$
Query OK, 0 rows affected, 2 warnings (0.01 sec)

#调用存储过程
mysql> delimiter ;
mysql> call insert_dept(100,10);
Query OK, 0 rows affected (0.01 sec)

mysql> select * from dept ;
+----+--------+------------+----------+
| id | deptno | dname      | loc      |
+----+--------+------------+----------+
|  1 |    101 | sqzyWknJJc | uPRPyWny |
|  2 |    102 | JIzyTYkhea | MMwVrWFn |
|  3 |    103 | yJHubWCauW | yDuqsQbL |
|  4 |    104 | zpcqwlOlOo | amifZKyp |
|  5 |    105 | dygjEsdkSJ | PXrTreuK |
|  6 |    106 | sImmyFJrNO | JOaNOEFl |
|  7 |    107 | sclZmoJqML | uOLngSTP |
|  8 |    108 | wKlcBBdKqB | HJlkuqvf |
|  9 |    109 | phTUTIJwhu | ELSeUlwz |
| 10 |    110 | jvArhMohVh | WoFhVhYw |
+----+--------+------------+----------+
10 rows in set (0.00 sec)

mysql> call insert_emp(100001,500000);
Query OK, 0 rows affected (31.37 sec)

image-20211018214549181
image-20211018214549181

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

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

  • 分析步骤

    • 是否支持 , 看看当前的mysql版本是否支持

      mysql> show variables like '%profiling%';
      +------------------------+-------+
      | Variable_name          | Value |
      +------------------------+-------+
      | have_profiling         | YES   |
      | profiling              | OFF   |
      | profiling_history_size | 15    |
      +------------------------+-------+
      3 rows in set, 1 warning (0.00 sec)
      
    • 开启功能,默认是关闭,使用前需要开启

      mysql> set profiling = on ;
      Query OK, 0 rows affected, 1 warning (0.00 sec)
      
      mysql> show variables like '%profiling%';
      +------------------------+-------+
      | Variable_name          | Value |
      +------------------------+-------+
      | have_profiling         | YES   |
      | profiling              | ON    |
      | profiling_history_size | 15    |
      +------------------------+-------+
      3 rows in set, 1 warning (0.00 sec)
      
    • 运行SQL

    • 查看结果,show profiles

    mysql> show profiles ;
    +----------+------------+------------------------------------------------------------------+
    | Query_ID | Duration   | Query                                                            |
    +----------+------------+------------------------------------------------------------------+
    |        1 | 0.00142325 | show variables like '%profiling%'                                |
    |        2 | 0.00311650 | select * from tbl_emp                                            |
    |        3 | 0.00040100 | select * from tbl_emp                                            |
    |        4 | 0.00018025 | SELECT DATABASE()                                                |
    |        5 | 0.00356750 | select * from tbl_emp                                            |
    |        6 | 0.00046875 | select * from tbl_emp e inner join tbl_dept on e.deptid = d.id   |
    |        7 | 0.00317325 | select * from tbl_emp e inner join tbl_dept d on e.deptid = d.id |
    |        8 | 0.00029500 | select * from tbl_emp e inner join tbl_dept d on e.deptid = d.id |
    |        9 | 0.00031975 | select * from tbl_emp e left join tbl_dept d on e.deptid = d.id  |
    |       10 | 0.00016175 | SELECT DATABASE()                                                |
    |       11 | 0.48647650 | select * from emp group by id%10 limit 150000                    |
    |       12 | 0.46590675 | select * from emp group by id%20 order by 5                      |
    +----------+------------+------------------------------------------------------------------+
    12 rows in set, 1 warning (0.00 sec)
    
    • 诊断sql,show profile cpu ,block io for query 上一步前面的问题sql数字号码 ;
    mysql> show profile cpu , block io for query 7;
    +--------------------------------+----------+----------+------------+--------------+---------------+
    | Status                         | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |
    +--------------------------------+----------+----------+------------+--------------+---------------+
    | starting                       | 0.000054 | 0.000000 |   0.000000 |         NULL |          NULL |
    | Executing hook on transaction  | 0.000003 | 0.000000 |   0.000000 |         NULL |          NULL |
    | starting                       | 0.000005 | 0.000000 |   0.000000 |         NULL |          NULL |
    | checking permissions           | 0.000002 | 0.000000 |   0.000000 |         NULL |          NULL |
    | checking permissions           | 0.000003 | 0.000000 |   0.000000 |         NULL |          NULL |
    | Opening tables                 | 0.000041 | 0.000000 |   0.000000 |         NULL |          NULL |
    | init                           | 0.000004 | 0.000000 |   0.000000 |         NULL |          NULL |
    | System lock                    | 0.000007 | 0.000000 |   0.000000 |         NULL |          NULL |
    | optimizing                     | 0.000012 | 0.000000 |   0.000000 |         NULL |          NULL |
    | statistics                     | 0.000027 | 0.000000 |   0.000000 |         NULL |          NULL |
    | preparing                      | 0.000017 | 0.000000 |   0.000000 |         NULL |          NULL |
    | executing                      | 0.002927 | 0.000000 |   0.000000 |         NULL |          NULL |
    | end                            | 0.000005 | 0.000000 |   0.000000 |         NULL |          NULL |
    | query end                      | 0.000002 | 0.000000 |   0.000000 |         NULL |          NULL |
    | waiting for handler commit     | 0.000007 | 0.000000 |   0.000000 |         NULL |          NULL |
    | closing tables                 | 0.000006 | 0.000000 |   0.000000 |         NULL |          NULL |
    | freeing items                  | 0.000041 | 0.000000 |   0.000000 |         NULL |          NULL |
    | cleaning up                    | 0.000013 | 0.000000 |   0.000000 |         NULL |          NULL |
    +--------------------------------+----------+----------+------------+--------------+---------------+
    
    #看看耗时多的 找结论
    

    image-20211019231456204
    image-20211019231456204

    • 日常开发需要注意的结论
      • converting heap to myisam 查询结果太大 , 内存不够用了往磁盘上搬
      • creating temp table 创建临时表
        • 拷贝数据到临时表
        • 用完再删除
      • copying to tmp table on dist 把内存中临时表复制到磁盘,危险!!
      • locked
#使用temp临时表很消耗时间 要创建 拷贝 处理 释放
mysql> show profile cpu , block io for query 11;
+--------------------------------+----------+----------+------------+--------------+---------------+
| Status                         | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |
+--------------------------------+----------+----------+------------+--------------+---------------+
| starting                       | 0.000078 | 0.000000 |   0.000000 |         NULL |          NULL |
| Executing hook on transaction  | 0.000005 | 0.000000 |   0.000000 |         NULL |          NULL |
| starting                       | 0.000005 | 0.000000 |   0.000000 |         NULL |          NULL |
| checking permissions           | 0.000004 | 0.000000 |   0.000000 |         NULL |          NULL |
| Opening tables                 | 0.000264 | 0.000000 |   0.000000 |         NULL |          NULL |
| init                           | 0.000005 | 0.000000 |   0.000000 |         NULL |          NULL |
| System lock                    | 0.000005 | 0.000000 |   0.000000 |         NULL |          NULL |
| optimizing                     | 0.000003 | 0.000000 |   0.000000 |         NULL |          NULL |
| statistics                     | 0.000015 | 0.000000 |   0.000000 |         NULL |          NULL |
| preparing                      | 0.000011 | 0.000000 |   0.000000 |         NULL |          NULL |
| Creating tmp table             | 0.000055 | 0.000000 |   0.000000 |         NULL |          NULL |
| executing                      | 0.485898 | 0.421875 |   0.046875 |         NULL |          NULL |
| end                            | 0.000010 | 0.000000 |   0.000000 |         NULL |          NULL |
| query end                      | 0.000003 | 0.000000 |   0.000000 |         NULL |          NULL |
| waiting for handler commit     | 0.000017 | 0.000000 |   0.000000 |         NULL |          NULL |
| closing tables                 | 0.000007 | 0.000000 |   0.000000 |         NULL |          NULL |
| freeing items                  | 0.000074 | 0.000000 |   0.000000 |         NULL |          NULL |
| cleaning up                    | 0.000019 | 0.000000 |   0.000000 |         NULL |          NULL |
+--------------------------------+----------+----------+------------+--------------+---------------+
18 rows in set, 1 warning (0.00 sec)
全局查询日志
  • 配置启用

image-20211019232353049
image-20211019232353049

  • 编码启用

image-20211019232435273
image-20211019232435273

mysql> set global general_log = 1 ;
Query OK, 0 rows affected (0.01 sec)

mysql> set global log_output = 'TABLE' ;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from dept ;
+----+--------+------------+----------+
| id | deptno | dname      | loc      |
+----+--------+------------+----------+
|  1 |    101 | sqzyWknJJc | uPRPyWny |
|  2 |    102 | JIzyTYkhea | MMwVrWFn |
|  3 |    103 | yJHubWCauW | yDuqsQbL |
|  4 |    104 | zpcqwlOlOo | amifZKyp |
|  5 |    105 | dygjEsdkSJ | PXrTreuK |
|  6 |    106 | sImmyFJrNO | JOaNOEFl |
|  7 |    107 | sclZmoJqML | uOLngSTP |
|  8 |    108 | wKlcBBdKqB | HJlkuqvf |
|  9 |    109 | phTUTIJwhu | ELSeUlwz |
| 10 |    110 | jvArhMohVh | WoFhVhYw |
+----+--------+------------+----------+
10 rows in set (0.01 sec)

mysql> select * from mysql.general_log  ;
+----------------------------+------------------------------+-----------+-----------+--------------+------------------------------------------------------------------+
| event_time                 | user_host                    | thread_id | server_id | command_type | argument                                                         |
+----------------------------+------------------------------+-----------+-----------+--------------+------------------------------------------------------------------+
| 2021-10-19 23:25:31.577792 | root[root] @ localhost [::1] |        12 |         1 | Query        | 0x73656C656374202A2066726F6D2064657074                           |
| 2021-10-19 23:25:44.682913 | root[root] @ localhost [::1] |        12 |         1 | Query        | 0x73656C656374202A2066726F6D206D7973716C2E67656E6572616C5F6C6F67 |
+----------------------------+------------------------------+-----------+-----------+--------------+------------------------------------------------------------------+
2 rows in set (0.00 sec)

mysql> set global general_log = 0 ;
Query OK, 0 rows affected (0.00 sec)

mysql> alter table mysql.general_log modify column argument varchar(100) not null ;
Query OK, 3 rows affected (0.04 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> set global general_log = 1 ;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from mysql.general_log  ;
+----------------------------+------------------------------+-----------+-----------+--------------+---------------------------------+
| event_time                 | user_host                    | thread_id | server_id | command_type | argument                        |
+----------------------------+------------------------------+-----------+-----------+--------------+---------------------------------+
| 2021-10-19 23:25:31.577792 | root[root] @ localhost [::1] |        12 |         1 | Query        | select * from dept              |
| 2021-10-19 23:25:44.682913 | root[root] @ localhost [::1] |        12 |         1 | Query        | select * from mysql.general_log |
| 2021-10-19 23:28:59.776726 | root[root] @ localhost [::1] |        12 |         1 | Query        | set global general_log = 0      |
+----------------------------+------------------------------+-----------+-----------+--------------+---------------------------------+
3 rows in set (0.01 sec)
mysql锁机制

锁是计算机协调多个进程或线程并发访问某一资源的机制

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

image-20211019233809036
image-20211019233809036

  • 锁的分类

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

    • 表锁(偏读)
      • 特点 偏向myisam存储引擎,开销小,加锁快,无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低
    #建表
    mysql> create table mylock(
        -> id int not null primary key auto_increment,
        -> name varchar(20)
        -> )engine myisam;
    Query OK, 0 rows affected (0.02 sec)
    
    #查看哪些上锁mysql> show open tables ;
    +--------------------+---------------------------+--------+-------------+
    | Database           | Table                     | In_use | Name_locked |
    +--------------------+---------------------------+--------+-------------+
    | mysql              | check_constraints         |      0 |           0 |
    | mysql              | column_type_elements      |      0 |           0 |
    
    #加读写锁
    mysql> lock table mylock read , emp write ;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> show open tables ;
    +--------------------+---------------------------+--------+-------------+
    | Database           | Table                     | In_use | Name_locked |
    +--------------------+---------------------------+--------+-------------+
    | mysql              | check_constraints         |      0 |           0 |
    | mysql              | column_type_elements      |      0 |           0 |
    | bigdata            | emp                       |      1 |           0 |
    | mysql              | general_log               |      0 |           0 |
    | bigdata            | mylock                    |      1 |           0 |
    +--------------------+---------------------------+--------+-------------+
    
    #释放锁
    mysql> unlock tables ;
    Query OK, 0 rows affected (0.00 sec)
    

    加读锁

    powershell 中加锁

    mysql> lock table mylock read ;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> select * from mylock ;
    +----+------+
    | id | name |
    +----+------+
    |  1 | a    |
    |  2 | b    |
    |  3 | c    |
    |  4 | d    |
    |  5 | e    |
    +----+------+
    5 rows in set (0.00 sec)
    

    cmd中也能读

    mysql> use bigdata ;
    Database changed
    mysql> select * from mylock ;
    +----+------+
    | id | name |
    +----+------+
    |  1 | a    |
    |  2 | b    |
    |  3 | c    |
    |  4 | d    |
    |  5 | e    |
    +----+------+
    5 rows in set (0.01 sec)
    

    但powershell中加了读锁,不能对该表进行写操作,也就是增删改操作,也不能对其他表进行crud操作

    mysql> update mylock set name = 'a2' where id = 1 ;
    ERROR 1099 (HY000): Table 'mylock' was locked with a READ lock and can't be updated
    mysql> insert into mylock values(6,'ljs');
    ERROR 1099 (HY000): Table 'mylock' was locked with a READ lock and can't be updated
    mysql> delete from mylock where id = 5 ;
    ERROR 1099 (HY000): Table 'mylock' was locked with a READ lock and can't be updated
    mysql> select * from emp ;
    ERROR 1100 (HY000): Table 'emp' was not locked with LOCK TABLES
    

    而cmd不能对该表进行写的操作,可以对该表进行读操作,写操作会阻塞,直到powershell中释放锁,但能对其他表进行crud操作

    mysql>  update mylock set name = 'a2' where id = 1 ;
    ^C -- query aborted
    ERROR 1317 (70100): Query execution was interrupted
    mysql> insert into mylock values(6,'ljs');
    ^C -- query aborted
    ERROR 1317 (70100): Query execution was interrupted
    mysql>  delete from mylock where id = 5 ;
    ^C -- query aborted
    ERROR 1317 (70100): Query execution was interrupted
    mysql> select * from emp ;
    +--------+--------+--------+----------+-----+------------+---------+--------+--------+
    | id     | empno  | ename  | job      | mgr | hiredate   | sal     | comm   | deptno |
    +--------+--------+--------+----------+-----+------------+---------+--------+--------+
    |      1 | 100002 | lPrnpM | SALESMAN |   1 | 2021-10-18 | 2000.00 | 400.00 |    107 |
    |      2 | 100003 | NuIiSO | SALESMAN |   1 | 2021-10-18 | 2000.00 | 400.00 |    103 |
    

    当powershell解锁后,cmd立刻得到执行

    #powershell端
    mysql> unlock tables ;
    Query OK, 0 rows affected (0.01 sec)
    
    #cmd端  等到解锁后立刻执行
    mysql>  update mylock set name = 'a2' where id = 1 ;
    Query OK, 1 row affected (19.80 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    

    image-20211020200516110
    image-20211020200516110

    image-20211020200614618
    image-20211020200614618

    加写锁

    powershell加写锁,其他未锁的表都访问不了

    mysql> lock table mylock write ;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> select * from mylock ;
    +----+------+
    | id | name |
    +----+------+
    |  1 | a2   |
    |  2 | b    |
    |  3 | c    |
    |  4 | d    |
    |  5 | e    |
    +----+------+
    5 rows in set (0.01 sec)
    
    mysql> update mylock set name = 'a4' where id =1 ;
    Query OK, 1 row affected (0.01 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    mysql> select * from emp ;
    ERROR 1100 (HY000): Table 'emp' was not locked with LOCK TABLES
    

    cmd访问该表,发生阻塞,直至powershell解锁

    mysql> select * from mylock ;
    +----+------+
    | id | name |
    +----+------+
    |  1 | a4   |
    |  2 | b    |
    |  3 | c    |
    |  4 | d    |
    |  5 | e    |
    +----+------+
    5 rows in set (2.44 sec)
    

    image-20211020201254172
    image-20211020201254172

    image-20211020201310837
    image-20211020201310837

    image-20211020201455840
    image-20211020201455840

    表锁分析

    • show open tables ; 哪些表被锁了
    • 通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定;
    • show status like ’table%’ ;
    mysql> show status like 'table%' ;
    +----------------------------+-------+
    | Variable_name              | Value |
    +----------------------------+-------+
    | Table_locks_immediate      | 104   |
    | Table_locks_waited         | 0     |
    | Table_open_cache_hits      | 89    |
    | Table_open_cache_misses    | 24    |
    | Table_open_cache_overflows | 0     |
    +----------------------------+-------+
    

    image-20211020203053434
    image-20211020203053434

    • 行锁
      • innodb存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高
      • innodb与myisam的最大不同有两点:一是支持事务transactioon;二是采用行级锁

    事务

    事务及acid属性

    image-20211020203617056
    image-20211020203617056

    并发事务处理带来的问题

    • 更新丢失

    image-20211020203848386
    image-20211020203848386

    • 脏读

    image-20211020204010474
    image-20211020204010474

    • 不可重复读

    image-20211020204034952
    image-20211020204034952

    • 幻读

    image-20211020204053892
    image-20211020204053892

    事务的并发隔离级别

    • read uncommitted
    • read committed
    • repeatable read
    • serializable

    image-20211020204239692
    image-20211020204239692

    mysql> show variables like 'transaction_isolation' ;
    +-----------------------+-----------------+
    | Variable_name         | Value           |
    +-----------------------+-----------------+
    | transaction_isolation | REPEATABLE-READ |
    +-----------------------+-----------------+
    1 row in set, 1 warning (0.01 sec)
    

    示例

    mysql> create table test_innodb_lock(a int(11),b varchar(16))engine=innodb;
    Query OK, 0 rows affected, 1 warning (0.04 sec)
    
    mysql> create index test_innodb_a_ind on test_innodb_lock(a);
    Query OK, 0 rows affected (0.02 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    
    mysql> create index test_innodb_lock_b_ind on test_innodb_lock(b);
    Query OK, 0 rows affected (0.02 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    

    powershell中修改数据

    mysql> set autocommit = 0 ;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> update test_innodb_lock set b = '4001' where a = 4 ;
    Query OK, 1 row affected (0.01 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    

    为保证数据一致性 在cmd中 拿到的肯定还是4000

    mysql> select * from test_innodb_lock ;
    +------+------+
    | a    | b    |
    +------+------+
    |    1 | b2   |
    |    3 | 3    |
    |    4 | 4000 |
    |    5 | 5000 |
    |    6 | 6000 |
    |    7 | 7000 |
    |    8 | 8000 |
    |    9 | 9000 |
    |    1 | b1   |
    +------+------+
    

    powershell修改数据

    mysql> update test_innodb_lock set b = '4002' where a = 4 ;
    Query OK, 1 row affected (0.01 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    

    如果不提交 就发生了行锁 cmd中等到powershell中提交了 才会更新 否则会阻塞

    mysql> update test_innodb_lock set b = '4003' where a = 4 ;
    Query OK, 1 row affected (19.16 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    mysql> commit ;
    Query OK, 0 rows affected (0.01 sec)
    

    动不同的行是没有问题的

    image-20211020210157165
    image-20211020210157165

    无索引行锁升级为表锁

    powershell中由于varchar没有加单引号‘’ 导致类型转换发生了索引失效

    mysql> set autocommit = 0 ;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> update test_innodb_lock set a = 41 where b = 4000 ;
    Query OK, 0 rows affected (0.01 sec)
    Rows matched: 0  Changed: 0  Warnings: 0
    

    理论上只是行锁,但是索引失效导致行锁升级为了表锁,操作其他行也发现表被锁住了

    mysql> select * from test_innodb_lock ;
    +------+------+
    | a    | b    |
    +------+------+
    |    1 | b2   |
    |    3 | 3    |
    |    4 | 4003 |
    |    5 | 5000 |
    |    6 | 6000 |
    |    7 | 7000 |
    |    8 | 8000 |
    |    9 | 9000 |
    |    1 | b1   |
    +------+------+
    9 rows in set (0.01 sec)
    
    mysql> update test_innodb_lock set b = '9002' where a =9 ;
    Query OK, 1 row affected (21.24 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    

    间隙锁

    cmd中更新一个范围,还没提交

    mysql> update test_innodb_lock set  b = '0629' where a>1 and a<6 ;
    Query OK, 3 rows affected (0.01 sec)
    Rows matched: 3  Changed: 3  Warnings: 0
    
    mysql> commit ;
    Query OK, 0 rows affected (0.01 sec)
    

    powershell中插入的话要等到cmd中commit的时候才能插入,否则会阻塞

    mysql> insert into test_innodb_lock values (2,'2000');
    Query OK, 1 row affected (23.87 sec)
    

    什么是间隙锁

    当我们使用范围条件而不是相等条件检索数据,并请求共享或排他锁时,innodb会给符合条件的已有数据记录的索引项加锁;对于键值再条件范围内但并不存在的记录,叫做间隙GAP

    innodb也会对这个间隙加锁,这种锁机制就是所谓的间隙锁NETT –KEY锁

    危害

    因为query执行过程中通过范围查找的话,它会锁定整个范围内所有的索引键值,即使这个键值并不存在

    间隙锁有一个比较致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成锁定的时候无法插入锁定键值范围内的任何数据,在某些场景下这可能会对性能造成很大的危害

    image-20211022212523821
    image-20211022212523821

    如何锁定一行

    select xxx … for update 锁定某一行后,其他的操作会被阻塞,直到锁定行的会话提交commit

    cmd使用begin … for update 来锁定一行

    mysql> begin ;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> select * from test_innodb_lock where a = 8 for update ;
    +------+------+
    | a    | b    |
    +------+------+
    |    8 | 8000 |
    +------+------+
    1 row in set (0.01 sec)
    
    mysql> commit ;
    Query OK, 0 rows affected (0.01 sec)
    

    powershell不能对该行做改动,除非cmd提交了commit才会释放锁

    mysql> update test_innodb_lock set b = 'xxx' where a = 8 ;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    mysql> update test_innodb_lock set b = 'xxx' where a = 8 ;
    Query OK, 1 row affected (2.17 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    mysql> commit ;
    Query OK, 0 rows affected (0.01 sec)
    
    案例结论

    innodb存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会更高一点,但是在整体并发处理能力方面要远远优于myisam的表级锁定的.当系统并发量较高的时候,innodb的整体性能和myisam相比就会有比较明显的优势了.

    但是,innodb的行级锁定同样也有其脆弱的一面,当我们使用不当的时候,可能会让innodb的整体性能表现不仅不能比myisam高,甚至可能会更差.

    如何分析行锁定

    通过检查innodb_row_lock变量来分析系统上的行锁的争夺情况

    mysql> show status like 'innodb_row_lock%' ;
    +-------------------------------+--------+
    | Variable_name                 | Value  |
    +-------------------------------+--------+
    | Innodb_row_lock_current_waits | 0      |
    | Innodb_row_lock_time          | 117831 |
    | Innodb_row_lock_time_avg      | 23566  |
    | Innodb_row_lock_time_max      | 51419  |
    | Innodb_row_lock_waits         | 5      |
    +-------------------------------+--------+
    5 rows in set (0.01 sec)
    

    image-20211022214707680
    image-20211022214707680

    优化建议
    • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁

    • 合理设计索引,尽量缩小锁的范围

    • 尽可能减少检索条件,避免使用间隙锁

    • 尽量控制事务大小,减少锁定资源量和时间长度

    • 尽可能低级别事务隔离

      页锁 开销和加锁时间介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般

主从复制
  • 复制的基本原理
    • slave会从master读取binlog来进行数据同步
    • 三步骤+原理图
  • 复制的基本原则
    • 每个slave只有一个master
    • 每个slave只能有一个唯一的服务器ID
    • 每个master可以有多个slave
  • 复制的最大问题
    • 延时
  • 一主一从常见配置
    • mysql版本一致且后台以服务运行
    • 主从都配置在[mysqld]节点下,都是小写
    • 主机修改my.ini配置文件
      • [必须]主服务器唯一ID
        • server-id =1 ;
      • [必须]启用二进制日志
        • log-bin = 自己本地的路径/mysqlbin
        • log-bin =C:\ProgramData\MySQL\MySQL Server 8.0\Data\LAPTOP-FKVMDBSI-bin
      • [可选]启用错误日志
        • log-err = 自己本地的路径/mysqlerr
        • log-err=C:\ProgramData\MySQL\MySQL Server 8.0\Data\LAPTOP-FKVMDBSI
      • [可选]根目录
        • basedir=“自己本地路径”
        • basedir=“C:/Program Files/MySQL/MySQL Server 8.0/”
      • [可选]临时目录 好像不行再8.0版本中
        • tmpdir=“自己本地路径”
        • tmpdir=“C:/Program Files/MySQL/MySQL Server 8.0/”
      • [可选]数据目录
        • data=“自己本地路径/Data/”
        • datadir=C:/ProgramData/MySQL/MySQL Server 8.0\Data
      • read-only = 0
        • 主机,读写都可以
      • [可选]设置不要复制的数据库
        • binlog-ignore-db=不需要复制的主数据库名字
      • [可选]设置需要复制的数据库
        • binlog-do-db=需要复制的主数据库名字
    • 从机修改my.cnf配置文件
      • [必选]从服务器唯一ID
      • [可选]启用二进制日志
    • 因修改过配置文件,请主机+从机都重启后台mysql服务
    • 主机从机都关闭防火墙
      • windows手动关闭
      • 关闭虚拟机linux防火墙 service iptables stop
    • 在windows主机上建立账户并授权slave
      • create user ‘zhangsan’@‘192.168.202.64’ identified by ‘123456’;
      • grant replication slave on *.* to ‘zhangsan’ @ ‘192.168.202.64’ ;
      • 8.0 不支持grant的时候用identified by
      • flush privileges ; 刷新权限
      • 查询master的状态
        • show master status ;
        • 记录下file和position的值
    • 在linux从机上配置需要复制的主机
      • change master to master_host=‘ip地址’ , master_user =‘授权的用户’ , master_password = ‘密码’ , master_log_file=‘mysqlbin.具体数字’,master_log_pos = 具体值;
      • change master to master_host=‘192.168.202.64’ , master_user =‘zhangsan’ , master_password = ‘123456’ , master_log_file=‘LAPTOP-FKVMDBSI-bin.000105’,master_log_pos = 156;
      • 启动从服务器复制功能
        • start slave ;
      • show slave status \G ;
        • 下面两个参数都是yes的话,则说明主从配置成功!
        • slave_io_running :yes
        • slave_sql_running :yes
    • 主机新建库/新建表/insert记录/从机复制
    • 如何停止从服务复制功能
      • stop slave ;

三步骤+原理图

image-20211022215731804
image-20211022215731804

主从复制配置过程

主机

设置主服务器

image-20211023100459506
image-20211023100459506

设置二进制日志文件

image-20211023095228167
image-20211023095228167

设置错误文件

image-20211023095304402
image-20211023095304402

image-20211023100542517
image-20211023100542517

设置读写都可以 设置忽略和需要复制的数据库 设置数据库目录位置

image-20211023101034426
image-20211023101034426

这里有个坑,使用WSL,linux子系统的话不设置端口会导致端口占用,必须把windows上的mysql服务关掉,linuxmysql才能start 所以再配置文件里改一下端口号试试看

image-20211023113130359
image-20211023113130359

弄完重启主机从机的mysql服务

在windows主机中授权 从000104的912行开始抄书 复制的数据库是bigdata 忽略的数据库是mysql

mysql> create user 'zhangsan'@'192.168.202.64' identified by '123456';
Query OK, 0 rows affected (0.02 sec)

mysql> grant replication slave,replication client on *.*  to 'zhangsan'@'192.168.202.64' ;
Query OK, 0 rows affected (0.00 sec)

mysql> flush privileges ;
Query OK, 0 rows affected (0.01 sec)

mysql> show master status ;
+----------------------------+----------+--------------+------------------+-------------------+
| File                       | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+----------------------------+----------+--------------+------------------+-------------------+
| LAPTOP-FKVMDBSI-bin.000104 |      912 | bigdata      | mysql            |                   |
+----------------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

binlog_do_db为空说明都要复制,我重新设置一下

image-20211023115552278
image-20211023115552278

重启服务,查看状态

mysql> show master status ;
+----------------------------+----------+--------------+------------------+-------------------+
| File                       | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+----------------------------+----------+--------------+------------------+-------------------+
| LAPTOP-FKVMDBSI-bin.000105 |      156 |              | mysql            |                   |
+----------------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

接下来进入linux命令行进行操作 通过 Last_IO_Errno 和 Last_SQL_Errno 来排查问题 简单的

mysql> change master to master_host='192.168.202.64' , master_user ='zhangsan' , master_password = '123456' , master_log_file='LAPTOP-FKVMDBSI-bin.000105',master_log_pos =  156;
Query OK, 0 rows affected, 8 warnings (0.00 sec)

mysql> start slave ;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> show slave status \G ;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for source to send event
                  Master_Host: 192.168.202.64
                  Master_User: zhangsan
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: LAPTOP-FKVMDBSI-bin.000105
          Read_Master_Log_Pos: 156
               Relay_Log_File: yourtreedad-relay-bin.000003
                Relay_Log_Pos: 334
        Relay_Master_Log_File: LAPTOP-FKVMDBSI-bin.000105
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 156
              Relay_Log_Space: 549
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File:
           Master_SSL_CA_Path:
              Master_SSL_Cert:
            Master_SSL_Cipher:
               Master_SSL_Key:
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 1
                  Master_UUID: 3a5f9439-a33e-11eb-85f6-002b67a4fefa
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Replica has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind:
      Last_IO_Error_Timestamp:
     Last_SQL_Error_Timestamp:
               Master_SSL_Crl:
           Master_SSL_Crlpath:
           Retrieved_Gtid_Set:
            Executed_Gtid_Set:
                Auto_Position: 0
         Replicate_Rewrite_DB:
                 Channel_Name:
           Master_TLS_Version:
       Master_public_key_path:
        Get_master_public_key: 0
            Network_Namespace:
1 row in set, 1 warning (0.00 sec)

ERROR:
No query specified

image-20211023150356197
image-20211023150356197

起飞!!!

完结撒花~~~

😜😋🤣😊

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy