• 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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    查询的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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    explain结果
    idselect_tyletablepartitionstypepossible_keyskeykey_lenrefrowsfilteredextra
    1SIMPLEbookindexPRIMARYidx_publishtime712100.00Backward index scan
    1SIMPLEeq_ref8jav.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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    优化后explain结果如下:

    idselect_tyletablepartitionstypepossible_keyskeykey_lenrefrowsfilteredextra
    1SIMPLEbookindexPRIMARYidx_publishtime712100.00Backward index scan
    1SIMPLEeq_ref8jav.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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    再修改

    表结构已经优化过了, 能走的索引也都加上了, 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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    执行结果: 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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    1. 开篇辞和一些SQL语句基本概念
    使用Plotly可视化
    Django
    MapStruct踩坑笔记
    hyperscan技术
    Windows取证——隐藏用户
    js JSON.stringify 对象转字符串
    119.(前端)商品管理增加基本信息布局——model与ref与prop概念介绍、使用级联选择器与tab中使用form表单
    数学建模算法与应用 数理统计
    【多任务案例:猫狗脸部定位与分类】
  • 原文地址:https://blog.csdn.net/zzsan/article/details/126410800