原因可能有以下几种:
(1)SQL语句本身写得不够优秀
(2)索引失效
(3)关联(join)查询太多
(4)服务器参数设置(缓存,线程数等)
……
人类写SQL和理解SQL的顺序大体是:
SELECT DISTICT --> FROM ON (JOIN)-> WHERE -->GROUP BY --> HAVING --> ORDER BY --> LIMIT
而MySQL并不是以这样的顺序来理解,机器的理解顺序是:
FROM ON (JOIN) --> WHERE --> GROUP BY --> HAVING --> SELECT --> DISTINCT --> ORDER BY --> LIMIT
索引是帮助MySQL高效获取数据的数据结构,或者说成是排好序的快速查找的数据结构,完整的定义是:
除数据本身之外,数据库还维护者一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据的基础上实现高效查找算法,这种数据结构就是索引(通常是指B+树索引,另外还存在Hash索引、full-text索引,R-TREE索引)。
索引优势:提高检索数据的效率,降低数据库的IO成本,通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗。
索引劣势:索引实际上也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是会占据空间的;虽然索引大大提高了查询速度,但同时会降低更新表的速度。
创建索引:
CREATE [UNIQUE] INDEX indexNmae ON mytable(columnname(length));
ALTER mytable ADD [UNIQUE] INDEX [indexName] ON (columnnane(length));
删除索引
DROP INDEX [indexNmae] ON mytable;
查看索引:
SHOW INDEX FROM table_name\G;
有四种方式来添加数据表的索引:
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,用于全文索引
如上图所示的B+树,浅蓝色表示一个磁盘块,每个磁盘快包含几个数据项(深蓝色)和指针(黄色)。P1表示小于数据17的磁盘快,P2表示值在17和35之间的磁盘快,P3表示大于35的磁盘快。真实的数据存在于叶子节点,即3、5、9、10、13、15、28、29、36、60、75、79、90、99,非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。
(1)主键自动建立唯一索引
(2)频繁作为查询条件的字段应该创建索引
(3)查询中与其它表关联的字段,外键关系建立索引
(4)频繁更新的字段不适合创建索引
(5)where条件里用不到的字段不创建索引
(6)单键/组合索引的选择问题(在高并发下倾向于创建组合索引)
(7)查询中排序的字段
(8)查询中统计或分组字段
(1)表记录太少
(2)经常增删改的表
(3)表中包含大量重复记录
使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而可以知道MySQL是如何处理SQL语句的,并据此分析查询语句或是表结构的性能瓶颈,使用方法是EXPLAIN + SQL语句。EXPLAIN能够显示的信息如下:
(1)表的读取顺序
(2)数据读取操作的操作类型
(3)哪些索引可以使用
(4)哪些索引被实际使用
(5)表之间的引用
(6)每张表有多少行被优化器查询
执行计划包含的信息有:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | extra |
id: id相同,执行顺序由上至下;如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行;id有相同有不同,如果id相同,可以认为是一组,从上往下顺序执行,在所有组中,id值越大,优先级越高,越先执行。
select_type:查询类型
(1)SIMPLE 简单查询,不包含子查询或者UNION
(2)PRIMARY 查询中若包含任何复杂的子部分,最外层查询被标记为PRIMARY
(3) SUBQUERY 子查询
(4)在FROM列表中包含的子查询被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放在临时表里。
(5)若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM子句的子查询中,外层SELECT将被标记为DERIVED
(6)UNION RESULT: 从UNION表获取结果的SELECT
table:显示数据是关于哪张表
type:访问类型排列,常见的从最好到最差依次是:system > const > eq_ref > ref > range > index > ALL
ALL为什么是最差的呢?ALL表示全表扫描,对于大数据量而言性能是很差的。一般来说,得保证查询至少达到range级别,最好能达到ref。
system—表示表只有一行记录(等于系统表),是const类型的特例,平时不会出现,可忽略不计
const—表示通过索引一次就找到了数据。
eq_ref—唯一索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见于主键索引或唯一索引扫描。
ref—非唯一性索引扫描,返回匹配某个单独值的所有行。
range—只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引,一般就是在where子句中出现了between、<、>、in等查询时会显示range,这种扫描索引比全表扫描要好(开始于索引的某一点,结束于另一点)。
index—扫描索引树,性能比ALL好一些,index扫描全部索引,而ALL遍历磁盘。
ALL—扫描全表,性能差,可以尝试优化。
possible_keys: 显示可能在本表上应用到的索引,但不一定被查询使用。
key: 实际使用的索引,如果为NULL,则表示没有索引或索引失效。查询中若使用了覆盖索引,则索引仅出现在key列表中,possible_keys为NULL。
key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引长度,在不损失精确性的情况下,长度越短越好(要注意,key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)。
ref:显示索引的哪一列被使用了。
rows:根据表统计信息及索引选用情况,大致估算出找到所需记录所需要读取的行数。
Extra::
Using filesort,Using temprorary都是应该避免,建立索引时group by应该尽量和索引匹配。Using index表示相应的SELECT操作中使用了覆盖索引,避免访问了表的数据行,执行效率很高;如果同时出现using where,表明索引被用来执行索引键值得查找;如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。
索引失效的常见情况
(1)全值匹配
(2)违背最佳左前缀法则。最佳左前缀法则是指,查询从索引的最左前列开始并且不跳过索引中的列。
(3)不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
(4)存储引擎不能使用索引中范围条件右边的列
(5)尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select*的使用
(6)MySQL在使用不等于(!= 或者<>)的时候无法使用索引会导致全表扫描
(7)is nul,is not null也无法使用索引
(8)like以通配符开头(‘%abc’),MySQL会发生索引失效导致全表扫描
(9)字符串不加单引号索引会失效
(10)少用or,用它来连接时索引会失效