索引列不能是表达式的一部分, 也不能是函数的参数.
/* 不能使用 user_id 列上的索引 */
select … where user_id + 1 = 5;
/* 不能使用 date 列上的索引 */
select … where TO_DAYS(CURRENT_DATE) - TO_DAYS(date) <=10;
始终将索引列单独放在比较符号的一侧
对很长的字符列(BLOB, TEXT, 很长的VARCHAR)做索引列是, 可以索引开始的部分字符. —> 前缀索引
mysql也不允许索引这些列的完整长度.
不重复的索引值(基数)和数据表的记录总数(#T)的比值. 取值为 1/#T~1 之间,
比值越高查询效率越高(因为可以过滤掉更多的数据行), 查询效率越高. —> 索引选择性
唯一索引的选择性是1.
合适的长度.
前缀的”基数”应该接近于完整列的”基数”
注意
mysql 无法使用前缀索引做 order by 和 group by, 也无法做索引覆盖扫描.
ps: 后缀索引, mysql原声不支持, 但是可以吧列值 字符串反转 在入库, 后加前缀索引即可.
在多个列上建立独立的单列索引大部分情况下并不能提高 mysql 的查询性能.
mysql5.0 和更新版本引入了 索引合并 的策略.
ex:
select user_id,role_id from user_role
where user_id = 1 or role_id = 1;
可能会变成使用 union all (mysql5.0之后, 优化器做的优化)
select user_id,role_id from user_role where user_id = 1
union all
select user_id,role_id from user_role where role_id = 1 and user_id <> 1;
* 当对多个索引做相交时(多个 and 条件) —> 意味着需要一个包含相关列的多列索引.
* 当对多个索引做联合时(多个 or 条件) —> 会耗费大量CPU和内存资源在算法的缓存, 排序, 和 合并上.
特别是索引选择性不高时.
* 优化器不会把以上计算到 “查询成本” 里,有可能执行此查询计划还不如走全表扫描.
ps: optimizer_switch 关闭索引合并
ignore index 让优化器忽略掉某些索引
正确的索引顺序依赖于使用该索引的查询.
并且更好的满足查询中的排序和分组. (B-Tree索引是顺序存储数据的)
如果只考虑优化 where条件, 不考虑排序和分组. 那么将选择性最高的列放在前面比较好.
否则就实际去测试吧…经验法则未必适合所有的场景.
并不是一种单独的索引类型, 而是一种数据存储格式.
InnoDB的聚簇索引就是在同一个结构中保存了B-Tree索引和数据行. (想想myisam)
InnoDB默认会选择表的主键(primary key), 如果没有这样的索引,
InnoDB存储引擎会隐试的定义一个”主键”来作为聚簇索引
好处:
* 在InnoDB表中按主键的顺序写入行 *
最简单的是使用 AUTO_INCREMENT 的自增列.
最好避免随机的(不连续的且值分布范围非常大的)聚簇索引. 比如UUID.
* 顺序的主键造成的问题 *
在高并发的情况下. 按主键顺序插入可能会造成争用, 并发插入可能导致间隙锁. 写入可能会成为瓶颈.
mysql有时也可以使用索引来直接获取列的数据. (这样做就不再需要读取数据行了)
一个索引包含(或者说覆盖)所有需要查询的字段的值 —> 覆盖索引
* 只需要扫描索引而无需回表 *
在 索引中 满足查询的成本 比 查询行 的成本 小很多 时
维护索引也是有代价的, 更新记录行时, 新增记录行时, 还有事务中 等等.
覆盖索引必须要存储索引列的值.
mysql 只能会用 B-Tree 索引做覆盖索引.
哈希索引, 空间索引, 和全文索引 都不存储索引列的值.
* 覆盖索引的 坑 *
mysql 查询优化器会在执行查询前 判断是否有一个索引能进行覆盖. 如果索引覆盖了 where 条件中的字段,
但不是整个查询涉及的字段, 如果条件为假, mysql5.5和其之前的版本也总是会回表获取数据行.
(取一些不需要的数据, 然后在过滤掉返回给客户端)
Tips:
InnoDB 的二级索引的叶子节点都包含了主键值, 变相的 相当于
key (username) == key (username,id) —> 其实 id 是 username 列上的索引中保存的值
mysql 的排序方式有两种:
1. 普通的排序操作 file_sort
2. 按索引顺序扫描
如果有 join 操作的时候, order by 后的所有字段都是第一张表的时候, 才能使用到索引来排序.
并且也需要满足 索引的最左前缀原则
ps:
key i_date_username_age (date, username, age)
* 但是 如果将排序的第一个字段被制定为常量时,可以不遵循最左前缀原则 *
select … WHERE date = 2017-08-08 order by username, age
* 排序的顺序也很重要 ASC / DESC *
排序不同,不能使用索引
select … WHERE date = 2017-08-08 order by username asc, age desc
* 注意范围条件 是不能使用索引来排序的 *
select … where date < 2017-08-08 order username, age
针对 MyISAM 引擎, 减小索引的大小, 可以让更多的索引放在内存中.
每创建一个索引, mysql 都要对其进行维护, 并且优化器在优化的时候, 也会去逐个进行考虑.
如果有了(a, b), 有建了(a), 那么(a)就是重复的索引了, 可以删除掉.
但是(b,a)相对而(a,b)来说就不是冗余索引.
对于 InnoDB 存储引擎, (a,id) 索引, id 就是多余的了, 因为索引 a 的值就是其对应的 id.
删除之.
percona toolkit —> pt-index-usage
索引可以让 锁定 根据索引过滤出来的数据行.
InnoDB只有在访问数据行的时候, 才会对其加锁. 而索引可以减少InnoDB访问的数据行.
InnoDB 根据索引检索数据返回给mysql服务器层后, mysql 服务器根据where字句过滤数据.
此时, 这些数据行已经被锁住了.
全表扫描的话就等于是锁表了.
ps: InnoDB在 二级索引上使用的是 共享(读)锁, 主键索引的时候是 排他(写)锁.