mysql优化-记一次sql优化的过程

背景

数据量大概是百万到千万级别吧, 然后能加的索引都加上了, 优化后, explain的结果都走索引了, 但是查询耗时还是10+s

修改前

表结构
CREATE TABLE `book`  (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '名称',
  `publish_time` datetime(3) NOT NULL COMMENT '发布时间',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_publishtime`(`publish_time`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1000001 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `category`  (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '名称',
  `type` bigint NOT NULL COMMENT '类型',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_type`(`type`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `book_category`  (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `book_id` bigint NOT NULL COMMENT '书本id',
  `category_id` bigint NOT NULL COMMENT '类型id',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_categoryid`(`category_id`) USING BTREE,
  INDEX `idx_bookid`(`book_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2706220 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
查询的sql
SELECT
	* 
FROM
	`book` 
WHERE
	id IN ( SELECT book_id FROM book_category LEFT JOIN category ON category.id = book_category.category_id WHERE category.type = 0 ) 
ORDER BY
	publish_time DESC 
	LIMIT 12
explain结果
idselect_tyletablepartitionstypepossible_keyskeykey_lenrefrowsfilteredextra
1SIMPLEbookindexPRIMARYidx_publishtime712100.00Backward index scan
1SIMPLE<subquery2>eq_ref<auto_distinct_key><auto_distinct_key>8jav.book.id1100.00
2MATERIALIZEDbook_categoryALLidx_categoryid,idx_bookid2863419100.00
2MATERIALIZEDcategoryeq_refPRIMARY,idx_typePRIMARY8jav.book_category.category_id147.37Using where

可以看到book_category这个表还有优化空间

第一次修改

基于上次explain结果, book_category存在优化空间, 在该表的book_idcategory_id同时加上索引
此时表结构为

CREATE TABLE `book_category`  (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `book_id` bigint NOT NULL COMMENT '书本id',
  `category_id` bigint NOT NULL COMMENT '类型id',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_categoryid`(`category_id`) USING BTREE,
  INDEX `idx_bookid`(`book_id`) USING BTREE,
  INDEX `idx_bookid_categoryid`(`book_id`, `category_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2706220 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

优化后explain结果如下:

idselect_tyletablepartitionstypepossible_keyskeykey_lenrefrowsfilteredextra
1SIMPLEbookindexPRIMARYidx_publishtime712100.00Backward index scan
1SIMPLE<subquery2>eq_ref<auto_distinct_key><auto_distinct_key>8jav.book.id1100.00
2MATERIALIZEDbook_categoryindexidx_categoryid,idx_bookid,idx_bookid_categoryididx_bookid_categoryid162863419100.00Using index
2MATERIALIZEDcategoryeq_refPRIMARY,idx_typePRIMARY8jav.book_category.category_id147.37Using where

可以看到, 虽然已经加上索引了, video_category的查询用上了typeindex, 已经用上了索引, 但是查询范围还是比较大, rows就几十万条数据了

把sql改成下面这个, explain结果一样, 故需再做优化

SELECT
	video.* 
FROM
	`video` 
WHERE
	EXISTS ( SELECT 1 FROM video_category LEFT JOIN category ON category_id = category.id WHERE video.id = video_id AND category.type = 0 ) 
ORDER BY
	publish_time DESC 
	LIMIT 12;

再修改

表结构已经优化过了, 能走的索引也都加上了, explain结果也说明已经走索引了, 故此时需要想办法把rows结果降低
将sql改成


SELECT
	* 
FROM
	book
WHERE
	id IN (
	SELECT
		book.id 
	FROM
		`book`
		INNER JOIN book_category ON book.id = book_id
		INNER JOIN category ON category_id = category.id 
		AND category.type = 0 
	) 
ORDER BY
	publish_time DESC 
	LIMIT 12;

执行结果: 0.135s
explain结果如下:

idselect_tyletablepartitionstypepossible_keyskeykey_lenrefrowsfilteredextra
1SIMPLEbookindexPRIMARYidx_publishtime78100.00Backward index scan
1SIMPLEbookeq_refPRIMARYPRIMARY8jav.book.id1100.00Using index
1SIMPLEbook_categoryrefidx_categoryid,idx_bookididx_bookid8jav.book.id2100.00
1SIMPLEcategoryeq_refPRIMARY,idx_typePRIMARY8jav.book_category.category_id147.37Using where; FirstMatch(book)

最后发现, 并没有走前面加的idx_bookid_categoryid索引, 虽然现在没走这个索引, 虽然对原sql查询性能有提高, 但是对优化后的sql并没起作用, 索引的维护对数据的更新和插入性能都有影响, 故将其删除, 此时的表结构恢复最初的模样

CREATE TABLE `book_category`  (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `book_id` bigint NOT NULL COMMENT '书本id',
  `category_id` bigint NOT NULL COMMENT '类型id',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_categoryid`(`category_id`) USING BTREE,
  INDEX `idx_bookid`(`book_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2706220 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值