索引问题排查
  前面我们谈使用索引的cost的值提到过explain。下面介绍explain的值,并以一个实际遇到的问题说明如何排查问题。
  Explain详解
  使用一个示例SQL来解释explain:
  select id from r_ibeacon_biz_device_d where ftime >= 20151126 and ftime <= 20151126 and biz_id = 11602 limit 50;

  IDX_BID_FTIME是表r_ibeacon_biz_device_d的其中一条索引。
  Biz_id,ftime均为bigint类型。
  我们着重关注几个重点字段的重点值:
  - type:索引的使用方式
  eq_ref      …  索引,关联匹配若干行
  ref          …  索引(前缀)匹配
  range        …  索引范围扫(BETWEEN、IN、>=、LIKE)得到数据
  index        …  索引全扫描
  all           …  表全扫描
  示例中使用的索引是使用全索引范围扫描,所以type为range
  - possible_keys:适用查询的索引列表。示例中有三条索引适用本次查询。
  - key: 查询实际执行使用的索引。示例使用的为IDX_BID_FTIME
  - key_len:查询使用索引的长度。
  null    1字节
  tinyint  1字节
  int    4字节
  bigint  8字节
  double  8字节
  datetime 8字节
  timestamp 4字节
  varchr(10)变长字段且允许NULL: 10*(Character Set:utf8=3,gbk=2,latin1=1)+1(NULL)+2(变长字段)
  char(10)固定字段且允许NULL: 10*(Character Set:utf8=3,gbk=2,latin1=1)+1(NULL)
  以上是常用类型的长度,示例中key_len为18,即:8字节(biz_id bigint)+1字节(biz_id允许为null)+8字节(ftimebigint)+1字节(ftime允许为null)。所以本次查询是使用了索引的所有字段加速查询
  - rows:查询预估扫描的行数
  Explain跟进问题
  摇一摇周边后台的数据统计接口尔会有小尖峰,涉及了一条SQL:
  一条SQL搞定卡方检验计算select d.id from r_ibeacon_biz_page_d d where d.ftime >= 20151126 and d.ftime <= 20151126 and d.biz_id = 11023 and d.page_id = 778495 limit 0,20;
  表r_ibeacon_biz_page_d 的主要字段信息如下:
  ftime  bigint(20)
  biz_id  bigint(20)
  page_id varchar(200)
  索引为:IDX_BID_PID_FTIME
  Explain结果如下

  观察以上explain结果可以看到一切正常,SQL“符合预期”的走了索引。但是rows稍微多了点,但是看起来也“好像”ok。但是问题是出现尖峰。
  问题排查:
  首先,注意到的一点是explain中的type异常,是ref。按照上面的解释,如果走了索引那应该是range类型才对啊。
  其次,观察key_len,9,发现确实有些不对,怎么会这么小。按照类型所占字节,9刚好为biz_id的长度,确定这条SQL虽然走了索引,但是只使用了biz_id字段。原因呢?
  然后执行“desc r_ibeacon_biz_page_d”,查看表结构的索引字段,突然发现page_id的类型怎么是varchar,再看SQL中page_id=11023。突然意识到了什么,此时刚好违反索引匹配的第四条规则。更改SQL“page_id=11023”为“page_id=‘11023’”验证,如下

  可以看到type=range、key_len=621,符合预期。接下来要做的是更改表中page_id的类型为bigint。隔天再看接口的尖峰果然削平。
  Explain是一个很好的工具,可以用来验证SQL是否使用了索引,更重要的是验证SQL是否如预期的使用索引上。排查线上问题还有profile和optimizer_trace,由于实际没有太多用到暂且不表。
  常见问题汇总
  - Range怎么使用索引?
  详见上文
  - Order by使用索引吗?
  该问题可以由以下资料解释:
  SQL queries with an order by clause don’t need to sort the result explicitly if the relevant index already delivers the rows in the required order. That means the same index that is used for the where clause must also cover the order by clause.
  总之一句话:索引本身并不能避免排序,当根据索引取出的数据已经满足order by子句的要求可以避免排序操作。
  - order by太慢?
  避免数据排序,采用索引排序(分页查询文艺写法)
  - limit offset太慢?
  避免大offset,使用where语句过滤更多的行。更多参考的实践《Efficient Pagination Using MySQL》
  - 为什么不走索引(索引也走了,还是慢)?
  类型是否一致: int vs char(varchar)、varchar(32)vs varchar(64)
  字符集是否一致:涉及表关联时,两表字符集是否一致。