天天要闻:真实场景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 ,然后再求和,再分页。
(资料图片仅供参考)
查第一页
SELECTt.name,COUNT(1) as num FROMtest t WHERE1 = 1 GROUP BY t.`name`ORDER BYnum DESC LIMIT 0,5
查询结果是这样的:
name | num |
---|---|
李四 | 3 |
无话 | 3 |
张三 | 2 |
大炮 | 2 |
狐狸 | 2 |
查第二页
SELECTt.name,COUNT(1) as num FROMtest t WHERE1 = 1 GROUP BY t.`name`ORDER BYnum DESC LIMIT 5,5
查询结果是这样的:
name | num |
---|---|
狐狸 | 2 |
武器 | 1 |
刘可 | 1 |
败给 | 1 |
事变 | 1 |
结果分析
显然第二页的"狐狸"不应该出现,他是第一页的最后一条数据。这个问题在mysql官方是给予了答案的,其实只要是order by 的排序字段在结果集中不唯一,排序字段一致的行他返回的结果都是无序的,这一点不容易被重视,也不容易被测试所发现(单表一般需要较多重复数据和分页才容易被发现),算是一个小坑。
优化方案一
网上一般提供的思路: 既然排序字段不是唯一的,我们一般期望唯一排序,只需要在order by 中跟上唯一标识的字段即可,像下面这样:
SELECTt.name,COUNT(1) as num FROMtest t WHERE1 = 1 GROUP BY t.`name`ORDER BYnum DESC,t.id descLIMIT 5,5
但是这种方式有个致命问题,ORDER BY 后面接了两个字段会让索引失效,大数据场景下是不推荐这种方式的。
方案二
使用 ROW_NUMBER() OVER ( ORDER BY t.id) AS serial_number让他按照指定方式排序,这基本也是万机油解决方案,对代码侵入程度很低。但是我们这个场景下两种方式效率一样,因为本来num字段就没有索引,但是当order by 存在一个字段可以用索引的话就不一样了。
SELECTt.name,COUNT(1) as num ,ROW_NUMBER() OVER ( ORDER BY t.name) AS serial_numberFROMtest t WHERE1 = 1 GROUP BY t.`name`ORDER BYnum DESCLIMIT 5,5
场景二: 大表查询优化问题(多租户情景下的连表查询规范)需求假设有这样一个场景,要求查某公司的商品出售情况的数据,数据库设计如下:
表名 | 备注 |
---|---|
order | 订单表,有create_by 字段 |
goods | 商品表 |
logistics | 物流表 |
order_goods_mapping | 商品与订单关联表 |
order_logistics_mapping | 物流与订单关联表 |
先不考虑数据库设计是否合理,现在要分页查询商品销售情况,在不考虑数据量的情况下一般这样写sql(伪sql):
select g.*,o.*,l.* from goods gjoin 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 gjoin 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
上一篇:西安青龙寺的樱花开了吗2022 青龙寺樱花观赏时间-当前最新
下一篇:最后一页

天天要闻:真实场景sql优化持续更新(老司机必备)
概述下述场景,均来自实际产品线上经验,出于保密考量,所有需求场景都是仿造的,模拟遇到过的真实场景。场
2023-04-25
西安青龙寺的樱花开了吗2022 青龙寺樱花观赏时间-当前最新
我国有好几大赏樱的胜地,分别就是武汉大学、贵州平坝樱花、无锡鼋头渚以及西安的青龙寺等等。目前为止很多
2023-04-25
维康药业:募集说明书中2022年1-9月中药材贸易业务按照净额法核算确认收入326.06万元
维康药业(300878)04月25日在投资者关系平台上答复了投资者关心的问题。
2023-04-25
范可新夺铜后亲吻冰面精神_范可新夺铜后亲吻冰面
1、29岁的范可新在本届北京冬奥会中为国家争得了一枚金牌和一枚铜牌。2、然而,她在比赛正式结束之后,亲吻
2023-04-25
一公司买7000部iPhone 全是空盒_天天视点
华脉科技修订了2017年-2021年五份年报,原因是公司在苹果手机采购中遭遇合同欺诈,无法确认相关收入。该公
2023-04-25
【天天新视野】“五一”假期将至 北京市文旅场所将按最大承载量开放
记者从2023年北京市“五一”节假日旅游工作会上获悉,假日期间,北京市旅游景区、演出场所等文旅场所将按核
2023-04-25
全球百事通!净利率连续5年下滑,华熙生物新的增长极在哪?
从医美原料到护肤品、化妆品,再到食品、饮品,乃至宠物用品、织物、口腔、生殖健康等领域,如今到处可见玻
2023-04-25
快讯丨向市民发放4000万元数字人民币满减消费券-要闻速递
打好经济增长主动仗实现经济运行整体好转
2023-04-25
中央气象台:云南海南岛等地将有强对流天气
湖南江西雷暴,云南海南强对流,注意防范雷电、强降水、大风、冰雹等灾害。
2023-04-25
被一张色情照毁了的大学院长 天天报道
又有高校教授“手抖”将不雅图片发到工作群,暴露了自己下流本色。近日,一则关于东南大学党委宣传部长袁久
2023-04-25X 关闭





X 关闭
- 最新全国疫情中高风险地区名单:全国现有高中风险地区15+64个(统计时间:5月19日6时)
- 北京疫情最新消息|5月18日北京新增50例本土确诊病例和5例无症状感染者
- 上海疫情最新消息|5月18日上海新增本土确诊病例82例和本土无症状感染者637例
- 郑州限号|今天是2022年5月19日,郑州限行尾号是4和9
- 发码总数超68万!郑州市“场所码”覆盖精度再提升
- 郑州发布100号通告:调整封控管控区域
- 【“郑”在抗疫】郑州互联网企业开展爱心购瓜网络公益活动
- 10岁顽童因“想妈妈”爬楼顶,暖心民警化身“心理医生”解心结
- 洛阳馨悦社工:以微薄之力让社区更安全
- 平顶山新华区对4名违反疫情防控有关规定人员依法处理