这种索引策略是由于“把 WHERE 条件里面的列都建上索引”这样模糊的建议导致的。这最多是“一星”索引。例,WHERE c1 = '1' OR c2 = '2',虽然在 MySQL5.0+ 会优化使用这两个索引,但是在 explain 的 extra 中会有 Using union,因此说明了索引的糟糕:
出现多个 AND 条件,则需要一个包含所有相关列的多列索引,而不是多个独立的单列索引。
多个 OR 或者联合操作时,如果有些索引的选择性不高,需要合并扫描返回的大量数据。
如果在 EXPLAIN 中看到有索引合并,应该好好检查查询和表的结构,也可以通过参数 optimizer_switch 来关闭索引合并功能,也可以使用 IGNORE INDEX 提示让优化器忽略掉某些索引。
当发起一个被覆盖的查询,在 EXPLAIN 的 Extra 列可以看到 Using index 的信息。
type 列的 index 和 Extra 列的 Using index 是完全不同,前者和覆盖索引毫无关系,它只是表示这个查询访问数据的方式。
索引覆盖查询还有很多陷阱可能导致无法实现优化:
没有任何索引能够覆盖这个查询。因为查询从表中选择了所有的列,而没有任何索引覆盖了所有的列。
MySQL 不能再索引中执行 LIKE 操作,MySQL5.5- 只允许在索引中做简单比较操作,MySQL 能在索引中做最左前缀匹配的 LIKE 查询,因为该操作可以转为简单的比较操作,但是如果是通配符开头的 LIKE 查询,存储引擎就无法做比较匹配。这种情况下,MySQL 服务器只能提取数据行的值,而不是索引值来做比较。
MySQL 有两种方式可以生成有序的结果:通过排序操作;或按索引顺序扫描;如果 EXPLAIN 出来的 type 列的值为 “index”,则说明 MySQL 使用了索引扫描来做排序(不要和 Extra 列的 “Using index” 搞混淆了)。 扫描索引本身是很快的,因为只需要从一条索引记录移动到紧接着的下一条记录。但如果索引不能覆盖查询所需的全部列,那就不得不每扫描一条索引记录就回表查询一次对应的行。这基本上都是随机 I/O,因此按索引顺序读取数据的速度通常要比顺序地全表扫描慢。 如果查询需要关联多张表,则只有当 ORDER BY 子句引用的字段全部为第一个表时,才能使用索引做排序。
WHERE rental_date = '2015-05-25' ORDER BY inventory_id, customer_id 因为索引第一列被指定为一个常数,所以查询排序。 WHERE rental_date = '2015-05-25' ORDER BY inventory_id 也可以使用查询排序。 WHERE rental_date > '2005-05-25 ORDER BY rental_date, inventory_id 也可以。 下面是不能使用索引做排序的查询:
WHERE rental_date = '2015-05-25' ORDER BY inventory_id DESC, customer_id ASC; 因为索引列都是正序排序。
WHERE rental_date = '2015-05-25' ORDER BY inventory_id, staff_id; 因为引用了一个不再索引中的列。
WHERE rental_date = '2015-05-25' ORDER BY customer_id; 无法组成索引的最左前缀。
WHERE rental_date > '2015-05-25' ORDER BY inventory_id, customer_id; 因为第一列上是范围条件。
WHERE rental_date = '2015-05-25' AND inventory_id IN (1,2) ORDER BY customer_id; 还是范围查询。
如果需要保存很多 true/false 值,可以考虑合并这些列到一个 SET 数据类型,它在 MySQL 内部是以一系列打包的位的集合来表示的。这样就有效的利用了存储空间。缺点是改变列的定义代价较高:需要 ALTER TABLE(这对大表是非常昂贵的操作,但是后面给出了解决方法)。一般来说,也无法再 SET 列上通过索引查找。
UPDATE daily_hit_counter as c INNERJOIN ( SELECTday, SUM(cnt) AS cnt, MIN(slot) AS mslot FROM daily_hit_counter GROUPBYday ) AS x USING(day) SET c.cnt = IF(c.slot = x.mslot, x.cnt, 0), c.slot = IF(c.slot = x.mslot, 0, c.slot); DELETEFROM daily_hit_counter WHERE slot <> 0AND cnt = 0;
4 加快 ALTER TABLE 操作的速度
假如要修改电影的默认租赁期限,从三天改到五天,下面是很慢的方式:
1 2
ALTERTABLE film MODIFYCOLUMN rental_duration tinyint(3) notnulldefault5;
show status语句显示这个语句做了 1000 次读和 1000 次插入操作。换句话说,它拷贝了整张表到一张新表。 理论上,MySQL 可以跳过创建新表的步骤,即直接修改 .frm 文件而不设计表数据:
1 2
ALTERTABLE film ALTERCOLUMN rental_duraion SETDEFAULT5;
5 总结
避免过度设计
使用小而简单的合适数据类型,避免使用 NULL 值
关联标识符尽量使用相同的数据类型
注意可变长字符串,其在临时表和排序时可能导致悲观的按最大长度分配内存
尽量使用整型定义标识列
小心使用 ENUM 和 SET
ALTER TABLE在大部分情况下都会锁表并且重建整张表。建议先在备库执行ALTER完成后将其切换为主库
Alice 和 Bob 约会,在告别的时候, Bob 给了 Alice 一张存储卡,并说“这是我的公钥”。 Alice 回到家中,从存储卡中取出 Bob 的公钥,并存放到自己所使用的 PGP 的公钥串中(导入公钥)。由于 Alice 确信刚刚导入的公钥确实是属于 Bob 本人的,因此 Alice 对这个公钥加上了自己的数字签名。 对 Bob 的公钥加上数字签名,就相当于 Alice 生命“这个公钥属于 Bob 本人(即这个公钥是合法的)”。 随后,Alice 收到了来自 Bob 的邮件,由于这封邮件带有 Bob 的数字签名,因此 Alice 想用 PGP 来验证 Bob 的数字签名。 PGP 将执行下面这些操作:
为了验证 Bob 的数字签名, PGP 需要从 Alice 的公钥串中寻找 Bob 的公钥。
Alice 的公钥串中包含 Bob 的公钥,因为前几天约会之后 Alice 导入了 Bob 的公钥。
PGP 发现 Bob 的公钥带有 Alice 的数字签名。
为了验证 Alice 的数字签名,PGP 需要从 Alice 的公钥串中寻找 Alice 自己的公钥。
PGP 使用 Alice 的公钥对 Bob 的公钥上的 Alice 的数字签名进行验证。如果验证成功,则可以确认这的确就是 Bob 的公钥。
PGP 使用合法的 Bob 的公钥对邮件上附带的 Bob 的数字签名进行验证。
2.场景2:通过自己完全信任的人的数字签名进行确认
Alice 有一个叫 Trent 的男朋友。在 Alice 的公钥串中,也包含带有 Alice 的数字签名的 Trent 的公钥。 Alice 非常信任 Trent ,她想:经过他签名的公钥一定是合法的。 假设 Alice 收到了一封来自 Carrol 的邮件