如何写一个高效的索引,优化mysql查询(分页,回表,排序)

1. 需要优化的地方

  1. where用索引(避免全表扫描)
  2. select用索引(避免回表)
  3. 排序走索引 (避免查询出来后还要进行排序)

按照《数据库索引设计与优化》的说法满足1,2和3的索引就是三星索引了

2. 例子

测试表有3308670行数据,数据是我网上找了个人名生成网站后把数据写进去的

CREATE TABLE `eyas_account` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_id` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户id',
  `nickname` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户名',
  `phone` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '电话号码(或第三方账号ID)',
  `password` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '密码',
  `sex` int(4) NOT NULL DEFAULT '0' COMMENT '性别:0-未知,1-男,2-女',
  `birthdate` date DEFAULT NULL COMMENT '出生日期(精度:天)',
  `icon` varchar(300) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '用户头像',
  `address` varchar(300) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '地址',
  `comment` varchar(1000) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '个性签名',
  `last_login_time` datetime DEFAULT NULL COMMENT '上次登录时间',
  `createtime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updatetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id_index` (`user_id`) USING BTREE,
  UNIQUE KEY `user_account_phone` (`phone`) USING BTREE,
  KEY `idx_birthdate` (`birthdate`)
) ENGINE=InnoDB AUTO_INCREMENT=3333174 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='用户账号表';

如何写一个高效的索引,优化mysql查询(分页,回表,排序)_第1张图片

查询条件

  1. 查询全部字段
  2. 根据用户出生日期为范围作为筛选条件
  3. 根据出生日期为倒序(如果相同则用主键id倒序)

sql语句:select nickname, birthdate from eyas_account where birthdate between ‘2012-01-01’ and ‘2015-01-01’ order by birthdate desc, id desc

先回顾下这几个优化的点

  1. where用索引(避免全表扫描)
  2. select用索引(避免回表)
  3. 排序走索引 (避免查询出来后还要进行排序)
  • 1:优化where条件
    - 通常要提升一个sql的性能最必要的是根据where条件建立索引,由于数据有300万不建索引真的太慢了,我真的不想等这么久(哭
    - 所以我提早建立好了birthdate的索引。这时候我们已经有了一个非叶子节点由birthdate顺序排列,叶子节点由birthdate和id组成也是顺序排列的索引了,索引长下图的样子。
    - 建好索引后where条件就能根据二分搜索去快速定位,大大的提升了速度
    如何写一个高效的索引,优化mysql查询(分页,回表,排序)_第2张图片

  • 2:优化回表

    • select的字段包含了nickname和birthdate
    • 根据mysql的查询顺序先where在select。那么缺少的nickname去哪里拿呢,这时候我们需要用到idx_birthdate索引中的id字段,用id值去聚簇索引把对应的列找回来(这个过程叫回表)。如果想避免这个过程可以建立一个birthdate和nickname的联合索引,因为索引已经包含了select的所有列所以不需要再去聚簇索引拿字段。
  • 3:优化排序

    • 按照上面的索引查出来的数据其实就是有序的。按照birthdate顺序(如果birthdate相同则id顺序)
    • 有个需要注意的点是,排序的字段要不全部顺序,要不全部倒序,如果既有顺序也有倒序数据库就会Using filesort。

下一篇说到如果加入分页如何优化:如何优化mysql分页查询

你可能感兴趣的:(mysql)