Si desea que el DBA se bloquee instantáneamente, déjelo optimizar el rendimiento de SQL

Este artículo es compartido por la comunidad HUAWEI CLOUD " Optimizar el rendimiento de SQL es realmente llamativo ", autor: tienda de comestibles de Shi Zhenzhen.

Muchos cálculos de big data se implementan con SQL. Cuando se ejecuta lentamente, necesita optimizar SQL, pero a menudo se encuentra con situaciones que deslumbran a las personas. Por ejemplo, tres declaraciones en un procedimiento almacenado que se ven así son lentas de ejecutar:

select a,b,sum(x) from T group by a,b where …;
select c,d,max(y) from T group by c,d where …;
select a,c,avg(y),min(z) from T group by a,c where …;
复制代码

Aquí T es una tabla enorme con cientos de millones de filas. Debe agruparse de tres maneras y el conjunto de resultados de la agrupación no es grande.

La operación de agrupación debe recorrer la tabla de datos, y estas tres sentencias SQL deben recorrer la tabla grande tres veces. Lleva mucho tiempo recorrer cientos de millones de filas de datos, por no mencionar tres veces. En esta operación de agrupación, el tiempo de cálculo de la CPU es casi insignificante en relación con el tiempo de atravesar el disco duro. Si se pueden calcular varios resúmenes de agrupación en un recorrido, aunque la cantidad de cálculo de la CPU no se reduce, la cantidad de datos leídos del disco duro se puede reducir considerablemente y la velocidad se puede duplicar. Si SQL admite una sintaxis como esta:

from T    -- 数据来自 T 表
       select a,b,sum(x) group by a,b where …            -- 遍历中的第一种分组
       select c,d,max(y) group by c,d where …            -- 遍历中的第二种分组
       select a,c,avg(y),min(z) group by a,c where …;  -- 遍历中的第三种分组
复制代码

Ser capaz de devolver varios conjuntos de resultados a la vez puede mejorar mucho el rendimiento.

Desafortunadamente, SQL no tiene esta sintaxis, por lo que es imposible escribir tal declaración. Solo hay una solución alternativa, es decir, usar el método de escritura del grupo a, b, c, d para calcular primero un resultado de agrupación más detallado. establecido, pero primero guárdelo como una tabla Temporal, para poder usar SQL para calcular el resultado objetivo. El SQL es más o menos como sigue:

create table T_temp as select a,b,c,d,
       sum(case when … then x else 0 end) sumx,
       max(case when … then y else null end) maxy,
       sum(case when … then y else 0 end) sumy,
       count(case when … then 1 else null end) county,
       min(case when … then z else null end) minz
group by a,b,c,d;
select a,b,sum(sumx) from T_temp group by a,b where …;
select c,d,max(maxy) from T_temp group by c,d where …;
select a,c,sum(sumy)/sum(county),min(minz) from T_temp group by a,c where …;
复制代码

Esto solo debe atravesarse una vez, pero para transferir diferentes condiciones DONDE al caso anterior cuando, el código es mucho más complicado y la cantidad de cálculo aumentará. Además, cuando el número de campos de agrupación en la tabla temporal aumenta, el conjunto de resultados puede ser muy grande.Finalmente, la tabla temporal se recorre muchas veces y el rendimiento del cálculo no es rápido. El cálculo de agrupación de conjuntos de resultados grandes también requiere caché de disco duro y su rendimiento también es muy bajo.

También puede usar el cursor de la base de datos del procedimiento almacenado para obtener los datos uno por uno para el cálculo, pero debe implementar las acciones WHERE y GROUP por su cuenta. Es demasiado engorroso de escribir y el rendimiento del cursor de la base de datos atravesar los datos solo será peor! ¡Solo puedo mirar!

Las operaciones de TopN también encontrarán este tipo de impotencia. Por ejemplo, usar SQL de Oracle para escribir top5 es más o menos así:

select * from (select x from T order by x desc) where rownum<=5
复制代码

表 T 有 10 亿条数据,从 SQL 语句来看,是将全部数据大排序后取出前 5 名,剩下的排序结果就没用了!大排序成本很高,数据量很大内存装不下,会出现多次硬盘数据倒换,计算性能会非常差!

避免大排序并不难,在内存中保持一个 5 条记录的小集合,遍历数据时,将已经计算过的数据前 5 名保存在这个小集合中,取到的新数据如果比当前的第 5 名大,则插入进去并丢掉现在的第 5 名,如果比当前的第 5 名要小,则不做动作。这样做,只要对 10 亿条数据遍历一次即可,而且内存占用很小,运算性能会大幅提升。

这种算法本质上是把 TopN 也看作与求和、计数一样的聚合运算了,只不过返回的是集合而不是单值。SQL 要是能写成这样:select top(x,5) from T 就能避免大排序了。

然而非常遗憾,SQL 没有显式的集合数据类型,聚合函数只能返回单值,写不出这种语句!

不过好在全集的 TopN 比较简单,虽然 SQL 写成那样,数据库却通常会在工程上做优化,采用上述方法而避免大排序。所以 Oracle 算那条 SQL 并不慢。

但是,如果 TopN 的情况复杂了,用到子查询中或者和 JOIN 混到一起的时候,优化引擎通常就不管用了。比如要在分组后计算每组的 TopN,用 SQL 写出来都有点困难。Oracle 的 SQL 写出来是这样:

select * from
       (select y,x,row_number() over (partition by y order by x desc) rn from T)
where rn<=5
复制代码

这时候,数据库的优化引擎就晕了,不会再采用上面说的把 TopN 理解成聚合运算的办法。只能去做排序了,结果运算速度陡降!

假如 SQL 的分组 TopN 能这样写:

select y,top(x,5) from T group by y
复制代码

把 top 看成和 sum 一样的聚合函数,这不仅更易读,而且也很容易高速运算。 可惜,不行。

还是干瞪眼!

关联计算也是很常见的情况。以订单和多个表关联后做过滤计算为例,SQL 大体是这个样子:

select o.oid,o.orderdate,o.amount
       from orders o
              left join city ci on o.cityid = ci.cityid
              left join shipper sh on o.shid=sh.shid
              left join employee e on o.eid=e.eid
              left join supplier su on o.suid=su.suid
       where ci.state='New York'
              and e.title = 'manager'
              and ...
复制代码

订单表有几千万数据,城市、运货商、雇员、供应商等表数据量都不大。过滤条件字段可能会来自于这些表,而且是前端传参数到后台的,会动态变化。

SQL 一般采用 HASH JOIN 算法实现这些关联,要计算 HASH 值并做比较。每次只能解析一个 JOIN,有 N 个 JOIN 要执行 N 遍动作,每次关联后都需要保持中间结果供下一轮使用,计算过程复杂,数据也会被遍历多次,计算性能不好。

通常,这些关联的代码表都很小,可以先读入内存。如果将订单表中的各个关联字段预先做序号化处理,比如将雇员编号字段值转换为对应雇员表记录的序号。那么计算时,就可以用雇员编号字段值(也就是雇员表序号),直接取内存中雇员表对应位置的记录,性能比 HASH JOIN 快很多,而且只需将订单表遍历一次即可,速度提升会非常明显! 也就是能把 SQL 写成下面的样子:

select o.oid,o.orderdate,o.amount
       from orders o
              left join city c on o.cid = c.#       -- 订单表的城市编号通过序号 #关联城市表
              left join shipper sh on o.shid=sh.#     -- 订单表运货商号通过序号 #关联运货商表
              left join employee e on o.eid=e.#      -- 订单表的雇员编号通过序号 #关联雇员表
              left join supplier su on o.suid=su.#     -- 订单表供应商号通过序号 #关联供应商表
       where ci.state='New York'
              and e.title = 'manager'
              and ...
复制代码

可惜的是,SQL 使用了无序集合概念,即使这些编号已经序号化了,数据库也无法利用这个特点,不能在对应的关联表这些无序集合上使用序号快速定位的机制,只能使用索引查找,而且数据库并不知道编号被序号化了,仍然会去计算 HASH 值和比对,性能还是很差!

有好办法也实施不了,只能再次干瞪眼!

还有高并发帐户查询,这个运算倒是很简单:

select id,amt,tdate,… from T
              where id='10100'
              and tdate>= to_date('2021-01-10', 'yyyy-MM-dd')
              and tdate<to_date('2021-01-25', 'yyyy-MM-dd')
              and …
复制代码

在 T 表的几亿条历史数据中,快速找到某个帐户的几条到几千条明细,SQL 写出来并不复杂,难点是大并发时响应速度要达到秒级甚至更快。为了提高查询响应速度,一般都会对 T 表的 id 字段建索引:

create index index_T_1 on T(id)
复制代码

在数据库中,用索引查找单个帐户的速度很快,但并发很多时就会明显变慢。原因还是上面提到的 SQL 无序理论基础,总数据量很大,无法全读入内存,而数据库不能保证同一帐户的数据在物理上是连续存放的。硬盘有最小读取单位,在读不连续数据时,会取出很多无关内容,查询就会变慢。高并发访问的每个查询都慢一点,总体性能就会很差了。在非常重视体验的当下,谁敢让用户等待十秒以上!

容易想到的办法是,把几亿数据预先按照帐户排序,保证同一帐户的数据连续存储,查询时从硬盘上读出的数据块几乎都是目标值,性能就会得到大幅提升。

Sin embargo, la base de datos relacional que utiliza el sistema SQL no tiene este conocimiento y no impone el orden físico de almacenamiento de datos. Este problema no es causado por la sintaxis de SQL, sino que también está relacionado con la base teórica de SQL, y estos algoritmos no se pueden implementar en bases de datos relacionales.

¿Qué pasa ahora? ¿Puedes simplemente mirar?

Ya no se puede usar SQL y bases de datos relacionales, para usar otros motores de cómputo.

El esProc SPL de código abierto se basa en fundamentos teóricos innovadores, admite más tipos de datos y operaciones, y puede describir nuevos algoritmos en los escenarios anteriores. ¡Escribir código con SPL simple y conveniente puede mejorar en gran medida el rendimiento informático en poco tiempo!

Los ejemplos de código escritos en SPL para los problemas anteriores son los siguientes:

Calcule varios grupos en una sola pasada

inserte la descripción de la imagen aquíCalcular Top5 por agregación

Complete Works Top5 (computación paralela de subprocesos múltiples)

inserte la descripción de la imagen aquí

Grupo Top5 (computación paralela de subprocesos múltiples)

inserte la descripción de la imagen aquíCódigo SPL asociado con el número de serie:

inicialización del sistema

inserte la descripción de la imagen aquí

Preguntar

inserte la descripción de la imagen aquí

Código SPL para consulta de cuenta concurrente alta:

Preprocesamiento de datos, almacenamiento ordenado

inserte la descripción de la imagen aquíConsulta de cuenta

inserte la descripción de la imagen aquí

Además de estos ejemplos simples, SPL también puede implementar más algoritmos de alto rendimiento, como la combinación ordenada para realizar la asociación entre órdenes y detalles, la tecnología de preasociación para realizar la asociación de tablas multidimensionales en el análisis multidimensional y el almacenamiento de bits. tecnología para realizar miles de etiquetas Las estadísticas y la tecnología de recopilación booleana pueden acelerar la consulta de múltiples condiciones de filtro de valores de enumeración, y la tecnología de agrupación de series temporales puede realizar análisis de embudos complejos, etc.

Amigos que están luchando con la optimización del rendimiento de SQL, vengan y hablen con nosotros:

Dirección de descarga de SPL : c.raqsoft.com.cn/article/159…

Dirección de código abierto de SPL : github.com/SPLWare/esP…

Haga clic en Seguir para conocer las nuevas tecnologías de HUAWEI CLOUD por primera vez~

Supongo que te gusta

Origin juejin.im/post/7078128296038252557
Recomendado
Clasificación