工作中接開發主管反饋,有個資訊接口調用時有出現響應較慢,需要優化。
接口返回有時較慢??第一反應是接口的redis緩存過期時有大量請求穿過redis緩存,對mysql訪問壓力較大造成的。
先看PHP代碼,邏輯不復雜,首先根據傳入的資訊類型id,從redis獲取資訊緩存,沒有就讀取mysql同時更新redis緩存,緩存有效期3-5分鍾。
大概定位在mysql查詢這塊,然后上阿里雲后台發現mysql慢查詢記錄。
表結構:
資訊分類表
CREATE TABLE `article_cats` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(50) NOT NULL COMMENT '分類名', `pid` INT(11) NOT NULL DEFAULT '0', `type` TINYINT(4) NOT NULL DEFAULT '0' COMMENT '類型(0文章1視頻)', `img_url` VARCHAR(300) NOT NULL COMMENT '類別ICON', `sort` INT(11) NOT NULL DEFAULT '0' COMMENT '排序(數值越大越靠前)', PRIMARY KEY (`id`), UNIQUE INDEX `name` (`name`), INDEX `sort` (`sort`) ) COMMENT='文章分類表' COLLATE='utf8_general_ci' ENGINE=InnoDB AUTO_INCREMENT=106;
資訊表
CREATE TABLE `article` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'id', `type` TINYINT(1) UNSIGNED ZEROFILL NULL DEFAULT NULL COMMENT '類型', `title` VARCHAR(500) NOT NULL COMMENT '標題', `abstract` VARCHAR(255) NULL DEFAULT '' COMMENT '摘要', `own` VARCHAR(50) NULL DEFAULT NULL, `url_md5` VARCHAR(200) NULL DEFAULT NULL COMMENT '文章URL', `live_status` TINYINT(1) NULL DEFAULT '1' COMMENT '顯示類型 1 web顯示,2 app顯示,3 全顯示,4 全不顯示', `status` TINYINT(1) NULL DEFAULT '1' COMMENT '狀態,1正常,-1刪除', `grab_time` DATETIME NULL DEFAULT NULL COMMENT '采集第3方時間', `img_url` VARCHAR(200) NULL DEFAULT '', `sort` INT(11) NULL DEFAULT '0' COMMENT '文章排序', `last_own` VARCHAR(20) NULL DEFAULT NULL COMMENT '最后編輯者帳號', `url` VARCHAR(200) NULL DEFAULT NULL, `level` TINYINT(4) NULL DEFAULT '1' COMMENT '快訊的重要程度:1不重要,2一般,3重要', `share_num` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '分享數', `status_level` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' COMMENT '置頂狀態',PRIMARY KEY (`id`), INDEX `title` (`title`(255)),
INDEX `status` (`status`),
INDEX `grab_time` (`grab_time`),
INDEX `t_l_s` (`type`, `live_status`, `status`),
INDEX `type` (`type`, `status`, `live_status`),
INDEX `live_status` (`live_status`, `status_level`),
INDEX `Index 11` (`sort`)
)
COMMENT='文章表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=537710;
查詢sql: SELECT
a.id,a.title,a.img_url,ac.type actype,a.template,a.sort,a.grab_time,a.type as type_id,a.abstract,ac.name as type,ac.img_url as cat_img_url
FROM article a
left join article_cats ac
on a.type = ac.id
WHERE ( a.status = 1 ) AND ( ac.pid = 25 )
ORDER BY a.id DESC LIMIT 0,20
/* Affected rows: 0 已找到記錄: 20 警告: 0 持續時間 1 query: 0.328 sec. (+ 0.016 sec. network) */
查詢結果顯示耗時0.3秒,這樣的時間間隔在大量請求穿透redis緩存直接訪問mysql時肯定會對接口有影響。
用explain執行sql得到如下分析結果:
其中possible_keys列指出MySQL能使用哪個索引在該表中找到行,key列顯示MySQL實際決定使用的鍵(索引)。
可以看到mysql服務端自做判斷使用status索引,但status只分兩種情況,1和-1,區分度不大,索引用起來效果不佳(當時也不知道誰對status字段建了索引。。。)。
我們可以強制mysql使用區分度大的索引和字段。
這里我們還有type索引:
`type` (`type`, `status`, `live_status`)
加上強制使用的索引sql變成如下
SELECT a.id,a.title,a.img_url,ac.type actype,a.template,a.sort,a.grab_time,a.type as type_id,a.abstract,ac.name as type,ac.img_url as cat_img_url FROM article a force index(type) left join article_cats ac on a.type = ac.id WHERE ( a.status = 1 ) AND ( ac.pid = 25 )
/* Affected rows: 0 已找到記錄: 20 警告: 0 持續時間 1 query: 0.015 sec. (+ 0.016 sec. network) */
查詢結果顯示耗時0.015秒,查詢效率有了很大提升。
再用explain分析
可以發現ac表走全表掃描(資訊類型表一百來號數據),a表使用type索引。
改完后測試環境測試完成么問題,下周來上線。
explain關鍵字是項目開發過程的一件sql效率提優利器,寫sql,特別是聯合查詢,字表查詢之類的時候還是大有裨益。