参考:
https://blog.csdn.net/kqqkqq123/article/details/98057744
https://www.cnblogs.com/ljl150/p/12934071.html
https://blog.csdn.net/weixin_43268933/article/details/108505662
https://www.cnblogs.com/gomysql/p/3720123.html
https://www.cnblogs.com/jjpbk/p/11734690.html
目录:
最左前缀匹配原则
EXPLAIN 命令详解
数据库与数据仓库的区别
结构化数据库与非结构化数据库
最左前缀原则
MySQL中的索引可以以一定顺序引用多列,这种索引叫作联合索引。如User表的name和city加联合索引就是(name,city),而最左前缀原则指的是,如果查询的时候查询条件精确匹配索引的左边连续一列或几列,则此列就可以被用到。如下:
select * from user where name=xx and city=xx ; //可以命中索引 select * from user where name=xx ; // 可以命中索引 select * from user where city=xx ; // 无法命中索引
这里需要注意的是,查询的时候如果两个条件都用上了,但是顺序不同,如 city= xx and name =xx
,那么现在的查询引擎会自动优化为匹配联合索引的顺序,这样是能够命中索引的。
由于最左前缀原则,在创建联合索引时,索引字段的顺序需要考虑字段值去重之后的个数,较多的放前面。ORDER BY子句也遵循此规则。
面试中常被提到的最左前缀匹配原则
最左前缀匹配原则:在MySQL建立联合索引时会遵守最左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。
要想理解联合索引的最左匹配原则,先来理解下索引的底层原理。索引的底层是一颗B+树,那么联合索引的底层也就是一颗B+树,只不过联合索引的B+树节点中存储的是键值。由于构建一棵B+树只能根据一个值来确定索引关系,所以数据库依赖联合索引最左的字段来构建。
举例:创建一个(a,b)的联合索引,那么它的索引树就是下图的样子。
可以看到a的值是有顺序的,1,1,2,2,3,3,而b的值是没有顺序的1,2,1,4,1,2。但是我们又可发现a在等值的情况下,b值又是按顺序排列的,但是这种顺序是相对的。这是因为MySQL创建联合索引的规则是首先会对联合索引的最左边第一个字段排序,在第一个字段的排序基础上,然后在对第二个字段进行排序。所以b=2这种查询条件没有办法利用索引。
由于整个过程是基于explain结果分析的,那接下来在了解下explain中的type字段和key_lef字段。
1.type:联接类型。下面给出各种联接类型,按照从最佳类型到最坏类型进行排序:(重点看ref,rang,index)
system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,可以忽略不计
const:表示通过索引一次就找到了,const用于比较primary key 或者 unique索引。因为只需匹配一行数据,所有很快。如果将主键置于where列表中,mysql就能将该查询转换为一个const
eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键 或 唯一索引扫描。
注意:ALL全表扫描的表记录最少的表如t1表
ref:非唯一性索引扫描,返回匹配某个单独值的所有行。本质是也是一种索引访问,它返回所有匹配某个单独值的行,然而他可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体。
range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了那个索引。一般就是在where语句中出现了bettween、<、>、in等的查询。这种索引列上的范围扫描比全索引扫描要好。只需要开始于某个点,结束于另一个点,不用扫描全部索引。
index:Full Index Scan,index与ALL区别为index类型只遍历索引树。这通常为ALL块,应为索引文件通常比数据文件小。(Index与ALL虽然都是读全表,但index是从索引中读取,而ALL是从硬盘读取)
ALL:Full Table Scan,遍历全表以找到匹配的行
2.key_len:显示MySQL实际决定使用的索引的长度。如果索引是NULL,则长度为NULL。如果不是NULL,则为使用的索引的长度。所以通过此字段就可推断出使用了那个索引。
计算规则:
1.定长字段,int占用4个字节,date占用3个字节,char(n)占用n个字符。
2.变长字段varchar(n),则占用n个字符+两个字节。
3.不同的字符集,一个字符占用的字节数是不同的。Latin1编码的,一个字符占用一个字节,gdk编码的,一个字符占用两个字节,utf-8编码的,一个字符占用三个字节。
(由于我数据库使用的是Latin1编码的格式,所以在后面的计算中,一个字符按一个字节算)
4.对于所有的索引字段,如果设置为NULL,则还需要1个字节。
接下来进入正题!!!
示例:
首先创建一个表
该表中对id列.name列.age列建立了一个联合索引 id_name_age_index,实际上相当于建立了三个索引(id)(id_name)(id_name_age)。
下面介绍下可能会使用到该索引的几种情况:
1.全值匹配查询时
通过观察上面的结果图可知,where后面的查询条件,不论是使用(id,age,name)(name,id,age)还是(age,name,id)顺序,在查询时都使用到了联合索引,可能有同学会疑惑,为什么底下两个的搜索条件明明没有按照联合索引从左到右进行匹配,却也使用到了联合索引? 这是因为MySQL中有查询优化器explain,所以sql语句中字段的顺序不需要和联合索引定义的字段顺序相同,查询优化器会判断纠正这条SQL语句以什么样的顺序执行效率高,最后才能生成真正的执行计划,所以不论以何种顺序都可使用到联合索引。另外通过观察上面三个图中的key_len字段,也可说明在搜索时使用的联合索引中的(id_name_age)索引,因为id为int型,允许null,所以占5个字节,name为char(10),允许null,又使用的是latin1编码,所以占11个字节,age为int型允许null,所以也占用5个字节,所以该索引长度为21(5+11+5),而上面key_len的值也正好为21,可证明使用的(id_name_age)索引。
2.匹配最左边的列时
该搜索是遵循最左匹配原则的,通过key字段也可知,在搜索过程中使用到了联合索引,且使用的是联合索引中的(id)索引,因为key_len字段值为5,而id索引的长度正好为5(因为id为int型,允许null,所以占5个字节)。
由于id到name是从左边依次往右边匹配,这两个字段中的值都是有序的,所以也遵循最左匹配原则,通过key字段可知,在搜索过程中也使用到了联合索引,但使用的是联合索引中的(id_name)索引,因为key_len字段值为16,而(id_name)索引的长度正好为16(因为id为int型,允许null,所以占5个字节,name为char(10),允许null,又使用的是latin1编码,所以占11个字节)。
由于上面三个搜索都是从最左边id依次向右开始匹配的,所以都用到了id_name_age_index联合索引。
那如果不是依次匹配呢?
通过key字段可知,在搜索过程中也使用到了联合索引,但使用的是联合索引中的(id)索引,从key_len字段也可知。因为联合索引树是按照id字段创建的,但age相对于id来说是无序的,只有id只有序的,所以他只能使用联合索引中的id索引。
通过观察发现上面key字段发现在搜索中也使用了id_name_age_index索引,可能许多同学就会疑惑它并没有遵守最左匹配原则,按道理会索引失效,为什么也使用到了联合索引?因为没有从id开始匹配,且name单独来说是无序的,所以它确实不遵循最左匹配原则,然而从type字段可知,它虽然使用了联合索引,但是它是对整个索引树进行了扫描,正好匹配到该索引,与最左匹配原则无关,一般只要是某联合索引的一部分,但又不遵循最左匹配原则时,都可能会采用index类型的方式扫描,但它的效率远不如最做匹配原则的查询效率高,index类型类型的扫描方式是从索引第一个字段一个一个的查找,直到找到符合的某个索引,与all不同的是,index是对所有索引树进行扫描,而all是对整个磁盘的数据进行全表扫描。
这两个结果跟上面的是同样的道理,由于它们都没有从最左边开始匹配,所以没有用到联合索引,使用的都是index全索引扫描。
3.匹配列前缀
如果id是字符型,那么前缀匹配用的是索引,中坠和后缀用的是全表扫描。
select * from staffs where id like 'A%';//前缀都是排好序的,使用的都是联合索引 select * from staffs where id like '%A%';//全表查询 select * from staffs where id like '%A';//全表查询
4.匹配范围值
在匹配的过程中遇到<>=号,就会停止匹配,但id本身就是有序的,所以通过possible_keys字段和key_len 字段可知,在该搜索过程中使用了联合索引的id索引(因为id为int型,允许null,所以占5个字节),且进行的是rang范围查询。
由于不遵循最左匹配原则,且在id<4的范围中,age是无序的,所以使用的是index全索引扫描。
不遵循最左匹配原则,但在数据库中id<2的只有一条(id),所以在id<2的范围中,age是有序的,所以使用的是rang范围查询。
不遵循最左匹配原则,而age又是无序的,所以进行的全索引扫描。
5.准确匹配第一列并范围匹配其他某一列
由于搜索中有id=1,所以在id范围内age是无序的,所以只使用了联合索引中的id索引。
最左匹配原则的底层原理
什么是最左匹配原则
顾名思义:最左优先,以最左边的为起点任何连续的索引都能匹配上。同时遇到范围查询(>、<、between、like)就会停止匹配。
例如:如果建立(a,b)顺序的索引,我们的条件只有b=xxx,是匹配不到(a,b)索引的;但是如果查询条件是a = 1 and b = 2或者b=2 and a=1就可以,因为优化器会自动调整a,b的顺序,并不需要严格按照索引的顺序来;再比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,因为c字段是一个范围查询,它之后的字段会停止匹配
为什么会形成最左匹配原则
首先要知道,最左匹配原则都是针对联合索引来说的,所以我们有必要了解一下联合索引的原理。了解了联合索引,那么为什么会有最左匹配原则这种说法也就理解了。
我们都知道索引的底层是一颗B+树,那么联合索引当然还是一颗B+树,只不过联合索引的健值数量不是一个,而是多个。构建一颗B+树只能根据一个值来构建,因此数据库依据联合索引最左的字段来构建B+树。
例子:假如创建一个(a,b)的联合索引,那么它的索引树是这样的
可以看到a的值是有顺序的,1,1,2,2,3,3,而b的值是没有顺序的1,2,1,4,1,2。所以b = 2这种查询条件没有办法利用索引,因为联合索引首先是按a排序的,b是无序的。
同时我们还可以发现在a值相等的情况下,b值又是按顺序排列的,但是这种顺序是相对的。所以最左匹配原则遇上范围查询就会停止,剩下的字段都无法使用索引。例如a = 1 and b = 2 a,b字段都可以使用索引,因为在a值确定的情况下b是相对有序的,而a>1and b=2,a字段可以匹配上索引,但b值不可以,因为a的值是一个范围,在这个范围中b是无序的。
EXPLAIN 命令详解
在工作中,我们用于捕捉性能问题最常用的就是打开慢查询,定位执行效率差的SQL,那么当我们定位到一个SQL以后还不算完事,我们还需要知道该SQL的执行计划,比如是全表扫描,还是索引扫描,这些都需要通过EXPLAIN去完成。EXPLAIN命令是查看优化器如何决定执行查询的主要方法。可以帮助我们深入了解MySQL的基于开销的优化器,还可以获得很多可能被优化器考虑到的访问策略的细节,以及当运行SQL语句时哪种策略预计会被优化器采用。需要注意的是,生成的QEP并不确定,它可能会根据很多因素发生改变。MySQL不会将一个QEP和某个给定查询绑定,QEP将由SQL语句每次执行时的实际情况确定,即便使用存储过程也是如此。尽管在存储过程中SQL语句都是预先解析过的,但QEP仍然会在每次调用存储过程的时候才被确定。
通过执行计划可以知道什么?
(root@yayun-mysql-server) [test]>explain select d1.age, t2.id from (select age,name from t1 where id in (1,2))d1, t2 where d1.age=t2.age group by d1.age, t2.id order by t2.id; +----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+ | 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 2 | Using temporary; Using filesort | | 1 | PRIMARY | t2 | ref | age | age | 5 | d1.age | 1 | Using where; Using index | | 2 | DERIVED | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where | +----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+ 3 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
MySQL执行计划调用方式
1.EXPLAIN SELECT ……
变体:
2.EXPLAIN EXTENDED SELECT ……
将执行计划"反编译"成SELECT语句,运行SHOW WARNINGS
可得到被MySQL优化器优化后的查询语句
3.EXPLAIN PARTITIONS SELECT ……
用于分区表的EXPLAIN生成QEP的信息
执行计划包含的信息
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
1. id:
包含一组数字,表示查询中执行select子句或操作表的顺序
Example(id相同,执行顺序由上至下)
(root@yayun-mysql-server) [test]>explain select t2.* from t1, t2, t3 where t1.id=t2.id and t1.id=t3.id and t1.name=''; +----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+ | 1 | SIMPLE | t1 | ref | PRIMARY,name | name | 63 | const | 1 | Using where; Using index | | 1 | SIMPLE | t2 | eq_ref | PRIMARY | PRIMARY | 4 | test.t1.id | 1 | | | 1 | SIMPLE | t3 | eq_ref | PRIMARY | PRIMARY | 4 | test.t1.id | 1 | Using index | +----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+ 3 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
Example (如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行)
(root@yayun-mysql-server) [test]>explain select t2.* from t2 where id = (select id from t1 where id = (select t3.id from t3 where t3.name='')); +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+ | 1 | PRIMARY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE noticed after reading const tables | | 2 | SUBQUERY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | no matching row in const table | | 3 | SUBQUERY | t3 | ref | name | name | 63 | | 1 | Using where; Using index | +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+ 3 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
Example(id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行)
(root@yayun-mysql-server) [test]>explain select t2.* from (select t3.id from t3 where t3.name='')s1, t2 where s1.id=t2.id; +----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+ | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | | | 1 | PRIMARY | t2 | const | PRIMARY | PRIMARY | 4 | const | 1 | | | 2 | DERIVED | t3 | ref | name | name | 63 | | 1 | Using where; Using index | +----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+ 3 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
2. select_type
示查询中每个select子句的类型(简单OR复杂)
a. SIMPLE:查询中不包含子查询或者UNION
b. 查询中若包含任何复杂的子部分,最外层查询则被标记为:PRIMARY
c. 在SELECT或WHERE列表中包含了子查询,该子查询被标记为:SUBQUERY
d. 在FROM列表中包含的子查询被标记为:DERIVED(衍生)用来表示包含在from子句中的子查询的select,mysql会递归执行并将结果放到一个临时表中。服务器内部称为"派生表",因为该临时表是从子查询中派生出来的
e. 若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
f. 从UNION表获取结果的SELECT被标记为:UNION RESULT
SUBQUERY和UNION还可以被标记为DEPENDENT和UNCACHEABLE。
DEPENDENT意味着select依赖于外层查询中发现的数据。
UNCACHEABLE意味着select中的某些 特性阻止结果被缓存于一个item_cache中。
Example
(root@yayun-mysql-server) [test]>explain select d1.name, ( select id from t3) d2 from (select id,name from t1 where name='')d1 union (select name,id from t2); +----+--------------+------------+--------+---------------+------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------+------------+--------+---------------+------+---------+------+------+--------------------------+ | 1 | PRIMARY | <derived3> | system | NULL | NULL | NULL | NULL | 0 | const row not found | | 3 | DERIVED | t1 | ref | name | name | 63 | | 1 | Using where; Using index | | 2 | SUBQUERY | t3 | index | NULL | age | 5 | NULL | 6 | Using index | | 4 | UNION | t2 | index | NULL | name | 63 | NULL | 4 | Using index | | NULL | UNION RESULT | <union1,4> | ALL | NULL | NULL | NULL | NULL | NULL | | +----+--------------+------------+--------+---------------+------+---------+------+------+--------------------------+ 5 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
第一行:id列为1,表示第一个select,select_type列的primary表 示该查询为外层查询,table列被标记为<derived3>,表示查询结果来自一个衍生表,其中3代表该查询衍生自第三个select查询,即id为3的select。
第二行:id为3,表示该查询的执行次序为2( 4 => 3),是整个查询中第三个select的一部分。因查询包含在from中,所以为derived。
第三行:select列表中的子查询,select_type为subquery,为整个查询中的第二个select。
第四行:select_type为union,说明第四个select是union里的第二个select,最先执行。
第五行:代表从union的临时表中读取行的阶段,table列的<union1,4>表示用第一个和第四个select的结果进行union操作。
3. type
表示MySQL在表中找到所需行的方式,又称“访问类型”,常见类型如下:
ALL, index, range, ref, eq_ref, const, system, NULL
从左到右,性能从最差到最好
Example
a. ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行
(root@yayun-mysql-server) [test]>explain select * from t1 where email=''; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
b. index:Full Index Scan,index与ALL区别为index类型只遍历索引树
(root@yayun-mysql-server) [test]>explain select id from t1; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | index | NULL | age | 5 | NULL | 4 | Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
c. range:索引范围扫描,对索引的扫描开始于某一点,返回匹配值域的行。显而易见的索引范围扫描是带有between或者where子句里带有<, >查询。当mysql使用索引去查找一系列值时,例如IN()和OR列表,也会显示range(范围扫描),当然性能上面是有差异的。
(root@yayun-mysql-server) [test]>explain select * from t1 where id in (1,4); +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>explain select * from t1 where id between 1 and 4; +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 3 | Using where | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>explain select * from t1 where id=1 or id=4; +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.01 sec) (root@yayun-mysql-server) [test]>explain select * from t1 where id > 1; +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 3 | Using where | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
d. ref:使用非唯一索引扫描或者唯一索引的前缀扫描,返回匹配某个单独值的记录行
(root@yayun-mysql-server) [test]>explain select * from t1 where name='yayun'; +----+-------------+-------+------+---------------+------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+-------+------+-------------+ | 1 | SIMPLE | t1 | ref | name | name | 63 | const | 1 | Using where | +----+-------------+-------+------+---------------+------+---------+-------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
e. eq_ref:类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件
(root@yayun-mysql-server) [test]>explain select t1.name from t1, t2 where t1.id=t2.id; +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ | 1 | SIMPLE | t1 | index | PRIMARY | name | 63 | NULL | 4 | Using index | | 1 | SIMPLE | t2 | eq_ref | PRIMARY | PRIMARY | 4 | test.t1.id | 1 | Using index | +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ 2 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
f. const、system:当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量
(root@yayun-mysql-server) [test]>explain select * from ( select * from t1 where id=1)b1; +----+-------------+------------+--------+---------------+---------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+--------+---------------+---------+---------+------+------+-------+ | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | | | 2 | DERIVED | t1 | const | PRIMARY | PRIMARY | 4 | | 1 | | +----+-------------+------------+--------+---------------+---------+---------+------+------+-------+ 2 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
注:system是const类型的特例,当查询的表只有一行的情况下,使用system
g. NULL:MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。
(root@yayun-mysql-server) [test]>explain select * from t1 where id = (select min(id) from t2); +----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+ | 1 | PRIMARY | t1 | const | PRIMARY | PRIMARY | 4 | const | 1 | | | 2 | SUBQUERY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away | +----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+ 2 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
4. possible_keys
指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用
5. key
显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL
Example
(root@yayun-mysql-server) [test]>explain select id,age from t1; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | index | NULL | age | 5 | NULL | 4 | Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
6. key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)
7. ref
表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
8. rows
表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数
Example
(root@yayun-mysql-server) [test]>explain select * from t1 , t2 where t1.id=t2.id and t2.name='atlas'; +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ | 1 | SIMPLE | t2 | ref | PRIMARY,name | name | 63 | const | 1 | Using where | | 1 | SIMPLE | t1 | eq_ref | PRIMARY | PRIMARY | 4 | test.t2.id | 1 | | +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ 2 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
9. Extra
包含不适合在其他列中显示但十分重要的额外信息
a. Using index
该值表示相应的select操作中使用了覆盖索引(Covering Index)
Example
(root@yayun-mysql-server) [test]>explain select id from t1; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | index | NULL | age | 5 | NULL | 4 | Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
覆盖索引(Covering Index)
MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件
包含所有满足查询需要的数据的索引称为覆盖索引(Covering Index)
注意:如果要使用覆盖索引,一定要注意select列表中只取出需要的列,不可select *,因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降
b. Using where
表示mysql服务器将在存储引擎检索行后再进行过滤。许多where条件里涉及索引中的列,当(并且如果)它读取索引时,就能被存储引擎检验,因此不是所有带where字句的查询都会显示"Using where"。有时"Using where"的出现就是一个暗示:查询可受益与不同的索引。
Example
(root@yayun-mysql-server) [test]>explain select id,name from t1 where id<4; +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+ | 1 | SIMPLE | t1 | index | PRIMARY | name | 63 | NULL | 4 | Using where; Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
c. Using temporary
表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询
这个值表示使用了内部临时(基于内存的)表。一个查询可能用到多个临时表。有很多原因都会导致MySQL在执行查询期间创建临时表。两个常见的原因是在来自不同表的上使用了DISTINCT,或者使用了不同的ORDER BY和GROUP BY列。可以强制指定一个临时表使用基于磁盘的MyISAM存储引擎。这样做的原因主要有两个:
1)内部临时表占用的空间超过min(tmp_table_size,max_heap_table_size)系统变量的限制
2)使用了TEXT/BLOB 列
Example
(root@yayun-mysql-server) [test]>explain select id from t1 where id in (1,2) group by age,name; +----+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where; Using temporary; Using filesort | +----+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
d. Using filesort
MySQL中无法利用索引完成的排序操作称为“文件排序”
Example
(root@yayun-mysql-server) [test]>explain select id,age from t1 order by name; +----+-------------+-------+------+---------------+------+---------+------+------+----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+----------------+ | 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 4 | Using filesort | +----+-------------+-------+------+---------------+------+---------+------+------+----------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>explain select id,age from t1 order by age; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | index | NULL | age | 5 | NULL | 4 | Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
e. Using join buffer
改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。
Example
(root@yayun-mysql-server) [test]>explain select t1.name from t1 inner join t2 on t1.name=t2.name; +----+-------------+-------+-------+---------------+------+---------+--------------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+--------------+------+--------------------------+ | 1 | SIMPLE | t1 | index | name | name | 63 | NULL | 4 | Using index | | 1 | SIMPLE | t2 | ref | name | name | 63 | test.t1.name | 2 | Using where; Using index | +----+-------------+-------+-------+---------------+------+---------+--------------+------+--------------------------+ 2 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>alter table t1 drop key name; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 (root@yayun-mysql-server) [test]>alter table t2 drop key name; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 (root@yayun-mysql-server) [test]>explain select t1.name from t1 inner join t2 on t1.name=t2.name; +----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+ | 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 4 | | | 1 | SIMPLE | t2 | ALL | NULL | NULL | NULL | NULL | 4 | Using where; Using join buffer | +----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+ 2 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
f. Impossible where
这个值强调了where语句会导致没有符合条件的行。
Example
(root@yayun-mysql-server) [test]>EXPLAIN SELECT * FROM t1 WHERE 1=2; +----+-------------+-------+------+---------------+------+---------+------+------+------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+------------------+ | 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE | +----+-------------+-------+------+---------------+------+---------+------+------+------------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
h. Select tables optimized away
这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行.
Example
(root@yayun-mysql-server) [test]>explain select max(id) from t1; +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ | 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away | +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
I. Index merges
当MySQL 决定要在一个给定的表上使用超过一个索引的时候,就会出现以下格式中的一个,详细说明使用的索引以及合并的类型。
Using sort_union(...)
Using union(...)
Using intersect(...)
总结:
• EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况
• EXPLAIN不考虑各种Cache
• EXPLAIN不能显示MySQL在执行查询时所作的优化工作
• 部分统计信息是估算的,并非精确值
• EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看执行计划。
数据库与数据仓库的区别
数据库与数据仓库的区别实际讲的是OLTP与OLAP的区别。
操作性处理,叫联机事务处理OLTP(On-Line Transaction Processing),也可以称面向交易的处理系统,他是针对具体业务在数据库联机的日常操作,通常对少数记录进行查询,修改。用户较为关心操作的响应时间,数据的安全性,完整性和并发支持的用户数等问题。传统的数据库系统作为数据管理的主要手段,主要用于操作性处理。
分析性处理,叫联机分析处理OLAP(On-Line Analytical Processing),一般针对某些主题的历史数据进行分析,支持管理决策。
数据仓库的出现并不是要取代数据库。
- 数据库是面向事务的设计,数据仓库是面向主题设计的。
- 数据一般存储业务数据,数据仓库存储的一般是历史数据。
- 数据库设一是尽量避免冗余,一般针对某一业务应用进行设计,比如一张简单的User表,记录用户名,密码等简单数据即可,符合业务应用,但是不符合分析。数据仓库在设计时有意引入冗余,依照分析需求,分析维度,分析指标进行设计。
- 数据库是为捕获数据而设计,数据仓库是为分析数据而设计。
以银行业务为例。数据库是事务系统的数据平台,客户在银行做的每笔交易都会写入数据库,被记录下来,这里,可以简单地理解为用数据库记账。数据仓库是分析系统的数据平台,它从事务系统获取数据,并做汇总、加工,为决策者提供决策的依据。比如,某银行某分行一个月发生多少交易,该分行当前存款余额是多少。如果存款又多,消费交易又多,那么该地区就有必要设立ATM了。
显然,银行的交易量是巨大的,通常以百万甚至千万次来计算。事务系统是实时的,这就要求时效性,客户存一笔钱需要几十秒是无法忍受的,这就要求数据库只能存储很短一段时间的数据。而分析系统是事后的,它要提供关注时间段内所有的有效数据。这些数据是海量的,汇总计算起来也要慢一些,但是,只要能够提供有效的分析数据就达到目的了。
数据仓库,是在数据库已经大量存在的情况下,为了进一步挖掘数据资源、为了决策需要而产生的,它决不是所谓的“大型数据库。
https://news.west.cn/58960.html
结构化数据库与非结构化数据库
在信息社会,信息可以划分为两大类。一类信息能够用数据或统一的结构加以表示,我们称之为结构化数据,如数字、符号;而另一类信息无法用数字或统一的结构表示,如文本、图像、声音、网页等,我们称之为非结构化数据。结构化数据属于非结构化数据,是非结构化数据的特例。
随着网络技术的发展,特别是Internet和Intranet技术的飞快发展,使得非结构化数据的数量日趋增大。这时,主要用于管理结构化数据的关系数据库的局限性暴露地越来越明显。因而,数据库技术相应地进入了“后关系数据库时代”,发展进入基于网络应用的非结构化数据库时代。所谓非结构化数据库,是指数据库的变长纪录由若干不可重复和可重复的字段组成,而每个字段又可由若干不可重复和可重复的子字段组成。简单地说,非结构化数据库就是字段可变的数据库。
目前有两大类型的数据库,一种是结构化SQL数据库,一种非结构化NOSQL数据库。
比拼1:数据的组织形式
SQL, 顾名思义是结构化查询语言。它的数据都是结构化的,这个需要在最初创建数据库的时候要做好设计,这个设计一旦定型以后,再修改的话就会比较麻烦。当然如果设计做得好的话,也就无需再修改了。所以结构化数据最大的一个工作就是表的设计。这是在使用这种数据库的时候,开发工作中的重中之重。
结构化数据的另一个体现就是各种数据之间的关系,比如说1对1的关系,一对多的关系,多对多的关系。另一个体现就是数据的定义严格,在一个表中只能存放一种表数据,也就是说,你的每一行的数据都要遵循这个表的的定义。这个表里的每行的数据都遵循这个表内定义好的数据类型,不能够存放一些所谓非定义的数据,否则出错。
而NOSQL数据库不需要结构化的数据设计,这样它的容错性就很强,也不存在太严格的设计,以后的扩展和修改都比较容易。
NOSQL数据库里面不存在关系这个概念,如果你想实现关系,比如说1对1,一对多,多对多,你需要用程序来实现,而不是用数据库本身来实现。另外一个是一个表中可以存放不同的数据类型, 简单的说就是每一行的数据可以不遵循统一的定义。
比拼2:原子操作
所谓原子操作,就是指一个操作要么成功,要么失败,没有半途而终的。假设说一个处理订单的操作中存在5个步骤,你处理一个订单,提交订单,开始计算数据,随后写入数据库五个表然后,才返回成功,如果有一个失败,那就返回失败。返回失败就意味着撤回之前所有的操作。
这种原子操作在SQL数据库中非常容易实现,它本身就存在这样的机制叫做事务处理机制。这也是我们选择SQL数据库的一个重要参考指标。只要我们在处理数据的过程中存在这样的操作,要么成功,要么失败,那么我们首先要选择的就是SQL数据库。
然而在NOSQL数据库中不存在这样的机制。但是这里追求数据的统一性,比如说你有很多个数据集,这里不称之为数据表了。一旦有一部分修改,你必须更新所有的包含这类数据数据集。
比拼3:效率方面。
结构化数据库有很多方式可以提高数据的处理效率。比如说创建索引,使用存储程序Stored Procedure, 一些架构如entity framework, hibernate。但是因为结构化数据库天然的追求数据的完整性,所以它在效率方面还是存在一些瓶颈的。
然而NOSQL非结构化数据库就不存在这样的问题。因为它关心的就是快速的写入数据,查询数据。虽然有一些数据的冗余,但是它的写入和查询速度都非常快,尤其是在处理巨量数据的时候,这个优势特别明显。但是如果数据集之间的耦合性非常强的话,因为要做到数据的统一,你需要不停的写多个相关的数据集,这样也会大大降低效率。
比拼4:扩展潜力
横向扩展和纵向扩展的区别。
横向扩展是指用多台服务器服务一个数据库,这种扩展的好处就是没有极限。这个对于结构化数据库来说,几乎是不可能的。非结构化数据库就可以做到横向扩展。
纵向扩展是指通过提高硬件性能软件性能来提高整体服务器的性能。这种扩展的劣势就是总会达到极限。当然这种扩展对于结构化数据库和非结构化数据库都是适用的。
小结:那么哪个更好呢?
说实在的,一下子很难说,是结构化数据库更好还是非结构化数据库更好?因为这两种数据库的设计初衷是不一样的。
结构化数据库的目标是追求数据操作的完整性,但是对单机服务器的性能要求比较高。非结构化数据库的设计,追求的是读写的效率和可扩展性,可以实现多机的协作。但是又不注重数据操作的完整性。同时会产生大量的冗余数据。
选择
目前许多大型互联网都会选用MySql+NoSql的组合方案,因为SQL和NoSql都有各自的优缺点。
关系型数据库适合存储结构化数据,比如:用户的账号、地址:
(1)这些数据通常需要做结构化查询,比如说Join,这个时候,关系型数据库就要胜出一筹。
(2)这些数据的规模、增长的速度通常是可以预期的。
(3)事务性、一致性,适合存储比较复杂的数据。
NoSql适合存储非结构化数据,比如:文章、评论:
(1)这些数据通常用于模糊处理,例如全文搜索、机器学习,适合存储较为简单的数据。
(2)这些数据是海量的,并且增长的速度是难以预期的。
(3)按照key获取数据效率很高,但是对于join或其他结构化查询的支持就比较差。