Mysql高级

shell->MySql

1、Mysql底层的四层构架

1、连接层
比如java层面的JDBC,主要完成一些类似于连接处理,授权认证等,在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程
2、服务层
处理sql语句,对sql进行分析,和优化如确定查询表的顺序,是否利用索引,最后生成相应的执行操作
3、引擎层
存储引擎层,真正的负责了MySql中数据的存储和提取。不同的存储引擎功能不同。常用的MyISAM和InnoDB
4、存储层
数据存储层,主要将数据存储磁盘上,并完成与存储引擎的交互。

2、MyISAM和InnoDB

对比项MyISAMInnoDB
主外键不支持支持
事务不支持支持
行表锁表锁,即使操作一条记录也会锁住整个表。不适合高并发行锁,操作时只锁某一行,不对其他行有影响。适合高并发
缓存只缓存索引,不缓存真实数据不仅缓存索引还要缓存真实数据,对内存要求较高,而且内存大小对性能有决定性的影响
表空间
关注点性能事务
默认安装YY

3、SQL性能

SQL性能分析:

  • 性能下降SQL慢
  • 执行时间长
  • 等待时间长

原因:

  • 查询语句写的烂
  • 索引失效
  • 关联查询太多join(执行等待时间长)
  • 服务器调优和各个参数设置(缓冲,线程数等)

SQL执行顺序

在服务层中执行

对于Mysql而言先从FROM后开始执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d6O5jses-1635784540144)(C:\Users\deku\AppData\Roaming\Typora\typora-user-images\image-20211031145511940.png)]

七种JOIN理论

img

  • 插入数据
mysql> show databases;#查看所有数据库
+--------------------+
| Database           |
+--------------------+
| information_schema |
| wzw                |
+--------------------+
2 rows in set (0.01 sec)

mysql> use wzw #进入指定数据库
Database changed
mysql> show table;
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 '' at line 1
mysql> show tables;
Empty set (0.00 sec)
# 创建表
mysql> CREATE TABLE `tbl_emp` (
    -> `id` int(11) NOT NULL AUTO_INCREMENT,
    -> `name` varchar(20) DEFAULT NULL,
    -> `deptId` int(11) DEFAULT NULL,
    -> PRIMARY KEY (`id`) ,
    -> KEY `fk_dept_id`(`deptId`)
    -> )ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8;
Query OK, 0 rows affected (0.03 sec)
# 查看所有表
mysql> show tables;
+---------------+
| Tables_in_wzw |
+---------------+
| tbl_emp       |
+---------------+
1 row in set (0.00 sec)
# 创建表
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 CHARACTER SET = utf8;
Query OK, 0 rows affected (0.02 sec)

mysql> show tables;
+---------------+
| Tables_in_wzw |
+---------------+
| tbl_dept      |
| tbl_emp       |
+---------------+
2 rows in set (0.00 sec)
# 插入数据
mysql> INSERT INTO tbl_dept(deptName,locAdd) VALUES('RD', 11);
me,locAdd) VALUES('FD', 15);Query OK, 1 row affected (0.00 sec)

mysql> 
mysql> INSERT INTO tbl_dept(deptName,locAdd) VALUES('HR', 12);
Query OK, 1 row affected (0.01 sec)

mysql> 
mysql> INSERT INTO tbl_dept(deptName,locAdd) VALUES('MK', 13);
Query OK, 1 row affected (0.00 sec)

mysql> 
mysql> INSERT INTO tbl_dept(deptName,locAdd) VALUES('MIS', 14);
Query OK, 1 row affected (0.00 sec)

mysql> 
mysql> INSERT INTO tbl_dept(deptName,locAdd) VALUES('FD', 15);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO tbl_emp(NAME, deptId) VALUES('z3', 1);
2);

INSERT INTO tbl_emp(NAME, deptId) VALUES('s7', 3);

INSERT INTO tbl_emp(NAME, deptId) VALUES('s8', 4);

INSERT INTO tbl_emp(NAME, deptId) VALUES('s9', 51);Query OK, 1 row affected (0.01 sec)

mysql> 
mysql> INSERT INTO tbl_emp(NAME, deptId) VALUES('z4', 1);
Query OK, 1 row affected (0.00 sec)

mysql> 
mysql> INSERT INTO tbl_emp(NAME, deptId) VALUES('z5', 1); 
Query OK, 1 row affected (0.01 sec)

mysql> 
mysql> INSERT INTO tbl_emp(NAME, deptId) VALUES('w5', 2);
Query OK, 1 row affected (0.03 sec)

mysql> 
mysql> INSERT INTO tbl_emp(NAME, deptId) VALUES('w6', 2);
Query OK, 1 row affected (0.03 sec)

mysql> 
mysql> INSERT INTO tbl_emp(NAME, deptId) VALUES('s7', 3);
Query OK, 1 row affected (0.01 sec)

mysql> 
mysql> INSERT INTO tbl_emp(NAME, deptId) VALUES('s8', 4);
Query OK, 1 row affected (0.01 sec)

mysql> 
mysql> INSERT INTO tbl_emp(NAME, deptId) VALUES('s9', 51);
Query OK, 1 row affected (0.01 sec)

mysql> show tables;
+---------------+
| Tables_in_wzw |
+---------------+
| tbl_dept      |
| tbl_emp       |
+---------------+
2 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)

1、Inner join

mysql> show tables;
+---------------+
| Tables_in_wzw |
+---------------+
| tbl_dept      |
| tbl_emp       |
+---------------+
2 rows in set (0.00 sec)

mysql> select * from tb1_dept;
ERROR 1146 (42S02): Table 'wzw.tb1_dept' doesn't exist
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)

mysql> select * from tbl_emp;
+----+------+--------+
| id | name | deptId |
+----+------+--------+
|  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 d
    -> inner join tbl_emp e
    -> on d.id=e.deptId;
+----+----------+--------+----+------+--------+
| id | deptName | locAdd | id | name | deptId |
+----+----------+--------+----+------+--------+
|  1 | RD       | 11     |  1 | z3   |      1 |
|  1 | RD       | 11     |  2 | z4   |      1 |
|  1 | RD       | 11     |  3 | z5   |      1 |
|  2 | HR       | 12     |  4 | w5   |      2 |
|  2 | HR       | 12     |  5 | w6   |      2 |
|  3 | MK       | 13     |  6 | s7   |      3 |
|  4 | MIS      | 14     |  7 | s8   |      4 |
+----+----------+--------+----+------+--------+
7 rows in set (0.00 sec)

mysql>

2、left join

mysql> select * from tbl_dept d
    -> left join tbl_emp e
    -> on d.id=e.deptId;
+----+----------+--------+------+------+--------+
| id | deptName | locAdd | id   | name | deptId |
+----+----------+--------+------+------+--------+
|  1 | RD       | 11     |    1 | z3   |      1 |
|  1 | RD       | 11     |    2 | z4   |      1 |
|  1 | RD       | 11     |    3 | z5   |      1 |
|  2 | HR       | 12     |    4 | w5   |      2 |
|  2 | HR       | 12     |    5 | w6   |      2 |
|  3 | MK       | 13     |    6 | s7   |      3 |
|  4 | MIS      | 14     |    7 | s8   |      4 |
|  5 | FD       | 15     | NULL | NULL |   NULL |
+----+----------+--------+------+------+--------+
8 rows in set (0.00 sec)

3、left join 去掉inner join

mysql> select * from tbl_dept d
    -> left join tbl_emp e
    -> on d.id =e.deptId
    -> where e.deptId is null;
+----+----------+--------+------+------+--------+
| id | deptName | locAdd | id   | name | deptId |
+----+----------+--------+------+------+--------+
|  5 | FD       | 15     | NULL | NULL |   NULL |
+----+----------+--------+------+------+--------+
1 row in set (0.00 sec)

4、Right join

mysql> select * from tbl_dept d
    -> right join tbl_emp e
    -> on d.id=e.deptId;
+------+----------+--------+----+------+--------+
| id   | deptName | locAdd | id | name | deptId |
+------+----------+--------+----+------+--------+
|    1 | RD       | 11     |  1 | z3   |      1 |
|    1 | RD       | 11     |  2 | z4   |      1 |
|    1 | RD       | 11     |  3 | z5   |      1 |
|    2 | HR       | 12     |  4 | w5   |      2 |
|    2 | HR       | 12     |  5 | w6   |      2 |
|    3 | MK       | 13     |  6 | s7   |      3 |
|    4 | MIS      | 14     |  7 | s8   |      4 |
| NULL | NULL     | NULL   |  8 | s9   |     51 |
+------+----------+--------+----+------+--------+
8 rows in set (0.00 sec)

5、Right join 去掉inner join

mysql> select * from tbl_dept d
    -> right join tbl_emp e
    -> on d.id =e.deptId
    -> where d.id is null;
+------+----------+--------+----+------+--------+
| id   | deptName | locAdd | id | name | deptId |
+------+----------+--------+----+------+--------+
| NULL | NULL     | NULL   |  8 | s9   |     51 |
+------+----------+--------+----+------+--------+
1 row in set (0.00 sec)

6、Full outer join Mysql不支持;union;

mysql> select * from tbl_dept d
    -> full outer join tbl_emp e
    -> on d.id =e.deptId
    -> ;
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 'full outer join tbl_emp e
on d.id =e.deptId' at line 2

变通:union:合并加去重

mysql> select * from tbl_dept d
    -> right join tbl_emp e
    -> on d.id =e.deptId
    -> union
    -> select * from tbl_dept d
    -> left join tbl_emp e
    -> on d.id =e.deptId;
+------+----------+--------+------+------+--------+
| id   | deptName | locAdd | id   | name | deptId |
+------+----------+--------+------+------+--------+
|    1 | RD       | 11     |    1 | z3   |      1 |
|    1 | RD       | 11     |    2 | z4   |      1 |
|    1 | RD       | 11     |    3 | z5   |      1 |
|    2 | HR       | 12     |    4 | w5   |      2 |
|    2 | HR       | 12     |    5 | w6   |      2 |
|    3 | MK       | 13     |    6 | s7   |      3 |
|    4 | MIS      | 14     |    7 | s8   |      4 |
| NULL | NULL     | NULL   |    8 | s9   |     51 |
|    5 | FD       | 15     | NULL | NULL |   NULL |
+------+----------+--------+------+------+--------+
9 rows in set (0.00 sec)


7、查询A,B各自的独有

mysql> select * from tbl_dept d                                                                                          
    -> left join tbl_emp e                                                                                               
    -> on d.id =e.deptId
    -> where e.deptId is null
    -> union 
    -> select * from tbl_dept d
    -> right join tbl_emp e
    -> on d.id =e.deptId
    -> where d.id is null;
+------+----------+--------+------+------+--------+
| id   | deptName | locAdd | id   | name | deptId |
+------+----------+--------+------+------+--------+
|    5 | FD       | 15     | NULL | NULL |   NULL |
| NULL | NULL     | NULL   |    8 | s9   |     51 |
+------+----------+--------+------+------+--------+
2 rows in set (0.00 sec)

4、索引(index)是什么

  • 索引(index):是帮助mysql高效获取数据的数据结构

  • 索引就是数据结构

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

  • 除数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法,这种数据结构就是索引

  • 索引以索引文件的形式存储在磁盘上

一般我们常用的索引,都是B树结构组织的索引

其中聚集索引,次要索引,覆盖索引,前缀索引,唯一索引都是默认的B+树索引

除B+树这种索引之外,还有一种哈希索引(hashIndex)等

优势:

  • 提高数据检索的效率,降低数据库的IO成本
  • 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的成本

劣势

  • 实际索引也是一张表,该表保存了主键和索引字段,并指向实体表的记录,是要占用空间的
  • 虽然索引大大提高了检索的效率,缺减少了更新表的的速度。 因为更新表时,Mysql不仅要保存数据,还要保存以下索引文件每次更新添加了索引列的字段

1、索引分类

  • 单值索引

即一个索引只包含单个列,一个表可以包含多个单列索引

  • 唯一索引

索引列的值必须唯一,但允许有空值(比如说银行卡号)

  • 复合索引

即一个索引包含多个列

2、基本语法

indexName 索引名

myTable 表名

columnname(length) 字段

  • 创建

CREATE [union] INDEX indexName ON myTable(columnname(length));

ALTER mytable ADD [union] INDEX [indexName] on (columnname(length))

  • 删除

DROP INDEX [indexName] ON mytable;

  • 查看

SHOW INDEX FROM table_ name;

3、索引结构

  • BTree索引

检索原理:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LqNrgrU5-1635784540148)(C:\Users\deku\AppData\Roaming\Typora\typora-user-images\image-20211031170320984.png)]

  • Hash索引
  • full-text全文索引
  • R-Tree索引

4、哪些情况下需要创建索引

  • 主键 自动建立唯一索引
  • 频繁作为查询条件的字段应该创建索引
  • 查询中与其他表关联的字段,外键关系建立索引
  • 频繁更新的字段不适合创建索引
  • where条件里用不到的字段不创建索引
  • 单键/组合索引的选择问题,who?(在高并发下倾向创建组合索引)
  • 查询中统计或者分组字段(count,group by)

5、哪些情况下不创建索引

  • 表记录太少
  • 经常增删改的表
  • 数据重复且平均分布的表字段

6、性能分析

1.MySql Query Optimizer: mysql服务层中的性能优化器

  • mysql中有专门负责优化SELECT语句的优化器模块,主要功能:通过计算分析系统中收集到的统计信息,为客户端请求QUERY提供他认为最优的执行计划
  • 当客户端向MySql请求一条Query,命令解析器模块完成请求分类,区别出SELECT并转发给MySql Query Optimizer时,MySql Query Optimizer首先会对整条Query进行优化。

2、MySql常见瓶颈:

  • CPU :CPU 饱和的时候一般发生在数据装入内存或从磁盘上读取数据的时候
  • IO:磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
  • 服务器硬件的性能瓶颈:top,free,iostat,vmstat,来查看系统的性能状态

3、Explain

5、Explain

什么是Explain:使用EXPLAIN关键字可以模拟优化器执行执行SQL查询语句,从而知道MySql是如何处理你的sql语句的。分析你的查询语句或是表结构的性能瓶颈

mysql> explain select * from tbl_emp;
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | tbl_emp | ALL  | NULL          | NULL | NULL    | NULL |    8 | NULL  |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
1 row in set (0.00 sec)
mysql> explain select * from tbl_dept d
    -> right join tbl_emp e
    -> on d.id =e.deptId;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                                              |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
|  1 | SIMPLE      | e     | ALL  | NULL          | NULL | NULL    | NULL |    8 | NULL                                               |
|  1 | SIMPLE      | d     | ALL  | PRIMARY       | NULL | NULL    | NULL |    5 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
2 rows in set (0.00 sec)

1、id

表的读取和加载顺序

  • select查询的一个序列号,包含了一组数字,表示查询中执行select字句或操作表的顺序
  • id值有三种情况
    • id相同,执行顺序由上至下
    • id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
    • id相同不同,同时存在:id相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行

2、select_type和table

常见值:主要用来区别,普通查询,联合查询,子查询等的复杂查询

  • SIMPLE:简单的 select 查询,查询中不包含子查询和 UNION
  • PRIMARY:查询若包含人和复杂的子部分,最外层的部分则被标记为 PRIMARY
  • SUBQUERY:在 SELECT 或 WHERE列表中包含了子查询
  • DERIUDE:在FROM列表中包含的子查询被标记为DERIUDE(衍生),mysql会递归这些子查询,把结果放在临时表里
  • UNION:若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM子句的子查询中,外层SELECT将被标记为 DERIUDE
  • UNION RESULT:从union表获取结果的SELECT
mysql> explain select * from tbl_dept d left join tbl_emp e  on d.id =e.deptId where e.deptId is null 
    -> union
    -> select * from tbl_dept d right join tbl_emp e on d.id =e.deptId  where d.id is null; 
+----+--------------+------------+------+---------------+------------+---------+----------+------+----------------------------------------------------------------+
| id | select_type  | table      | type | possible_keys | key        | key_len | ref      | rows | Extra                                                          |
+----+--------------+------------+------+---------------+------------+---------+----------+------+----------------------------------------------------------------+
|  1 | PRIMARY      | d          | ALL  | NULL          | NULL       | NULL    | NULL     |    5 | NULL                                                           |
|  1 | PRIMARY      | e          | ref  | fk_dept_id    | fk_dept_id | 5       | wzw.d.id |    1 | Using where                                                    |
|  2 | UNION        | e          | ALL  | NULL          | NULL       | NULL    | NULL     |    8 | NULL                                                           |
|  2 | UNION        | d          | ALL  | PRIMARY       | NULL       | NULL    | NULL     |    5 | Using where; Not exists; Using join buffer (Block Nested Loop) |
| NULL | UNION RESULT | <union1,2> | ALL  | NULL          | NULL       | NULL    | NULL     | NULL | Using temporary                                                |
+----+--------------+------------+------+---------------+------------+---------+----------+------+----------------------------------------------------------------+
5 rows in set (0.07 sec)

  • table:就是表示当前执行属于哪张表

3、type

访问类型排列

显示查询使用了何种类型,

从最好到最差依次是:

system>const>eq_ref>ref>range>index>ALL

  • system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,可以忽略
  • const:表示通过索引一次就找到了,const 用于比较primary key或者unique索引。因为只匹配一行数据,所以很快
mysql> explain select * from tbl_emp where id=1;
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table   | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | tbl_emp | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL  |
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+
1 row in set (0.00 sec)

  • eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
mysql> explain select * from tbl_emp,tbl_dept where tbl_emp.id=tbl_dept.id;
+----+-------------+----------+--------+---------------+---------+---------+-----------------+------+-------+
| id | select_type | table    | type   | possible_keys | key     | key_len | ref             | rows | Extra |
+----+-------------+----------+--------+---------------+---------+---------+-----------------+------+-------+
|  1 | SIMPLE      | tbl_dept | ALL    | PRIMARY       | NULL    | NULL    | NULL            |    5 | NULL  |
|  1 | SIMPLE      | tbl_emp  | eq_ref | PRIMARY       | PRIMARY | 4       | wzw.tbl_dept.id |    1 | NULL  |
+----+-------------+----------+--------+---------------+---------+---------+-----------------+------+-------+
2 rows in set (0.00 sec)

  • ref:非唯一性索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,他返回所有匹配某个单独值的行,然而,他可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体
  • range:只检索给定范围的行,使用一个索引来选择行。key列显示了使用那个索引。一般就是在你的where语句中出现了between、<、>、in的查询。这种范围扫描要比全表扫描要好,因为他只需要开始与索引的某一个点,而结束于另一个点,不用扫描全部索引
  • index: Full Index Scan,index与All都是读全表,区别是index类型之遍历索引数,总的来说index是从索引中读取的,而All是从硬盘中读取的
mysql> explain select id from tbl_emp;
+----+-------------+---------+-------+---------------+------------+---------+------+------+-------------+
| id | select_type | table   | type  | possible_keys | key        | key_len | ref  | rows | Extra       |
+----+-------------+---------+-------+---------------+------------+---------+------+------+-------------+
|  1 | SIMPLE      | tbl_emp | index | NULL          | fk_dept_id | 5       | NULL |    8 | Using index |
+----+-------------+---------+-------+---------------+------------+---------+------+------+-------------+
1 row in set (0.00 sec)

  • ALL:就是SELECT * 查询所有数据

一般来说,得保证查询至少达到 range 级别

4、possible_keys和key和key_len

  • possible_keys:

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

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

  • key:表示实际上使用到的索引,

  • key_len:

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

表示索引中使用的字节数,可通过该列计算查询中使用索引的长度。在不损失精确度的情况下,长度越短越好

key_len 显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是通过表定义计算得出,不是通过表内检索得出的

5、Extra

  • Using filesort
  • Using temporary
  • USING index
  • Using where
  • using join buffer 使用了连接缓存
  • impossible where
  • select tables optimized away
  • distainct

6、索引分析

单表

  • 建表
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 (0.02 sec)

mysql> show tables;
+---------------+
| Tables_in_wzw |
+---------------+
| article       |
| tbl_dept      |
| tbl_emp       |
+---------------+
3 rows in set (0.01 sec)

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'),
    -> (3,3,3,3,'3','3'),
    -> (1,1,1,3,'3','3'),
Query OK, 3 rows affected (0.00 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 | 1     | 1       |
|  2 |         2 |           2 |     2 |        2 | 2     | 2       |
|  3 |         3 |           3 |     3 |        3 | 3     | 3       |
|  4 |         1 |           1 |     1 |        3 | 3     | 3       |
+----+-----------+-------------+-------+----------+-------+---------+
3 rows in set (0.00 sec)

mysql> 

案例:

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

mysql> SELECT id,author_id FROM article WHERE category_id=1 AND comments >1 ORDER BY views DESC LIMIT 1;                 
+----+-----------+
| id | author_id |
+----+-----------+
|  4 |         1 |
+----+-----------+
1 row in set (0.00 sec)

#查看性能
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   | type | possible_keys | key  | key_len | ref  | rows | Extra                       |
+----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
|  1 | SIMPLE      | article | ALL  | NULL          | NULL | NULL    | NULL |    4 | Using where; Using filesort |
+----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
1 row in set (0.00 sec)

#查看索引
mysql> show index from article;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| article |          0 | PRIMARY  |            1 | id          | A         |           4 |     NULL | NULL   |      | BTREE      |         |               |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

#开始优化-建索引:category_id,comments,views
mysql> create index idx_article_ccv on article(category_id,comments,views);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

# 查看索引
mysql> show index from article;
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name        | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| article |          0 | PRIMARY         |            1 | id          | A         |           4 |     NULL | NULL   |      | BTREE      |         |               |
| article |          1 | idx_article_ccv |            1 | category_id | A         |           4 |     NULL | NULL   |      | BTREE      |         |               |
| article |          1 | idx_article_ccv |            2 | comments    | A         |           4 |     NULL | NULL   |      | BTREE      |         |               |
| article |          1 | idx_article_ccv |            3 | views       | A         |           4 |     NULL | NULL   |      | BTREE      |         |               |
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.00 sec)

# 查看效果:range,但是还是有Using filesort的问题(原因:comments >1 大于号出现问题)
mysql> explain SELECT id,author_id FROM article WHERE category_id=1 AND comments >1 ORDER BY views DESC LIMIT 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: article
         type: range
possible_keys: idx_article_ccv
          key: idx_article_ccv
      key_len: 8
          ref: NULL
         rows: 1
        Extra: Using index condition; Using filesort
1 row in set (0.00 sec)
# >号变为= 号 Using filesort消失 type变为ref(找到问题)
# 原因:大于号表示的范围会导致索引失效
mysql> explain SELECT id,author_id FROM article WHERE category_id=1 AND comments =1 ORDER BY views DESC LIMIT 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: article
         type: ref
possible_keys: idx_article_ccv
          key: idx_article_ccv
      key_len: 8
          ref: const,const
         rows: 1
        Extra: Using where
1 row in set (0.00 sec)

# 不改变题意,另寻思路,删除索引
mysql>  DROP INDEX idx_article_ccv ON article;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0
# 重建索引,去掉commits字段上的索引
mysql> create index idx_article_cv on article(category_id,views);
Query OK, 0 rows affected (0.04 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\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: article
         type: ref
possible_keys: idx_article_cv
          key: idx_article_cv
      key_len: 4
          ref: const
         rows: 2
        Extra: Using where
1 row in set (0.00 sec)

两表

  • 建表:记住左连接索引加右表,又连接索引加左表
CREATE TABLE IF NOT EXISTS `class`(
`id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`card` INT (10) UNSIGNED NOT NULL
);
CREATE TABLE IF NOT EXISTS `book`(
`bookid` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`card` INT (10) UNSIGNED NOT NULL
);
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));

INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));

  • 分析性能,问题来了,我们应该在哪张表上加索引呢
# type=All,出现了 Using join buffer (Block Nested Loop),性能太差
mysql> explain select * from class left join book on class.card = book.card\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: class
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 20
        Extra: NULL
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: book
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 20
        Extra: Using where; Using join buffer (Block Nested Loop)
2 rows in set (0.00 sec)

#第一种情况,在book表Card字段上上加索引 
mysql> create index Y on book(card);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

# 我们可以看到Using join buffer (Block Nested Loop)消失了,
# book表的type=ref,但是class的type还是等于all
mysql> explain select * from class left join book on class.card = book.card\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: class
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 20
        Extra: NULL
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: book
         type: ref
possible_keys: Y
          key: Y
      key_len: 4
          ref: wzw.class.card
         rows: 1
        Extra: Using index
2 rows in set (0.00 sec)

# 第二种情况,在class上加索引
 mysql> drop index Y on book;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> create index Y on class(card);
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0
# class的type变为index,没有ref效果好
mysql> explain select * from class left join book on class.card = book.card\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: class
         type: index
possible_keys: NULL
          key: Y
      key_len: 4
          ref: NULL
         rows: 20
        Extra: Using index
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: book
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 20
        Extra: Using where; Using join buffer (Block Nested Loop)
2 rows in set (0.00 sec)

对于左连接:

  • 在左表和右表的连接字段上都不建立索引:左右表都是全表扫描,查询效率最低;
    仅在左表的连接字段上建立索引:左表使用了覆盖索引扫描,扫描行数没变,查询效率得到优化;b表使用了全表扫描,且扫描行数没变,查询效率未得到优化;
  • 仅在右表连接字段上建索引: 左表表使用了全表扫描,扫描行数没变,查询效率未得到优化;右表使用了非唯一性索引单值扫描ref,扫描行数减少,查询晓得得到优化;
  • 在左表和右表的连接字段上都建索引后: 左表使用了覆盖索引扫描,扫描行数没变,查询效率得到优化;右表使用了非唯一性索引单值扫描ref,扫描行数减少,查询效率得到优化;

对于内连接

  • 在左表和右表的连接字段上都不建立索引:左右表都是全表扫描,查询效率最低;
    仅在左表的连接字段上建立索引: 左表使用了覆盖索引扫描,扫描行数减少,查询效率得到优化;右表使用了全表扫描,扫描行数没变,查询效率没有得到优化;
  • 仅在右表连接字段上建索引: 左表使用了全表扫描,扫描行数没变,查询效率未优化; 右表使用了非唯一性索引单值扫描,扫描行数减少,查询效率得到优化;
  • 在左表a和右表b的连接字段上都建索引后:左表使用了覆盖索引,扫描行数不变,查询效率得到优化;右表使用了非唯一性索引扫描,扫描行数减少,查询效率得到优化;

三表

  • 建表
CREATE TABLE IF NOT EXISTS `phone`(
`phoneid` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`card` INT (10) UNSIGNED NOT NULL
)ENGINE = INNODB;

INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));

# 在book,phone上建索引
mysql> create index Y on phone(card);
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

# 三表查询
mysql> select * from class
    -> left join book 
    -> on class.card=book.card
    -> left join phone 
    -> on book.card=phone.card;
# 查看性能
mysql> explain select * from class inner join book on class.card=book.card left join phone on book.card=phone.card;
+----+-------------+-------+------+---------------+------+---------+----------------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref            | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+----------------+------+-------------+
|  1 | SIMPLE      | class | ALL  | NULL          | NULL | NULL    | NULL           |   20 | NULL        |
|  1 | SIMPLE      | book  | ref  | M             | M    | 4       | wzw.class.card |    1 | Using index |
|  1 | SIMPLE      | phone | ref  | Y             | Y    | 4       | wzw.class.card |    1 | Using index |
+----+-------------+-------+------+---------------+------+---------+----------------+------+-------------+
3 rows in set (0.00 sec)

结论

join语句的优化

  • 尽可能减少Join语句中NestedLoop的循环总次数:“永远用小的结果集驱动大的结果集”。
  • 优先优化NestedLoop的内层循环
  • 保证Join语句被驱动表上的join条件字段已经被索引
  • 当无法保证被驱动表的join条件字段被索引且内存资源充足的下,不要太吝啬JoinBuffer的设置

7、索引优化(重点)

  • 全值匹配我最爱
  • 最佳左前缀法则
  • 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换)
  • 存储引擎不能使用索引中范围条件右边的列
  • 尽量使用覆盖索引,(只访问索引的查询(索引列和查询)),减少 select *
  • mysql 在使用不等于 (!= 或者<>) 的时候无法使用索引会导致全表扫描
  • is null 和 is not null 也无法使用索引
  • like以通配符开头(‘%abc…’)mysql索引失效会变成全表扫描
  • 字符串不加单引号索引失效
  • 少用or,用它来连接时会索引失效
CREATE TABLE staffs(
id INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(24)NOT NULL DEFAULT'' COMMENT'姓名',
`age` INT NOT NULL DEFAULT 0 COMMENT'年龄',
`pos` VARCHAR(20) NOT NULL DEFAULT'' COMMENT'职位',
`add_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT'入职时间'
)CHARSET utf8 COMMENT'员工记录表';
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('z3',22,'manager',NOW());
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('July',23,'dev',NOW());
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('2000',23,'dev',NOW());
#建立索引
mysql> ALTER TABLE staffs ADD INDEX index_staffs_nameAgePos(`name`,`age`,`pos`);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

#mysql 在使用不等于 (!= 或者<>) 的时候无法使用索引会导致全表扫描
mysql> explain select * from staffs where name='z3' and age<>23 and pos='dev';
+----+-------------+--------+------+-------------------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys           | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+-------------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | index_staffs_nameAgePos | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+-------------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

dd_time) VALUES('z3',22,'manager',NOW()); INSERT INTO staffs(name,age,pos,add_time) VALUES('July',23,'dev',NOW()); INSERT INTO staffs(name,age,pos,add_time`) VALUES(‘2000’,23,‘dev’,NOW());


```shell
#建立索引
mysql> ALTER TABLE staffs ADD INDEX index_staffs_nameAgePos(`name`,`age`,`pos`);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

#mysql 在使用不等于 (!= 或者<>) 的时候无法使用索引会导致全表扫描
mysql> explain select * from staffs where name='z3' and age<>23 and pos='dev';
+----+-------------+--------+------+-------------------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys           | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+-------------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | index_staffs_nameAgePos | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+-------------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值