SQL优化

索引的优势和劣势

索引简单来说是排好序的数据结构,所以它能大幅度的提高查询效率,降低数据排序的成本,降低数据库的IO成本。

但是索引也同样占用很大空间,通常是以索引文件的形式存储在磁盘上,并且更新数据的同时得更新相应的索引。

总的来说:索引能提高查询效率,但降低更新效率,所以经常更新的表尽量不要加索引

索引的分类和索引命令语句

索引分类

  1. 单值索引:一个索引只包含单个列,一个表可以有多个单值索引。
  2. 唯一索引:索引列的值必须唯一,但允许有空值
  3. 复合索引:一个索引包含多个列

一张表建议建立索引不要超过五个

基本语法

  1. 创建:
    (1)CREATE [UNIQUE] INDEX indexname ON mytable(columnname(length));
    (2)ALTER mytable ADD [UNIQUE] INDEX [indexname] ON (columnname(length));
  2. 删除:DROP INDEX [indexname] ON mytable;
  3. 查看:SHOW INDEX FROM table_name\G;
    (\G存在的情况下列形式显示,不存在的情况下行形式显示)
  4. 使用ALTER命令:ALTER TABLE mytable ADD (PRIMARYKEY)((UNIQUE|INDEX|FULLTEXT)indexname)(column_list);
    PRIMARYKEY:唯一索引(主键索引,一般自动添加),索引值唯一,且不能为null。
    UNIQUE:唯一索引,索引值唯一,可出现null,且null可重复出现。
    INDEX:普通索引,索引值可出现多次。
    FULLTEXT:全文索引。

判断是否适合建索引

适合建立索引的情况

  1. 主键自动建立唯一索引
  2. 频繁作为查询条件的字段
  3. 查询中与其它表关联的字段
  4. 查询中排序的字段
  5. 查询中统计或分组的字段

不适合建立索引的情况

  1. 频繁更新的字段(更新不止是更新记录,还得更新索引)
  2. Where语句用不到的字段
  3. 表的记录不多
  4. 频繁增删改的表
  5. 数据列大量重复内容(如:性别,只有 男,女)

一般来说复合索引优于单值索引

性能分析

MySql Query Optimizer

MySql自带的查询优化器,MySql有专门负责优化SELECT语句的优化器模块。

MySql 常见瓶颈

  1. CPU:CPU在饱和的时候,一般发生在数据装入内存或者从磁盘读取数据的时候。
  2. IO:磁盘I/O瓶颈常发生在装入数据远大于内存容量的时候。
  3. 服务器硬件问题。

EXPLAIN(查看执行计划)

使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySql是如何处理你的SQL语句,从中得出如何优化你的SQL语句。

如何使用?

在SQL语句前+EXPLAIN关键字(如:explain select * from userdb)

包含信息

  1. id:select查询的序列号,表示操作顺序,id相同的情况下顺序由上到下,id不同的情况下顺序由大到小。
  2. select_type:查询类型,主要用于区别普通查询,联合查询,嵌套查询等的复合查询。
  3. table:显示这一行数据是关于哪张表
  4. type:从好到怀:system(表只有一行记录,系统表)>const(通过一次索引就找到)>eq_ref(唯一索引扫描)>ref(非唯一索引扫描)>range(只索引给定范围)>index(全索引扫描)>all(全表扫描) 一般来说要保证达到range级别
  5. possible_keys:可能用到的哪些索引
  6. key:实际用到的索引
  7. key_len:索引中使用的字节数,在不损失精确性的情况下,越小越好。
  8. ref:显示索引的哪些列被使用
  9. rows:查找到所需数据的行数
  10. extra: 加分项:USING index , 扣分项:USING filesort、USING temporary(有扣分项的情况下尽量优化)

EXPLAIN实际运用

检验是否有效

  • 创建表
CREATE TABLE IF NOT EXISTS blogs(
id INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
author_id INT(10) UNSIGNED NOT NULL,
category VARCHAR(20) NOT NULL,
views INT(20) NOT NULL,
assist INT(20) NOT NULL,
comments VARCHAR(20) NOT NULL,
title VARCHAR(255) NOT null,
content TEXT NOT NULL);
  • 插入数据
INSERT INTO blogs(author_id,category,views,assist,comments,title,content)VALUES
(1,'java',821,100,'1','java','1'),
(2,'sql',534,100,'2','sql','1'),
(3,'c',1928,100,'3','c','1'),
(4,'c++',851,100,'4','c++','1'),
(5,'c#',89,50,'5','c#','1');
  • 没有建立索引的情况下用EXPLAIN:
EXPLAIN SELECT id, author_id  from blogs where comments = '1' and views > 1 ORDER BY assist desc LIMIT 1;

在这里插入图片描述

  • 查找表中的索引
show index from blogs;

这里只有sql自主建的主键索引
在这里插入图片描述

  • 建立索引
create index idx_blogs_cva on blogs (comments,views,assist);
  • 再查找表中的索引
show index from blogs

在这里插入图片描述

  • 再用EXPLAIN查看性能
EXPLAIN SELECT id, author_id  from blogs where comments = '1' and views > 1 ORDER BY assist desc LIMIT 1;

在这里插入图片描述
这里很明显已经用到了索引,但是Extra里面出现了Using filesort,SQL需要优化

  • 优化方式:删除索引,再建立comments,assist索引(具体原因,后面会说到)
Drop index idx_blogs_cva on blogs;
create index idx_blogs_ca on blogs(comments,assist);
EXPLAIN SELECT id, author_id  from blogs where comments = '1' and views > 1 ORDER BY assist desc LIMIT 1;

在这里插入图片描述

检验如何创建索引更高效

  • 创建表
CREATE TABLE IF NOT EXISTS class(
	class_id INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
	class_card INT(20) UNSIGNED NOT NULL
);

CREATE TABLE IF NOT EXISTS book(
	book_id INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
	book_card INT(20) UNSIGNED NOT NULL
);
  • 插入数据
INSERT INTO book(book_card) VALUES(FLOOR(1+(RAND()*10)));
INSERT INTO book(book_card) VALUES(FLOOR(1+(RAND()*10)));
INSERT INTO book(book_card) VALUES(FLOOR(1+(RAND()*10)));
INSERT INTO book(book_card) VALUES(FLOOR(1+(RAND()*10)));
INSERT INTO book(book_card) VALUES(FLOOR(1+(RAND()*10)));
INSERT INTO book(book_card) VALUES(FLOOR(1+(RAND()*10)));
INSERT INTO book(book_card) VALUES(FLOOR(1+(RAND()*10)));
INSERT INTO book(book_card) VALUES(FLOOR(1+(RAND()*10)));
INSERT INTO book(book_card) VALUES(FLOOR(1+(RAND()*10)));
INSERT INTO book(book_card) VALUES(FLOOR(1+(RAND()*10)));
  • 在没建索引的情况下用EXPLAIN查看两表连接
EXPLAIN SELECT * FROM book RIGHT JOIN class ON class_card = book_card; 

在这里插入图片描述

  • 建立索引优化
ALTER TABLE book ADD INDEX idx_book_c (book_card);
  • 再用EXPLLAIN查看
EXPLAIN SELECT * FROM book RIGHT JOIN class ON class_card = book_card; 

在这里插入图片描述

  • 建立class的索引,再用EXPLAIN
ALTER TABLE class ADD INDEX idx_class_c (class_card);
EXPLAIN SELECT * FROM book RIGHT JOIN class ON class_card = book_card; 

在这里插入图片描述

-删除book索引,再用EXPLAIN

DROP index idx_book_c on book;
EXPLAIN SELECT * FROM book RIGHT JOIN class ON class_card = book_card; 

在这里插入图片描述
很明显效率不如添加book表的索引好
总结一下:right join (右连接) ,用左表建索引效果高于右表,left join (左连接) 同理,即:与连接相对的表建立索引效果较好。 尽量用小结果集驱动大结果集(如右连接,就是左表驱动右边,在左表建立索引的效果要好于右表)。

用EXPLAIN得出调优结论

最佳左前缀法则

查询应从索引的最左前列开始并且不跳过索引中的列

  • 首先建立表并插入一些数据,创建表,并用EXPLAIN查看索引是否失效
CREATE TABLE students(
id INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) NOT NULL,
age INT(10) UNSIGNED NOT NULL DEFAULT 0,
grade INT(10) UNSIGNED NOT NULL  DEFAULT 0,
pos VARCHAR(20)  DEFAULT ''
);

INSERT INTO students(name,age,grade,pos) VALUES ('zs',20,97,'monitor');
INSERT INTO students(name,age,grade,pos) VALUES ('ls',22,74,'committee');
INSERT INTO students(name,age,grade,pos) VALUES ('ww',21,100,'committee');
INSERT INTO students(name,age,grade,pos) VALUES ('mw',21,59,'personal');
INSERT INTO students(name,age,grade,pos) VALUES ('zl',21,87,'personal');

ALTER TABLE students ADD INDEX idx_students_nap (name, age, pos);

EXPLAIN SELECT * FROM students WHERE name='zs'; 

在这里插入图片描述

EXPLAIN SELECT * FROM students WHERE name='zs' AND age=20; 

在这里插入图片描述

EXPLAIN SELECT * FROM students WHERE name='zs' AND age=20 AND pos='monitor'; 

在这里插入图片描述
很明显,三条语句依次是增加了精度,同时key_len也增加,但是都用到了索引
现在看看索引失效的情况:

EXPLAIN SELECT * FROM students WHERE  age=20 AND pos='monitor'; 

在这里插入图片描述

EXPLAIN SELECT * FROM students WHERE  pos='monitor'; 

在这里插入图片描述

EXPLAIN SELECT * FROM students WHERE  age=20; 

在这里插入图片描述
这三次效果一样,都是全表扫描

避免函数、计算、类型转换等操作

在select语句里面进行函数、计算、类型转换等操作,会使索引失效,从而变为全表扫描

EXPLAIN SELECT * FROM students WHERE LEFT(name,2) = 'mw';

在这里插入图片描述

EXPLAIN SELECT * FROM students WHERE name = 'mw';	

在这里插入图片描述

尽量不要使用不等于(即:!= 或 <>)

在where子句中使用不等于的时候无法使用索引。

EXPLAIN SELECT * FROM students WHERE name <>'mw';

在这里插入图片描述

尽量不要用’%name’ 或’%name%'的LIKE操作('name%'可用)

EXPLAIN SELECT * FROM students WHERE name like'%mw';

在这里插入图片描述

EXPLAIN SELECT * FROM students WHERE name like'mw%';

在这里插入图片描述

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值