记一次sql优化

最近为公司写后台程序,就是数据库检索,其实就是拼sql,在测试环境运行没问题,线上却没有数据显示,最后发现是sql运行超时,整整6s,最后给优化到0.5s

表结构:

| newreport | CREATE TABLE `newreport` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` varchar(32) NOT NULL DEFAULT '' COMMENT '...',
  `report_id` varchar(32) NOT NULL DEFAULT '' COMMENT '...',
  `report_type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '...',
  `reason_ids` varchar(256) NOT NULL DEFAULT '' COMMENT '...',
  `description` varchar(256) NOT NULL DEFAULT '' COMMENT '...',
  `pictures` varchar(256) NOT NULL DEFAULT '' COMMENT '...',
  `createTs` int(10) unsigned NOT NULL DEFAULT '0',
  `comment_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '...',
  `status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '...',
  `extra` text,
  `chat_type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '...',
  `version` varchar(32) NOT NULL DEFAULT '' COMMENT '...',
  `region` varchar(32) NOT NULL DEFAULT '' COMMENT '...',
  `operator` varchar(32) NOT NULL DEFAULT '' COMMENT '...'
  `update_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '...',
  PRIMARY KEY (`id`),
  KEY `report_id` (`report_id`),
  KEY `idx_user_id` (`user_id`),
) ENGINE=InnoDB AUTO_INCREMENT=1701203 DEFAULT CHARSET=utf8 |

原sql

select sql_no_cache report_id, count(*) as report_total, max(createTs) as recent_time from newreport where report_type=4  and createTs between 1566849758 and 1567454558  and 0='\'\''  and 2=2  and status=0  group by report_id  order by report_total desc, createTs  limit 0,20\G

就是这样的sql运行时间6s

为createTs加了索引:

  KEY `idx_createTs` (`createTs`)

时间减少到0.5s ...
当然没有完,要不然这个就太水了,加个索引就解决了还有啥说的。

createTs小范围createTs between 1566849758 and 1567454558对应的数据量占总数的0.87%。
大范围 createTs between 1556668800 and 1567454558对应的行数占总数97% 时间跨度20190501-20190902。
每天新增数据量接近2k,数据库总数 1701186。
当范围查询超过总数一定比例,mysql会全局扫描。

问题来了,上面的查询时间范围为大概一周,当查询时间增加到4个月时,查询时间降到3s,继续优化:

select sql_no_cache report_id, count(*) as report_total, max(createTs) as recent_time from newreport s right join (select id from newreport where createTs > 1556668800 and createTs < 1567454558) t using(id) where report_type=4 and 0='\'\''  and 2=2  and status=0  group by report_id  order by report_total desc, recent_time limit 0,20;

explain结果:

explain select sql_no_cache report_id, count(*) as report_total, max(createTs) as recent_time from newreport where id in (select id from newreport where createTs > 1556668800 and createTs < 1567454558) and report_type=4 and 0='\'\''  and 2=2  and status=0  group by report_id  order by report_total desc, createTs limit 0,20\G


*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: newreport
   partitions: NULL
         type: ALL
possible_keys: PRIMARY,report_id
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1655044
     filtered: 1.00
        Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: newreport
   partitions: NULL
         type: eq_ref
possible_keys: PRIMARY,idx_createTs
          key: PRIMARY
      key_len: 8
          ref: starmaker.newreport.id
         rows: 1
     filtered: 36.68
        Extra: Using where

时间区间4个月,查询时间0.8s,勉强够用,可是看到right join的sql并没有使用createTs索引,继续优化,强制让mysql使用这个索引

explain select sql_no_cache report_id, count(*) as report_total, max(createTs) as recent_time from newreport s right join (select id from newreport use index(idx_createTs) where createTs > 1556668800 and createTs < 1567454558) t using(id) where report_type=4 and 0='\'\''  and 2=2  and status=0  group by report_id  order by report_total desc, createTs limit 0,20\G

explain结果:


           id: 1
  select_type: SIMPLE
        table: newreport
   partitions: NULL
         type: range
possible_keys: idx_createTs
          key: idx_createTs
      key_len: 4
          ref: NULL
         rows: 607124
     filtered: 100.00
        Extra: Using where; Using index; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: s
   partitions: NULL
         type: eq_ref
possible_keys: PRIMARY,report_id
          key: PRIMARY
      key_len: 8
          ref: starmaker.newreport.id
         rows: 1
     filtered: 5.00
        Extra: Using where
2 rows in set, 2 warnings (0.00 sec)

扫描行数由1655044减少到607124
时间区间4个月,查询时间0.5s

当然如果可以,还能继续优化,那就是修改索引createTs为联合索引,where子句中离散型字段有点多,也就是distinct field后,没有几个值的field,可以把这几个field放在createTs前面构建联合索引,比如(field, createTs),放在createTs的前面是因为最左前缀,当只需要联合索引的后面字段时,可以把联合索引靠前的字段通过 field in (...) and createTs = ...利用起来。

但是索引不是越多越好,因为维护索引也是需要成本的嘛,索引多了,插入更新就会困难,要做的是将已有的资源发挥到极限。

至于为什么要right join一下,因为btree原理,createTs是非主键索引,底层维护存储的是createTs和对应记录行的主键,通过这个非聚簇索引找到满足条件的id,这个id就是主键索引,即聚簇索引,聚簇索引底层除了保存了主键,还有主键对应的记录行,也就是聚簇索引保存了数据表,直接利用聚簇索引的话,不就省了回表操作嘛。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352