背景

最近工作上遇到一个”神奇”的问题, 或许对大家有帮助, 因此形成本文.

问题大概是, 我有两个表 TableA, TableB, 其中 TableA 表大概百万行级别(存量业务数据), TableB 表几行(新业务场景, 数据还未膨胀起来), 语义上 TableA.columnA = TableB.columnA, 其中 columnA 上建立了索引, 但查询的时候确巨慢无比, 基本上到 5-6 秒, 明显跟预期不符合.

下面我以一个具体的例子来说明吧, 模拟其中的 SQL 查询场景.

场景重现

  • user_info 表, 为了场景尽量简单, 我只 mock 了其中的三列数据.
  • user_score 表, 其中 uiduser_info.uid 语义一致.
  • 其中数据情况如下, 都是很常见的场景.
  • 索引情况是
  • 查询业务场景: 已知 user_score.id, 需要关联查询对应user_info的信息, (大家先忽略这个具体业务场景是否合理哈). 那么对应的 SQL 很自然的如下:

请忽略其中的数据, 我刚开始 mock 了 100W, 然后又重复导入了两遍, 因此数据有一些重复. 300W 数据, 最后查询出来也是 1.18 秒. 按道理应该更快的. 老规矩 explain 看看啥情况?

发现 user_info表没用上索引, 全表扫描近 300W 数据? 现象是这样, 为什么呢?

你不妨思考一下, 如果你遇到这种场景, 应该怎么去排查?

(分割线, 花 10 秒想想?)


我当时也是”一顿操作猛如虎”, 然并卵? 尝试了什么多种 sql 写法来完成这个操作. 比如更换Join表的顺序(驱动表/被驱动表), 再比如用子查询. 最终, 还是没有结果. 但直接单表查询写 SQL 确能用上索引.

问题解决

尝试更换检索条件, 比如更换 uid 直接关联查询, 索引仍然用不上, 差点放弃了都. 在准备求助 DBA 前, 看了下表的建表语句.

完全有理由怀疑因为字符集不一致的问题导致索引失效的问题了.
于是修改了小表(真实线上环境可别乱操作)的字符集与大表一致, 再测试下.

mysql> select * from user_score us-> inner join user_info ui on us.uid = ui.uid-> where us.id = 5;
+----+-----------+-------+---------+-----------+---------+
| id | uid       | score | id      | uid       | name    |
+----+-----------+-------+---------+-----------+---------+
|  5 | 111111111 |   100 |       1 | 111111111 | tanglei |
|  5 | 111111111 |   100 | 3685399 | 111111111 | tanglei |
|  5 | 111111111 |   100 | 3685400 | 111111111 | tanglei |
|  5 | 111111111 |   100 | 3685401 | 111111111 | tanglei |
|  5 | 111111111 |   100 | 3685402 | 111111111 | tanglei |
|  5 | 111111111 |   100 | 3685403 | 111111111 | tanglei |
+----+-----------+-------+---------+-----------+---------+
6 rows in set (0.00 sec)mysql> explain-> select * from user_score us-> inner join user_info ui on us.uid = ui.uid-> where us.id = 5;
+----+-------------+-------+-------+-------------------+-----------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys     | key       | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+-------------------+-----------+---------+-------+------+-------+
|  1 | SIMPLE      | us    | const | PRIMARY,index_uid | PRIMARY   | 4       | const |    1 | NULL  |
|  1 | SIMPLE      | ui    | ref   | index_uid         | index_uid | 194     | const |    6 | NULL  |
+----+-------------+-------+-------+-------------------+-----------+---------+-------+------+-------+
2 rows in set (0.00 sec)

果然 work 了.

挖掘根因

其实深究原因, 就是网上各种 MySQL军规/规约所提到的, “索引列不要参与计算”. 这次这个 case, 如果知道 explain extended + show warnings 这个工具的话, (以前都不知道explain后面还能加 extended 参数), 可能就尽早”恍然大悟”了. (最新的 MySQL 8.0版本貌似不需要另外加这个关键字).

看下效果. (啊, 我还得把字符集改回去!!!)

mysql> explain extended select * from user_score us  inner join user_info ui on us.uid = ui.uid where us.id = 5;
+----+-------------+-------+-------+-------------------+---------+---------+-------+---------+----------+-------------+
| id | select_type | table | type  | possible_keys     | key     | key_len | ref   | rows    | filtered | Extra       |
+----+-------------+-------+-------+-------------------+---------+---------+-------+---------+----------+-------------+
|  1 | SIMPLE      | us    | const | PRIMARY,index_uid | PRIMARY | 4       | const |       1 |   100.00 | NULL        |
|  1 | SIMPLE      | ui    | ALL   | NULL              | NULL    | NULL    | NULL  | 2989934 |   100.00 | Using where |
+----+-------------+-------+-------+-------------------+---------+---------+-------+---------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
mysql> show warnings;
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message                                                                                                                                                                                                                                                                              |
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note  | 1003 | /* select#1 */ select '5' AS `id`,'111111111' AS `uid`,'100' AS `score`,`test`.`ui`.`id` AS `id`,`test`.`ui`.`uid` AS `uid`,`test`.`ui`.`name` AS `name` from `test`.`user_score` `us` join `test`.`user_info` `ui` where (('111111111' = convert(`test`.`ui`.`uid` using utf8mb4))) |
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

(滑动看右边)

索引列参与计算了, 每次都要根据字符集去转换, 全表扫描, 你说能快得起来么?

至于这个问题为什么会发生? 综合来看, 就是因为历史原因, 老业务场景中的原表是假 utf8, 新业务新表采用了真 utf8mb4.

  1. 考虑新表的时候, 忽略和原库字符集的比较. 其实, 发现库里面的不同表可能都有不同的字符集, 不同人建的时候可能都依据个人喜好去选择了不同的字符集. 由此可见, 开发规范有多重要.
  2. 虽然知道索引列不能参与计算, 但这个场景下都是相同的类型, varchar(64) 最终查询过程中仍然发生了类型转换. 因此需要把字段字符集不一致等同于字段类型不一致.
  3. 如果这个 case, 利用 fail-fast 的理念的话, 发现不一致, 直接不让 join 会不会更好? (就像 char v.s varchar 不能 join 一样).

说明: 本文测试场景基于 MySQL 5.6, 另外, 本文案例只是为了说明问题, 其中的 SQL 并不规范(例如尽量别用 select * 之类的), 请勿模仿(模仿了我也不负责

). 为了写本文, 可花了不少时间, 建 DB, mock数据, 包括排版公众号(啊,公众号后台对代码格式还是不友好, markdown 转来代码格式还是有问题)等等, 如果觉得有用, 还望你帮忙"在看", "转发". 最后留一个思考题供讨论, 欢迎留言说出你的看法.

留一道思考题

你能解释如下情况吗? 查询结果表现为何不一致?

sql server整表查询慢_这里有一个慢 SQL 查询等你来优化相关推荐

  1. SQL Server数据表中数据的增加(插入)、查询、修改、删除

    目录 零.码仙励志 一.数据表中数据的增加(插入) 二.数据表中数据的查询 三.数据表中数据的修改 四.数据表中数据的删除 零.码仙励志 伟人所达到并保持着的高处,并不是一飞就到的,而是他们在同伴们都 ...

  2. SQL Server数据库表的基本操作(批量插入、删除、查询数据,删除表中重复数据方法)

    实验名称:数据库表的基本操作与表内数据操作 实验目的: 掌握数据库表创建方法(交互式.T-SQL法) 掌握修改数据库表结构的方法 掌握删除数据库表的方法 掌握交互式EXCEL文件录入数据至数据库表的方 ...

  3. sql server management studio性能分析_如何分析一条SQL的性能

    来自公众号:谭小谭 这篇文章将给大家介绍如何使用 explain 来分析一条 sql . 网上其实已经有非常多的文章都很详细的介绍了 explain 的使用,这篇文章将实例和原理结合起来,尽量让你有更 ...

  4. sql server复制表_具有超过246列的表SQL Server复制

    sql server复制表 问题 (Problem) In our environment we use SQL Server merge replication to replicate data ...

  5. SQL Server两表比对数据

    sql server两表比对数据是否完全一直 使用sql server的tablediff工具进行比对 打开cmd cd切换路径到你sqlserver数据库的这个目录下:Microsoft SQL S ...

  6. php多表查询性能优化,MSSQL_SQL Server多表查询优化方案集锦,SQL Server多表查询的优化方案是 - phpStudy...

    SQL Server多表查询优化方案集锦 SQL Server多表查询的优化方案是本文我们主要要介绍的内容,本文我们给出了优化方案和具体的优化实例,接下来就让我们一起来了解一下这部分内容. 1.执行路 ...

  7. SQL Server创建复合索引时,复合索引列顺序对查询的性能影响

    SQL Server创建复合索引时,复合索引列顺序对查询的性能影响 原文:SQL Server创建复合索引时,复合索引列顺序对查询的性能影响 说说复合索引 写索引的博客太多了,一直不想动手写,有一下两 ...

  8. columnproperty server sql_导出SQL Server数据库表中字段的说明/备注

    时 间:2013-02-18 09:09:11 作 者:摘 要:导出SQL Server数据库表中字段的说明/备注 正 文: 打开SQL企业管理器 ,找到你要导出用户表字段信息的那个数据库 ,点击工具 ...

  9. sql a 表 若包含b表 则a 表 列显示_几道常见的SQL面试题,看你能答对几道?

    分享几道比较常见的SQL面试题,在不看底部参考答案的情况下,看自己能做对几道. 1.用一条SQL 语句 查询出每门课都大于80 分的学生姓名 2. 学生表 如下: 删除除了自动编号不同, 其他都相同的 ...

最新文章

  1. UA OPTI570 量子力学29 摄动理论简介
  2. shiro整合ehcache
  3. abap的子程序参数 USING 和 CHANGING 使用问题
  4. matlab画孔斯曲面,CAD CAM技术基础:第五讲 孔斯曲面
  5. mysql数据库(10):数据 备份
  6. Python学习心路历程
  7. 趣说单例模式——选班长
  8. 2018-2019-2 20165209 《网络对抗技术》Exp7: 网络欺诈防范
  9. Spring Boot和Dubbo整合
  10. 一维搜索---黄金分割法
  11. C语言关于排序的十一个函数
  12. 隐马尔科夫链(HMM)
  13. 初识Hadoop之概念认知篇
  14. 兼容IE8的旋转角度
  15. What is WPK (PowerShell Kit) ?
  16. Redis 4.0-rc1 发布,超高性能 key-value 数据库
  17. 2020年最酷的十个无人机项目(上)
  18. 远程桌面连接使用教程
  19. 效率成吨提升之代码生成器-蓝湖工具神器iOS,Android,Swift,Flutter
  20. Android+webview+h5 拍照闪退问题

热门文章

  1. A disk read error occurred Press Ctrl+Alt+del to restart
  2. matlab imadjust将暗图像分别在RGB与HSV域增加亮度
  3. 安徽大学计算机语言学考研真题,2019年安徽大学英语语言文学复试真题回忆
  4. 没有iml文件会怎么样_【商标服务】商标管理:商标没有办理续展会怎么样 ?...
  5. java 元祖_在java中对元组列表进行排序的有效方法
  6. 导数与微分的知识点思维导图_高中物理思维导图,高中三年知识点一个不漏
  7. python list join函数_Python中join()函数多种操作代码实例
  8. Sonar扫描python代码
  9. LuckyFrame执行Web自动化用例
  10. 毕业生简单的用Python实现一个信息管理系统【含示例代码】