【问题标题】:MariaDB - sql query performance or optimizationMariaDB - sql 查询性能或优化
【发布时间】:2020-06-13 05:23:34
【问题描述】:

我有一个存储过程。在性能方面有点问题。我想提高存储过程的性能,但我不知道该怎么做。我的数据库中有大约 300 万条记录。当我一个一个地运行这个查询时,它的性能很好。但是当 150 人同时运行这个存储过程时,CPU 就会出现峰值。

例如,我创建了我的过程和表结构。

我的存储过程:

BEGIN
SELECT ss.car_route from person o
inner join car_time ss on ss.inst_id =o.inst_id  
and ss.start_time<=DATE_FORMAT(CURTIME(),'%H:%i')  AND  ss.finish_time>= date_format(curtime() ,'%H:%i') AND ss.car_id=carid
and ss.days like concat('%',(select WEEKDAY(now())+1),'%') 
where (o.car_id=carid or o.back_car_id=carid ) LIMIT 1 into @route_;
select sf.stop_service from car_comp sf 
inner join cars s on s.inst_id = sf.id and s.id=carid and s.active=1 limit 1
into @stop_ser;



if @route_ = 1 and @stop_ser=0 THEN

select DISTINCT ss.start_time,ss.finish_time ,o.id,o.name,r.photo, oh.state ,oh.datee,ss.car_route,
ifnull(bh.id,0) AS called,
ifnull(mh.excuse_id,0) AS excuse_id,
ifnull(o.latitude_1,0) AS latitude_1,
ifnull(o.longitude_1,0) AS longitude_1,
ifnull(o.latitude_2,0) AS latitude_2,
ifnull(o.longitude_2,0) AS longitude_2,
case when (ifnull(o.call_notify,0)=1 or ifnull(o.mes_notify,0)=1) then 1 else 0 end AS call_notify ,
ifnull(o.rownumber,0) AS rownumber,
ifnull(o.number_1,0) AS number_1,
ifnull(o.number_2,0) AS number_2,
ifnull(o.brownumber,0) AS brownumber,
ifnull(ROUND(o.notify_meter_1/2),0) AS notify_meter_1,
ifnull(ROUND(o.notify_meter_2/2),0) AS notify_meter_2
from person o
inner join car_time ss on ss.inst_id =o.inst_id and o.car_id=ss.car_id
and ss.start_time<=DATE_FORMAT(CURTIME(),'%H:%i')  AND  ss.finish_time>= date_format(curtime() ,'%H:%i')
and ss.days like concat('%',(select WEEKDAY(now())+1),'%')

LEFT JOIN notify_records bh ON bh.table_id=o.id AND bh.car_route=@route_
and bh.table_name='person' AND bh.notify=4 AND bh.car_id=o.car_id and bh.date_ >= CURDATE() and bh.date_ < CURDATE() + INTERVAL 1 DAY 

left join  person_records oh   on  oh.person_id=o.id
and oh.car_id=o.car_id
and date_format(oh.datee,'%H:%i') >=ss.start_time
and date_format(oh.datee,'%H:%i') <=ss.finish_time
AND oh.car_route= @route_
and
oh.id in(select max(id) from person_records
where date_time >= CURDATE() and date_time < CURDATE() + INTERVAL 1 DAY and car_id = carid and car_id = carid
GROUP by person_id
)
left join inst ok on o.inst_id = ok.id and o.car_id=carid
left join excuse_records mh on mh.person_id=o.id and mh.date_time >= CURDATE() and mh.date_time < CURDATE() + INTERVAL 1 DAY and (mh.car_route=ss.car_route)
left join photo_ r on r.table_id = o.id and r.table_name = 'person'
where
(ss.car_route=o.cars_route_ or o.cars_route_=3) and

o.car_id = carid and o.active=1
AND o.work_time=ss.work_time;


elseif @route_ = 2 and @stop_ser=0 then

select DISTINCT ss.start_time,ss.finish_time ,o.id,o.name,r.photo, oh.state ,oh.datee,ss.car_route,
ifnull(bh.id,0) AS called,
ifnull(mh.excuse_id,0) AS excuse_id,
ifnull(o.latitude_1,0) AS latitude_1,
ifnull(o.longitude_1,0) AS longitude_1,
ifnull(o.latitude_2,0) AS latitude_2,
ifnull(o.longitude_2,0) AS longitude_2,
case when (ifnull(o.call_notify,0)=1 or ifnull(o.mes_notify,0)=1) then 1 else 0 end AS call_notify ,
ifnull(o.rownumber,0) AS rownumber,
ifnull(o.number_1,0) AS number_1,
ifnull(o.number_2,0) AS number_2,
ifnull(o.brownumber,0) AS brownumber,
ifnull(ROUND(o.notify_meter_1/2),0) AS notify_meter_1,
ifnull(ROUND(o.notify_meter_2/2),0) AS notify_meter_2
from person o
inner join car_time ss on ss.inst_id =o.inst_id and o.back_car_id=ss.car_id
and ss.start_time<=DATE_FORMAT(CURTIME(),'%H:%i')  AND  ss.finish_time>= date_format(curtime() ,'%H:%i')
and ss.days like concat('%',(select WEEKDAY(now())+1),'%')
LEFT JOIN notify_records bh ON bh.table_id=o.id AND bh.car_route=@route_
and bh.table_name='person' AND bh.notify=4 AND bh.car_id=o.back_car_id and bh.date_ >= CURDATE() and bh.date_ < CURDATE() + INTERVAL 1 DAY  
left join  person_records oh   on  oh.person_id=o.id
and oh.car_id=o.back_car_id and oh.car_route=2
and date_format(oh.datee,'%H:%i') >=ss.start_time
and date_format(oh.datee,'%H:%i') <=ss.finish_time
AND oh.car_route= @route_
and
oh.id in (select max(id) from person_records
where date_time >= CURDATE() and date_time < CURDATE() + INTERVAL 1 DAY and car_id = carid
GROUP by person_id
)
left join inst ok on o.inst_id = ok.id and o.car_id=carid
left join excuse_records mh on mh.person_id=o.id and mh.date_time >= CURDATE() and mh.date_time < CURDATE() + INTERVAL 1 DAY and (mh.car_route=ss.car_route)
left join photo_ r on r.table_id = o.id and r.table_name = 'person'
where
(ss.car_route=o.cars_route_ or o.cars_route_=3) and

o.back_car_id = carid and o.active=1
AND o.work_time=ss.work_time;


END IF;


end

I have a database example here.

我对 my.cnf 进行了改进,但性能仍然存在问题。这个查询有什么问题?我可以改变什么?

从现在开始谢谢你。

编辑:

服务器版本:10.1.41-MariaDB - MariaDB 服务器

我有索引。我在创建测试数据时忘记添加索引。

【问题讨论】:

  • 我不是 MySQL 专家,所以我不是在这里提供帮助的人。我确实注意到您对@route_ 和@stop_ser 的选择似乎很随意。在与日期和时间跨度匹配的所有路线中,您只需选择一条没有应用进一步规则的路线。然后您选择一个活动的 stop_route,再次不应用任何要选择的规则。您正在加入,我宁愿期待 INEXISTS 子句。您将时间设为字符串,而不是使用时间。您将几个星期的日期存储在一个字符串中,而不是有一个单独的表。
  • 然后,您的主要查询以SELECT DISTINCT 开头。这通常是一个写得很糟糕的查询的标志。为什么必须删除重复项?希望您的数据库已标准化,因此其中没有重复项。可能是您自己,盲目地加入表格,从而创建了一个太大的中间结果,您必须再次压缩。问问自己为什么要重复,并避免一开始就创建它们。然后删除distinct,您的查询应该会运行得更好。
  • 在您的小提琴示例中,没有索引,。组合索引中的 ON 或 WHERE 子句中应该有所有列。
  • 也许又是 INEXISTS 而不是可以帮助处理这些主要查询的连接。我不知道,因为我没有更仔细地查看您的查询。而且,是的,nbk 正确地提到您需要适当的索引。
  • 你好。首先,感谢您的回答。白天有2个方向。例如,中午前7点到9点之间路线_=1,下午15点到17点之间路线_=2。这只适用于1辆车。并且我使用变量 stop_ser 来停止一些公司的服务。 @ThorstenKettner

标签: mysql sql performance optimization mariadb


【解决方案1】:

这是什么鬼?

ss.days like concat('%',(select WEEKDAY(now())+1),'%')

至少可以通过更改为加快速度

ss.days like concat('%',WEEKDAY(now()),'%')

而且,如果 WEEKDAY 是“2”,这不会导致检查 2、21、20、12……吗?

这些可能对ss有用:

(car_id, inst_id, start_time)
(inst_id, car_id, finish_time)

LIMIT 1 没有ORDER BY 会导致返回一些随机行? LIMIT 是多余的吗?还是需要ORDER BY

建议您获取一些时间 -- SELECTs 中哪一个消耗的 CPU 最多并不明显。

如果carsPRIMARY KEYid,那么为什么要测试inst_idactive?哎呀!你好像没有cars的PK!请确认每个表都有一个 PK。

冗余:

              and  car_id = carid
              and  car_id = carid

为什么要两次?这些列在哪些表中?请限定列,以便我们了解发生了什么。

@stop_ser=0时,程序什么都不做?在这种情况下,请先执行该测试,这样您就可以避免计算 @route

start_time 更改为数据类型TIME;那么你就可以在

中摆脱DATE_FORMAT
      and  ss.start_time<=DATE_FORMAT(CURTIME(),'%H:%i')
      AND  ss.finish_time>= date_format(curtime() ,'%H:%i')

另外,请注意不等式测试,它可能会导致一些您不想要的极端情况。

不要在FLOAT 上使用(m,n)(例如,float(11,7));它会进行不必要的舍入。此外,除了非常靠近赤道和经度 = 0 之外,您无法获得 7 个小数位的 lat/lng。更多关于精度:http://mysql.rjweb.org/doc.php/latlng#representation_choices

在您清理完这些并提供所需信息后,我会再看一下。

【讨论】:

    猜你喜欢
    • 2013-07-02
    • 1970-01-01
    • 1970-01-01
    • 2021-04-06
    • 1970-01-01
    • 2018-02-07
    • 1970-01-01
    • 1970-01-01
    • 2016-07-09
    相关资源
    最近更新 更多