记录一次sql 性能优化的分析过程

1. 业务场景分析

  • 目前有这样一个业务,我们需要查询一个物流列表,在这个物流列表中,会关联查询订单、历史物流记录、包裹等各种表信息;
  • 在数据量上,每张表的数据量在五千万~2亿条之间
  • 数据库:SQL Server
  • 查询耗时如图所示:
    在这里插入图片描述

    可以看出来,最慢甚至要39s !!!

  • 具体sql语句大致如下:
    	SELECT
    	s.订单ID AS 订单ID,
    	s.主键ID AS 包裹ID,
    	s.发货日期,
    	s.发货状态,
    	s.运单编号ID,
    	s.物流公司,
    	s.服务类别,
    	t.商品编号 商品编号,
    	s.发货ID AS 发货ID,
    	t.主键ID AS 包裹商品ID,
    	t.商品编号 AS 商品编号,
    	h.商品类型 AS 商品类型,
    	h.发货时间 AS 发货时间,
    	t.商品标题 AS 商品标题,
    	是否使用官方物流 = CASE WHEN l.主键ID IS NULL THEN 0 ELSE 1 END
    	FROM1 s WITH(NOLOCK)
    	LEFT JOIN2 l WITH(NOLOCK) ON s.主键ID = l.包裹ID
    	LEFT JOIN3 NPG WITH (NOLOCK) ON l.xxxGroupId = NPG.主键ID
    	LEFT JOIN4 ON (CAST(s.订单ID AS CHAR(50)) = h.xxxNumber		AND s.运单编号ID = h.运单编号ID AND s.发货ID = h.发货ID)
    	INNER JOIN5 t WITH(NOLOCK) ON s.主键ID = t.包裹编号
    	WHERE
    	s.订单ID =  #{订单ID}
    	AND s.卖家ID =  #{卖家ID}
    	AND s.发货状态 = 'S'
    

    具体字段名为了保证隐私,已做了修改处理;

2. 遇到的问题

  • 查询数据偶发性变慢,有时候10s+, 有时候2s以内;

3. 发现到的规律

  1. 在一段时间不调用之后,只有第一次查询时会变慢
  2. 由于多表查询,且每张表数据量较大,导致left join 时效率低下,同时left join 存在严重的性能损耗问题,尤其在多表联查且数据量较大又有筛选条件或者没有加索引的时候;
    1. 不加索引效率慢应该都明白
    2. 数据量大且有筛选条件的时候,left join 表的时候,先筛选条件再join 的性能会更高一点,举例如下:
      test1:
      	select t1.emp_no,t1.emp_name,t2.dep_no,t2.dep_name
      	from (
      	select t.emp_no,t.emp_name,t.dep_no
      	from employee
      	where t.emp_no < 80707999
      	) t1
      	left join department t2 on t1.dep_no = t2.dep_no
      
      test2:
      	select t1.emp_no,t1.emp_name,t2.dep_no,t2.dep_name
      	from employee t1
      	left join department t2 on t1.dep_no = t2.dep_no
      	where t1.emp_no < 80707999
      

    在大数据量的情况下 test1 比 test2 效率高

4. 通过分析规律发现原因:

  1. 针对“只有第一次查询会变慢”,查询变慢可能会有多种原因,可能是数据库自身问题,也可能是网络问题;
    1. 第一次查询变慢则很可能不是偶发性的,而可能是程序问题;为什么这么说呢?在一般数据库中,都会有查询优化,在数据不变的情况下会走查询缓存,而不是直接在磁盘中查找数据;此情景下,只有缓存才可能会在第一次查询才会变慢
    2. 我们使用的是sql server,它在执行任何查询的时候,都会先将数据读取到内存,数据使用之后,不会立即释放,而是会缓存在内存Buffer中,当再次执行相同的查询时,如果所需数据全部缓存在内存中,那么SQL Server不会产生Disk IO操作,立即返回查询结果,这是SQL Server的性能优化机制;
  2. 针对“left join”问题,在规律中已经发现,在大数据量的情况下,先做筛选再做left join往往性能更好;同时我们已经对相关查询字段做了索引,且没有其他查询条件,我们可以先把表1 进行先筛选,再left join即可。

5. 优化后的结果

  • 查询耗时300毫秒:
    在这里插入图片描述
  • sql如下:
DECLARE @ShipmentPackage Table
	(
	订单ID INT ,
	包裹ID VARCHAR(256) ,
	发货日期 DATETIME,
	发货状态 VARCHAR(128) ,
	运单编号ID VARCHAR(128),
	物流公司 VARCHAR(256) ,
	服务类型  VARCHAR(256),
	发货ID INT NULL
	)
	INSERT INTO @ShipmentPackage(
	订单ID,包裹ID,
	发货日期,
	发货状态,运单编号ID,物流公司,服务类型,发货ID
	)
	SELECT
	s.订单ID AS 订单ID,
	s.主键ID AS 包裹ID,
	s.发货日期 AS 发货日期,
	s.发货状态 AS 发货状态,
	s.运单编号ID AS 运单编号ID,
	s.物流公司 AS 物流公司,
	s.服务类别 AS 服务类型,
	s.发货ID AS 发货ID
	FROM1 s WITH(NOLOCK)
	WHERE
	s.订单ID =  #{订单ID}
	AND s.卖家ID =  #{卖家ID}
	AND s.发货状态 = 'S'

	SELECT
	s.订单ID,
	s.包裹ID,
	s.发货日期,
	s.发货状态,
	s.运单编号ID,
	s.物流公司,
	s.服务类型,
	s.发货ID,
	t.商品编号 商品编号,
	t.主键ID AS 包裹商品ID,
	t.商品编号 AS itemNumber,
	h.商品类型 AS 商品类型,
	h.发货时间 AS deliveryTime,
	t.商品标题 AS itemTitle,
	是否使用官方物流 = CASE
	WHEN l.主键ID IS NULL THEN 0
	ELSE 1
	END
	FROM
	@ShipmentPackage s
	LEFT JOIN2 l WITH(NOLOCK) ON s.包裹ID = l.包裹ID
	LEFT JOIN3 NPG WITH (NOLOCK) ON l.xxxGroupId = NPG.主键ID
	LEFT JOIN4 ON (
	CAST(s.订单ID AS CHAR(50)) = h.xxxNumber
	AND s.运单编号ID = h.运单编号ID
	AND s.发货ID = h.发货ID
	)
	INNER JOIN5 t WITH(NOLOCK) ON s.包裹ID = t.包裹编号

猜你喜欢

转载自blog.csdn.net/qq_37128049/article/details/122297787