真实场景sql优化持续更新(老司机必备)
概述
下述场景,均来自实际产品线上经验,出于保密考量,所有需求场景都是仿造的,模拟遇到过的真实场景。
场景一: 统计数据(Order by 不具备唯一性导致的分页数据混乱)
需求
在实际业务场景中,我们经常遇到统计分析,比如现在有一张学生表student,现统计姓名为xxx的总共有多少学生。
id | name |
---|---|
1 | 张三 |
2 | 张三 |
3 | 李四 |
4 | 武器 |
5 | 大炮 |
6 | 大炮 |
7 | 李四 |
8 | 无用 |
9 | 刘可 |
10 | 狐狸 |
11 | 无话 |
12 | 败给 |
13 | 事变 |
14 | 狐狸 |
15 | 何必 |
16 | 无话 |
17 | 无用 |
18 | 无话 |
19 | 李四 |
实现
常规思路一般用groub by ,然后再求和,再分页。
查第一页
SELECT
t.name,
COUNT(1) as num
FROM
test t
WHERE
1 = 1
GROUP BY t.`name`
ORDER BY
num DESC
LIMIT 0,
5
查询结果是这样的:
name | num |
---|---|
李四 | 3 |
无话 | 3 |
张三 | 2 |
大炮 | 2 |
狐狸 | 2 |
查第二页
SELECT
t.name,
COUNT(1) as num
FROM
test t
WHERE
1 = 1
GROUP BY t.`name`
ORDER BY
num DESC
LIMIT 5,
5
查询结果是这样的:
name | num |
---|---|
狐狸 | 2 |
武器 | 1 |
刘可 | 1 |
败给 | 1 |
事变 | 1 |
结果分析
显然第二页的'狐狸'不应该出现,他是第一页的最后一条数据。这个问题在mysql官方是给予了答案的,其实只要是order by 的排序字段在结果集中不唯一,排序字段一致的行他返回的结果都是无序的,这一点不容易被重视,也不容易被测试所发现(单表一般需要较多重复数据和分页才容易被发现),算是一个小坑。
优化
方案一
网上一般提供的思路: 既然排序字段不是唯一的,我们一般期望唯一排序,只需要在order by 中跟上唯一标识的字段即可,像下面这样:
SELECT
t.name,
COUNT(1) as num
FROM
test t
WHERE
1 = 1
GROUP BY t.`name`
ORDER BY
num DESC,t.id desc
LIMIT 5,
5
但是这种方式有个致命问题,ORDER BY 后面接了两个字段会让索引失效,大数据场景下是不推荐这种方式的。
方案二
使用 ROW_NUMBER() OVER ( ORDER BY t.id) AS serial_number让他按照指定方式排序,这基本也是万机油解决方案,对代码侵入程度很低。但是我们这个场景下两种方式效率一样,因为本来num字段就没有索引,但是当order by 存在一个字段可以用索引的话就不一样了。
SELECT
t.name,
COUNT(1) as num ,
ROW_NUMBER() OVER ( ORDER BY t.name) AS serial_number
FROM
test t
WHERE
1 = 1
GROUP BY t.`name`
ORDER BY
num DESC
LIMIT 5,
5
场景二: 大表查询优化问题(多租户情景下的连表查询规范)
需求
假设有这样一个场景,要求查某公司的商品出售情况的数据,数据库设计如下:
表名 | 备注 |
---|---|
order | 订单表,有create_by 字段 |
goods | 商品表 |
logistics | 物流表 |
order_goods_mapping | 商品与订单关联表 |
order_logistics_mapping | 物流与订单关联表 |
实现
先不考虑数据库设计是否合理,现在要分页查询商品销售情况,在不考虑数据量的情况下一般这样写sql(伪sql):
select g.*,o.*,l.* from goods g
join order_goods_mapping ogm on(ogm.goods_id= g.goods_id)
join order o on(o.order_id= ogm.order_id)
join order_logistics_mapping olg on(olg.order_id = o.order_id)
join logistics l on(l.logistics_id = olg.logistics_id)
where l.company_id = #{companyId} limit 0,10
这些xxxid字段索引都有,当数据库较小的时候看上去没有任务问题。但是假设商品有1亿种商品,这个sql可以预见性的剧卡。因为join操作匹配本来就是nnn这样的操作,由于只限制了logistics 的company_id,所以查询出来的数据量依旧是巨大的。(亲身经历的一次因为慢查询,导致上线失败的根本原因)
优化
要限制每张表的数据尽可能少,一般多租户场景下,每张表要有租户id, 这样就可以按租户维度进行数据隔离。由于很多时候我们没有遇到过大表的情况,所以基本租户隔离技术在sql联表查询没有体现出来,往往只是限制了联表的某一张表的租户id等于登录的租户id,这是不可取的(有意思的是:难怪现在流行的多租户方案要求每张表都要有租户id,除了分库分表有用,查询优化也体现出了数据隔离的优势,一个小小的字段竟然有这么大的作用)。优化后的sql如下:
select g.*,o.*,l.* from goods g
join order_goods_mapping ogm on(ogm.goods_id= g.goods_id)
join order o on(o.order_id= ogm.order_id)
join order_logistics_mapping olg on(olg.order_id = o.order_id)
join logistics l on(l.logistics_id = olg.logistics_id)
where l.company_id = #{companyId} and g.company_id = #{companyId} and ogm..company_id = #{companyId} and o.company_id = #{companyId} and olg.company_id = #{companyId}limit 0,10
场景三: 子查询导致的效率低下的问题(纵表转横表的查询,本质上是连表取交集问题的解决思路)
需求
mysql作为关系型数据库,他对行内关系的描述较弱,比如有这样2个表,主表interface记录接口表,子表itf_param记录接口参数表。
itf_param假设构造如下:
字段名 | 描述 |
---|---|
id | 主键 |
itf_id | 接口id |
param_name | 参数名称 |
param_value | 参数值 |
现在要查所有(参数名='code',参数值='12')和(参数名='route',参数值='gw')的interface记录。
实现
通常我们会用如下sql实现:
select it.* from interface it where 1=1
and exists(
select 1 from itf_param p where p.param_name= 'code' and p.param_value='12'
)
and exists(
select 1 from itf_param p where p.param_name= 'route' and p.param_value='gw'
)
where 1=1 limit 0,10
在数据量少的情况下,这个sql是没有任何问题的,但是在大数据量场景下,此sql就难堪大任了,因为一般来讲子查询效率都会较低(这里即便分页了也是如此,具体原因要问DB工程师了,估摸着limit是最后被执行,所以逐条过滤大量数据导致效率较低)。
优化
通常连表查询效率高于子查询,这里采用纵表转横表的方式对sql进行优化,如下所示(伪sql):
select it.* ,
MAX(CASE WHEN p.param_name= 'code' THEN p.param_value ELSE NULL END) AS codeParamValue,
MAX(CASE WHEN p.param_name= 'route' THEN p.param_value ELSE NULL END) AS routeParamValue,
from interface it join itf_param p on(it.itf_id = p.itf_id)
where 1=1
group by it.*
having codeParamValue = '12' and codeParamValue='gw'
limit 0,10
真实场景sql优化持续更新(老司机必备)的更多相关文章
- 关于MYSQL优化(持续更新)
*利用MYSQL数据缓存提高效率,注意事项: 1.应用环境:不经常改变的表及对此表相同的查询 2.不适用于服务器端编写的语句 3.根据数据使用频率,合理分解表 4.合理使用默认条件,提高命中率 5.统 ...
- 特殊场景Sql优化
一.大表的大数据量修改 问题: 1.大量的行级锁,长时间阻塞 2.主从延时,大批数据不一致 解决方法: 分批次修改 二.大表的表结构修改 问题:长时间锁表 解决方法: 1.从库修改,主从切换,主库 ...
- SQL语言 持续更新中……
SQL提供了很多的聚集函数 COUNT([DISTINCT\ALL]*) SUM([DISTINCT\ALL]<列名>)AVG().…… WHERE 子句中是不能用聚集函数作为条件表达式 ...
- 几种常用排序算法代码实现和基本优化(持续更新ing..)
插入排序(InsertSort): 插入排序的基本思想:元素逐个遍历,在每次遍历的循环中,都要跟之前的元素做比较并“交换”元素,直到放在“合适的位置上”. 插入排序的特点:时间复杂度是随着待排数组的有 ...
- nginx(tengine)的一些小优化(持续更新)
1.nginx日志切割脚本 需求来源:nginx本身并没有日志切割的功能,由访问产生的大日志很难进行分析. 实现目的:每天对nginx日志进行切割,并备份至指定文件夹. 简要指令: mv /usr/l ...
- MySQL的一些常用sql函数(持续更新。。)
1. 字符串拼接函数 :CONCAT(str1,str2,...) SELECT CONCAT('AAA','BBB') STR; //AAABBB 2. 判断是否为null,为null就指定另外一个 ...
- SqlServer学习-常用的sql语句-持续更新中
1.获取数据库下的所有表名 select TABLE_NAME from information_schema.tables where TABLE_TYPE='Base TABLE' 2.随机取出1 ...
- SQL优化(子文章)(持续更新)
-----> 总文章 入口 文章目录 [-----> 总文章 入口](https://blog.csdn.net/qq_37214567/article/details/90174445) ...
- Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G
code&monkey Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...
- (摘)老司机也必须掌握的MySQL优化指南
当 MySQL 单表记录数过大时,增删改查性能都会急剧下降,本文会提供一些优化参考,大家可以参考以下步骤来优化. 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部 ...
随机推荐
- MySql数据库读取字段错误问题
一个小小的BUG,断断续续搞了一个月才搞定,使用MySql的时候使用mysql_fetch_fields()获取的字段始终始终是错误的,因为是修改别人的代码,一直找不到问题,Debug了无数次还是搞不 ...
- docker&docker-compose安装
一.docker安装 1.通过 uname -r 命令查看当前的内核版本,Docker 要求 CentOS 系统的内核版本高于 3.10 uname -r 2.查看系统是否安装过docker yum ...
- c语言中%d %f %c %s等的区别
%d整型输出(%ld长整型输出)%f以小数形式输出,默认情况下保留小数点6位 这里是引用%f和%lf分别是float类型和double类型用于格式化输入输出时对应的格式符号.其中:float,单精度浮 ...
- 什么是Markdown
什么是markdown? Markdown是一种轻量级标记语言,它允许人们使用已读一些的纯文本格式编写文档,然后转换成有效的XHTML(或者HTML)文档.这种语言吸收了很多在电子邮件中已有的纯文本标 ...
- LoadRunner——分析图详解(十四)
<分析图详解> 一.Running V user s 图 X轴表示运行所用的时间,Y轴表示vuser数, 显示在整个运行过程中随着时间的推移,虚拟用户数量是如何变化的,具体描述为:用户是如 ...
- 20个值得收藏的实用JavaScript技巧
1.确定对象的数据类型 function myType(type) { return Object.prototype.toString.call(type).slice(8, -1); 使用Obje ...
- Scanner进阶用法
Scanner进阶用法 判断是否为整数,浮点数 package charpter2; import java.util.Scanner; public class Scanner3 { public ...
- 记录hive一次数据倾斜问题的解决以及思考总结
解决数据倾斜是大数据开发中比较重要的能力,这个现象指的是分布式集群中,由于数据分发的不当,导致某个节点要处理的错误过多,导致整个计算机任务迟迟结束不了,甚至可能节点出现OOM使得任务失败 处理数据倾斜 ...
- Chronicle Pro - 一款简单 Mac 理财规划师,管理你的的个人预算
使用Chronicle追踪和支付账单,管理你的个人预算,这是一款简单的Mac理财规划师.获得通知,这样你就不会错过下一个付款截止日期;你再也不用付滞纳金了.把你所有的账单放在一起,计划.检查和分析它们 ...
- Laf v1.0 发布:函数计算只有两种,30s 放弃的和 30s 上线的
一般情况下,开发一个系统都需要前端和后端,仅靠一个人几乎无法胜任,需要考虑的特性和功能非常多,比如: 需要一个数据库来存放数据: 需要一个文件存储来存放各种文件,比如图片文件: 后端需要提供接口供前端 ...