mysql索引策略和优化

hash索引

在mysql中,只有memory显示支持hash,这也是memory默认索引类型.
hash索引基于hash表实现,只有精确匹配索引所有列的查询才有效。
mysql> select * from testhash;
±-----------±-----+
| fname | lname |
±-----------±-----+
| Arjen | Lentz |
| Baron | Schwartz |
| Peter | Zaitsev |
| Vadim | Tkachenko |
±-----------±-----+
假设索引使用假象的哈希函数f(),返回以下;
f(‘Arjen’)=2323
f(‘Baron’)=7437
f(‘Peter’)=8784
f(‘Vadim’)=2458
则哈希索引的结构如下:
槽(Slot) 值(Value)
2323 指向第一行的指针
2458 指向第二行的指针
7437 指向第三行的指针
8784 指向第四行的指针
-----
注意每个槽的编号是顺序的,但是数据行不是的。现在查询如下;
mysql> select lname from testhash where fname=‘Peter’;
mysql会先计算’Peter’的哈希值,并使用该值寻找对应的记录指针。因为f(‘Peter’)=8784,可以找到指向第三行的指针,最后一步是比较指针里的值是否为’Peter’,以确保就是要查找的行。
ps:
hash索引只包含哈希值和行指针。
hash索引只支持等值比较查询。
------
innodb引擎有一个功能叫做"自适应哈希索引"当Innodb注意到某些索引值被使用得非常频繁时,它会在内存中基于b-tree索引上再创建一个哈希索引。这是一个完全自动内部行为,可以关闭该功能。
------
在不支持的存储引擎创建hash索引可以模拟innodb一样。在b-tree基础上创建一个伪哈希索引。它使用哈希值而不是键本身进行索引查找。你只需要在查询的where句子中手动指定哈希函数。
例如需要存储大量的url,并根据url进行搜索查找。如果用b-tree存储url,内容就会很大。正常情况下如下查询:
mysql> select id from url where url=‘http://www.mysql.com’;
若删除原来url列上的索引,而新增一个被索引的url_crc列,使用crc32做哈希,用以下方式查询:
mysql> select id from url where url=‘http://www.mysql.com’ and url_crc=CRC32(‘http://www.mysql.com’);
这样做性能非常高,因为Mysql会使用这个选择性很高体积很小的基于url_crc列的索引来完成查找。如果数据量很大可以用sha1或者md5函数他们生成的是字符串,最好是整数如FNV64()。

索引策略

正确地创建和使用索引是实现高性能查询的基础.以下是如何使用高效的索引.

独立的列

独立的列
索引的列是指索引列不能是表达式的一部分,也不能是函数的参数。
例如,下面这个查询无法使用到索引
mysql> select actor_id from test where actor_id+1=5;
mysql无法解析这个方程式。

前缀索引和索引选择性

选择性是指,不重复的索引值(也称为基数)和数据表的记录总数(#T)的比值。范围从1/#T到1之间。索引的选择性越高则查询效率越高。唯一的索引选择性是1,这是最好的索引选择性,性能也是最好的,但也不能太长(节约空间).
对于blob、text或很长的varchar类型的列,必须使用前缀索引,因为Mysql不允许索引这些列的完整长度.
如:select distinct(left(city,3))/count(id) from table;

CREATE TABLE test (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
name varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
display_name varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

以下是数据
mysql索引策略和优化_第1张图片
查看选择性,选择合适的前缀长度
这里长度为4的时候是0.5,长度5也为0.5.这里选择长度4(最好多比较几个,看数据情况)
SELECT
count(DISTINCT LEFT(NAME, 2)) /count(),
count(DISTINCT LEFT(NAME, 3))/ count(
),
count(DISTINCT LEFT(NAME,4))/ count(),
count(DISTINCT LEFT(NAME,5))/ count(
)
FROM test

0.2500	0.3750	0.5000	0.5000

//建立前缀索引
alter table test add index(name(4))
//使用前缀索引
explain select name from test where name like ‘b%’;
在这里插入图片描述
前缀索引是一种能使索引更小,更快的有效办法,但另一方面也有其缺点:mysql无法使用其前缀索引做ORDER BY和GROUP BY,也无法使用前缀索引做覆盖扫描 .

多列索引

在使用复合索引的使用上
index(a,b,c)按照左前缀原则使用索引
ps:尽量少的使用多列索引,and条件的相交操作还好,or条件的联合操作尽量少用,or会消耗大量cpu和内存资源在算法的缓存、排序和合并操作上。
在explain中如果看到有索引合并,应该好好看下自己的表和结构,优化它.

索引与排序

使用索引与排序
排序可能发生两种情况
1:在innodb引擎中,沿着索引字段排序,也是自然有序的,using index;
对于myisam引擎,如果按某字段排序,如id但取出的字段中,有不是索引的字段,myisam做法,不是索引->回行,而是先取出所有行,再进行排序。
2:先取出数据,形成临时表,再按照filesort(文件排序,但文件可能在磁盘上,也可能在内存中)
如果explain extra filesort,必须要优化

索引扫描来做排序

使用索引扫描来做排序
如果explain出来的type列的值为Index,则说明mysql使用了索引扫描来做排序.
只有当索引的列顺序和order by字句的顺序完全一致,并且所有列的顺序方向都一样,才能够使用索引对结果做排序.
order by子句和查找型查询的限制是一样的,需要满足索引的做前缀原则.
如查询需要关联多张表,只有当order by字句引用的字段为第一个表时,才能使用索引做排序.
单独order by 用不了索引,索引考虑加where 或加limit.

其他操作

limit取数据 如100000,5 是取100005条,再扔掉那1000条数据。
技巧select * from table_name inner join(select id from table_name limit 如100000,5) as tmp on tmp.id=table_name.id;
以上第二个表操作用到了索引覆盖,再去关联第一个表去的所有数据。
where满足最左前缀原则且order by中列a、b、c的任意组合
如:where status=2 and is_delete=1 and types=1 order by is_delete desc这句话也会使用索引
recreate table rental(
primary key(rental_id),
unique key rental_date(rental_date,inventory_id,customer_id),
key idx_fk_inventory_id(invendory_id),
key idx_fk_customer_id(customer_id),
key idx_fk_staff(staff_id)
)
>explain select rental_id,staff_id from stakil.rental where rental_date=‘2005-05-25’ order by inventory_id,customer_id;
**1 rows
type:ref
possible_keys:rental_date
key:rental_date
rows:1
extra:using where
即使orderby 字句不满足索引的最做前缀要求,也可以用于查询,这是因为索引的第一列被指定为一个常数.
…where rental_date>‘2005-05-25’ order by rental_date,inventory_id;这个查询也没有问题.
下面是一些不能使用索引做排序的查询:
…where retal_date=‘2004-04-24’ order by inventory_id,staff_id.这里引用了一个不再索引中的列.
…where rental_date>‘2005-05-25’ order by inventory_id,customer_id.这里查询在索引列的第一列上是范围条件,所以无法使用索引的其他列.
…where rental_date=‘2005-05-25’ and inventory_id in(1,2) order by customer_id;这里inventory_id列上有多个等于条件.对于排序来说这也是一种范围查询.

优化关联查询
确保ON和USING字句中的列上有索引。在创建索引的时候就要考虑到关联的顺序。当表A和表B用列c关联的时候,如果优化器关联的顺序是A、B,那么就不需要在A表的对应列上创建索引。没有用到的索引会带来额外的负担,一般来说,除非有其他理由,只需要在关联顺序中的第二张表的相应列上创建索引(具体原因下文分析)。
要理解优化关联查询的第一个技巧,就需要理解MySQL是如何执行关联查询的。当前MySQL关联执行的策略非常简单,它对任何的关联都执行嵌套循环关联操作,即先在一个表中循环取出单条数据,然后在嵌套循环到下一个表中寻找匹配的行,依次下去,直到找到所有表中匹配的行为为止。然后根据各个表匹配的行,返回查询中需要的各个列。
太抽象了?以上面的示例来说明,比如有这样的一个查询:
Mysql代码
SELECT A.xx,B.yy
FROM A INNER JOIN B USING©
WHERE A.xx IN (5,6)
假设MySQL按照查询中的关联顺序A、B来进行关联操作,那么可以用下面的伪代码表示MySQL如何完成这个查询:
Mysql代码
outer_iterator = SELECT A.xx,A.c FROM A WHERE A.xx IN (5,6);
outer_row = outer_iterator.next;
while(outer_row) {
inner_iterator = SELECT B.yy FROM B WHERE B.c = outer_row.c;
inner_row = inner_iterator.next;
while(inner_row) {
output[inner_row.yy,outer_row.xx];
inner_row = inner_iterator.next;
}
outer_row = outer_iterator.next;
}
可以看到,最外层的查询是根据A.xx列来查询的,A.c上如果有索引的话,整个关联查询也不会使用。再看内层的查询,很明显B.c上如果有索引的话,能够加速查询,因此只需要在关联顺序中的第二张表的相应列上创建索引即可。

自己对or in的理解
上述表test为例
如果一个index(name,display_name)在使用or的时候不会走索引,
index(name) index(display_name)两个单独的列在使用or的时候会走索引这里是一个索引合并.
ps:如果想让or使用上索引,注意使用or的多个列需有索引.
如果in后面是直接的值则会使用(不能超过一定数量,有待探究),如果in是子查询则不会使用索引.
如果上述不同情况不执行索引,尝试union all

后续持续补充,欢迎各位纠正

你可能感兴趣的:(mysql)