11.长字符串的索引
根据email查用户
email整个索引
alter table SUser add index index1(email);
这样占太多空间了
前缀索引好
alter table SUser add index index2(email(6));
但是, 如果区分度不高, 很多前缀一样的 就要很多次回表
选择合适的前缀长度
select
count(distinct left(email,4))as L4,
count(distinct left(email,5))as L5,
count(distinct left(email,6))as L6,
count(distinct left(email,7))as L7,
from SUser;
找出不小于 L * 95% 的值,假设这里 L6、L7 都满足,你就可以选择前缀长度为 6。
前缀索引 坏处: 增加扫描行数
如果使用的是 index1(email 整个字符串的索引结构),执行顺序:
1.从 index1 索引树找到满足索引值是’zhangssxyz@xxx.com’的这条记录,取得 ID2 的值;
2.到主键上查到主键值是 ID2 的行,将这行记录加入结果集;
3,取 index1 索引树上刚刚查到的位置的下一条记录,发现已经不满足 email='zhangssxyz@xxx.com’的条件了,循环结束。
这个过程中,只需要 回主键索引取一次数据,所以系统认为只扫描了一行。
如果使用的是 index2(即 email(6) 索引结构),执行顺序是这样的:
1.从 index2 索引树找到满足索引值是’zhangs’的记录,找到的第一个是 ID1;
2.到主键上查到主键值是 ID1 的行,判断出 email 的值不是’zhangssxyz@xxx.com’,这行记录丢弃;
3.取 index2 上刚刚查到的位置的下一条记录,发现仍然是’zhangs’,取出 ID2,再到 ID 索引上取整行然后判断,这次值对了,将这行记录加入结果集;
重复上一步,直到在 idxe2 上取到的值不是’zhangs’时,循环结束。
index2 不是 email(6) 而是 email(7),也就是说取 email 字段的前 7 个字节来构建索引的话,即满足前缀’zhangss’的记录只有一个,也能够直接查到 ID2,只扫描一行就结束了。
也就是说使用前缀索引,要定义好长度
如果 查询语句是
select id,email from SUser where email='zhangssxyz@xxx.com';
不用名字, 只要id 和email
整个字符串索引 就可以 覆盖索引, 不用回表
但是 前缀索引 必须回到 id 索引再查一下
前缀的区分度不够好 怎么办?
使用倒序存储, 反着来的前缀 区分度没准就好了
select field_list from t where id_card = reverse('input_id_card_string');
加一列hash
alter table t add id_card_crc int unsigned, add index(id_card_crc);
查询时 hash+原字符串
select field_list from t where id_card_crc=crc32('input_id_card_string') and id_card='input_id_card_string'
crc32 算出来的值虽然有冲突的概率,但是概率非常小,可以认为每次查询的平均扫描行数接近 1
更新的时候用触发器就行了