[英]How can I optimize the following MySQL query to achieve concurrent calls per seconds?
以下查询从DB1.Data表中读取数据,该查询正常运行,但速度很慢。 该查询结果是来自CDR信息的并发呼叫。
MySQL查询
select sql_calc_found_rows H,M,S,(TCNT+ADCNT) as CNT from
(
select H,M,S,sum(CNT) as TCNT,
(
select
count(id) as CNT
from DB1.Data force index (datetimeOrgination) where 1=1 and
(datetimeOrgination<UNIX_TIMESTAMP(concat('2018-02-09',' ',T1.H,':',T1.M,':',T1.S)) and (datetimeOrgination+callDuration)>UNIX_TIMESTAMP(concat('2018-02-09',' ',T1.H,':',T1.M,':',T1.S)))
and (DB1.Data.datetimeOrgination between UNIX_TIMESTAMP('2018-02-09 00:00:00') and UNIX_TIMESTAMP('2018-02-09 23:59:59'))
) as ADCNT
from
(
(select
hour(from_unixtime(datetimeOrgination)) as H,
minute(from_unixtime(datetimeOrgination)) as M,
second(from_unixtime(datetimeOrgination)) as S,
count(id) as CNT
from DB1.Data where 1=1 and (DB1.Data.datetimeOrgination between UNIX_TIMESTAMP('2018-02-09 00:00:00') and UNIX_TIMESTAMP('2018-02-09 23:59:59'))
group by hour(from_unixtime(datetimeOrgination)),minute(from_unixtime(datetimeOrgination)),second(from_unixtime(datetimeOrgination)))
Union all
(select
hour(from_unixtime(datetimeOrgination+callDuration)) as H,
minute(from_unixtime(datetimeOrgination+callDuration)) as M,
second(from_unixtime(datetimeOrgination+callDuration)) as S,
count(id) as CNT
from DB1.Data force index (datetimeOrgination) where 1=1 and
(second(from_unixtime(datetimeOrgination+callDuration))>second(from_unixtime(datetimeOrgination))) and (DB1.Data.datetimeOrgination between UNIX_TIMESTAMP('2018-02-09 00:00:00') and UNIX_TIMESTAMP('2018-02-09 23:59:59'))
group by hour(from_unixtime(datetimeOrgination+callDuration)),minute(from_unixtime(datetimeOrgination+callDuration)),second(from_unixtime(datetimeOrgination+callDuration)))
) as T1 group by H,M,S
) as T2;
这是说明输出
这是JSON格式的查询输出:
{
"meta": {
"count": 18,
"totalCount": 18
},
"calls": [{
"H": 10,
"M": 30,
"S": 44,
"CNT": 1
}, {
"H": 11,
"M": 27,
"S": 1,
"CNT": 1
}, {
"H": 11,
"M": 28,
"S": 44,
"CNT": 1
}, {
"H": 12,
"M": 23,
"S": 52,
"CNT": 1
}, {
"H": 12,
"M": 29,
"S": 27,
"CNT": 1
}, {
"H": 12,
"M": 30,
"S": 38,
"CNT": 1
}, {
"H": 14,
"M": 26,
"S": 17,
"CNT": 1
}, {
"H": 14,
"M": 26,
"S": 44,
"CNT": 1
}, {
"H": 14,
"M": 26,
"S": 51,
"CNT": 1
}, {
"H": 14,
"M": 27,
"S": 2,
"CNT": 1
}, {
"H": 14,
"M": 27,
"S": 8,
"CNT": 1
}, {
"H": 14,
"M": 40,
"S": 27,
"CNT": 1
}, {
"H": 14,
"M": 40,
"S": 57,
"CNT": 1
}, {
"H": 14,
"M": 40,
"S": 58,
"CNT": 1
}, {
"H": 15,
"M": 8,
"S": 4,
"CNT": 1
}, {
"H": 15,
"M": 8,
"S": 31,
"CNT": 1
}, {
"H": 15,
"M": 56,
"S": 38,
"CNT": 1
}, {
"H": 16,
"M": 27,
"S": 30,
"CNT": 1
}]
}
结果中的第一条记录
"H": 10,
"M": 30,
"S": 44,
"CNT": 1
显示我们在10:30:44有1个并发呼叫
更多细节
为了计算每秒的并发呼叫数,我们应该计算每秒3种呼叫类型。
例如,如果我们要计算10:51:20的并发调用,则需要计算以下所有内容:
步骤1:计算所有在10:51:20开始的通话
步骤2-计算所有呼叫在10:51:20结束,但未在同一秒开始(20)。
步骤3-计算所有在10:51:20之前开始并在10:51:20之后结束的呼叫。
步骤4-最后,需要对所有这些求和进行求和以计算并发调用。
此查询适用于步骤1
(select
hour(from_unixtime(datetimeOrgination)) as H,
minute(from_unixtime(datetimeOrgination)) as M,
second(from_unixtime(datetimeOrgination)) as S,
count(id) as CNT
from DB1.Data where 1=1 and (DB1.Data.datetimeOrgination between UNIX_TIMESTAMP('2018-02-09 00:00:00') and UNIX_TIMESTAMP('2018-02-09 23:59:59'))
group by hour(from_unixtime(datetimeOrgination)),minute(from_unixtime(datetimeOrgination)),second(from_unixtime(datetimeOrgination)))
该查询适用于步骤2
(select
hour(from_unixtime(datetimeOrgination+callDuration)) as H,
minute(from_unixtime(datetimeOrgination+callDuration)) as M,
second(from_unixtime(datetimeOrgination+callDuration)) as S,
count(id) as CNT
from DB1.Data force index (datetimeOrgination) where 1=1 and
(second(from_unixtime(datetimeOrgination+callDuration))>second(from_unixtime(datetimeOrgination))) and (DB1.Data.datetimeOrgination between UNIX_TIMESTAMP('2018-02-09 00:00:00') and UNIX_TIMESTAMP('2018-02-09 23:59:59'))
group by hour(from_unixtime(datetimeOrgination+callDuration)),minute(from_unixtime(datetimeOrgination+callDuration)),second(from_unixtime(datetimeOrgination+callDuration)))
该查询是针对前2个查询的并集结果的第3步查询
(
select
count(id) as CNT
from DB1.Data force index (datetimeOrgination) where 1=1 and
(datetimeOrgination<UNIX_TIMESTAMP(concat('2018-02-09',' ',T1.H,':',T1.M,':',T1.S)) and (datetimeOrgination+callDuration)>UNIX_TIMESTAMP(concat('2018-02-09',' ',T1.H,':',T1.M,':',T1.S)))
and (DB1.Data.datetimeOrgination between UNIX_TIMESTAMP('2018-02-09 00:00:00') and UNIX_TIMESTAMP('2018-02-09 23:59:59'))
) as ADCNT
该查询将收集所有这些查询并返回最终结果。
select sql_calc_found_rows H,M,S,(TCNT+ADCNT) as CNT from
(
如前所述,该查询有效但非常缓慢且复杂,我知道需要优化和简化。
栏位类型
`datetimeOrgination` BIGINT(20) NOT NULL DEFAULT
`callDuration` BIGINT(20) NOT NULL DEFAULT '0',
和索引
INDEX `datetimeOrgination` (`datetimeOrgination`),
INDEX `callDuration` (`callDuration`),
警告:我的一些建议是为了清楚或简化,不一定是为了提高速度。
潜在的错误: and (second(from_unixtime(datetimeOrgination+callDuration)) > second(from_unixtime(datetimeOrgination)))
没有多大意义。 它将捕获从11:22:00开始的2秒呼叫,但不会捕获从11:21:59开始的呼叫。 那真的是您想要的吗? 无论如何,请说明查询要执行的操作。
不要使用H,M,S,只需几秒钟即可工作-通过从日期中提取hh:mm:ss字符串,或以秒为单位获取一天中的时间。 转换为H,M,S作为最后一步,而不是第一步 。
不要FORCE INDEX
-今天可能会有所帮助,但明天会受到伤害。
将and (DB1.Data.datetimeOrgination between UNIX_TIMESTAMP('2018-02-09 00:00:00') AND UNIX_TIMESTAMP('2018-02-09 23:59:59'))
更改为
AND DB1.Data.datetimeOrgination >= '2018-02-00'
AND DB1.Data.datetimeOrgination < '2018-02-00' + INTERVAL 1 DAY
(同样,这是为了清楚起见,而不是速度。)
使用COUNT(*)
而不是COUNT(id)
我正在做很多猜测; 通过提供SHOW CREATE TABLE
帮助我们。 闻起来好像您为datetimeOrgination
使用了错误的数据类型。
转换为秒(从H,M,S)后,
datetimeOrgination < UNIX_TIMESTAMP(concat('2018-02-09',' ',',T1.H,':',T1.M,':',T1.S)
变成像
datetimeOrgination < '2018-02-09' + INTERVAL secs SECOND
更好的是从子查询中提取日期时间,然后移至类似
datetimeOrgination < datetime_from_subquery
这样可能会更好地使用索引。
清理代码并说明目标; 我将尝试提出更多的加速方案。
(由于问题的定义正在变化,所以我开始一个新的答案。)
在特定时间点的(所有类型的)呼叫次数很简单:
SELECT COUNT(*) FROM tbl
WHERE call_start <= '2018-02-14 15:11:35'
WHERE call_start + duration >= '2018-02-14 15:11:35';
但是,我会怀疑答案是“高”的,因为它没有考虑呼叫在给定秒数的哪一部分开始或结束。 因此,我认为这更接近纠正:
SELECT COUNT(*) FROM tbl
WHERE call_start < '2018-02-14 15:11:35'
WHERE call_start + duration >= '2018-02-14 15:11:35';
这应该尽可能地接近确切地说'2018-02-14 15:11:35.000000'发生了多少个并发调用; 它是'2018-02-14 15:11:35.5'的近似数字。
通过将COUNT(*)
更改为SUM(...)
(如前所述),可以获得给定类型的呼叫的计数。
然后,您可以使用datetime或timestamp算法添加GROUP BY
以完成任务。
一天
接听一天中开始的所有呼叫:
WHERE call_start >= '2018-02-09'
AND call_start < '2018-02-09' + INTERVAL 1 DAY
问题定义错误
为了计算每秒的并发呼叫数,我们应该计算每秒3种呼叫类型...
我认为这在数学上是错误的。
“并发呼叫”是即时的,而不是一秒钟(或一小时或一天)。 这表示“当时正在使用多少个电话连接。
让我将问题的陈述更改为“每小时并发通话”。 那有意义吗? 您可以询问“每小时呼叫”,这可以解释为“每小时发起的呼叫”,可以通过datetimeOrgination
和GROUP BY
进行计算。
假设我在每分钟开始时打电话,每次持续59秒。 一条电话线就可以解决这个问题。 我建议是“ 1个并发调用”。
相反,如果我有60个人都在中午开始他们59秒的通话,该怎么办? 那将需要60条电话线。 在一天的繁忙时间内,这将是60个并发呼叫。
您拥有的指标涉及一个datetimeOrgination
,它被截断(或四舍五入到1秒)边界。
让我不要修改示例以更好地解释您的3个步骤错误的原因。 我想按小时分组,并且我愿意在小时的顶部衡量通话次数。 特别地,让我们看一下10点钟的时间。
为什么将10分钟的通话计为两个小时? 这会增加“并发”计数。
因此,我认为唯一合理的计算是
第1步-计算所有始于10:51:20的呼叫-计算为在:20瞬间发生。
步骤2-计算所有呼叫均
在
10:51:20 之前结束,但未在同一秒(20)中开始。 - 不计入:20。
步骤3-计算所有在10:51:20之前开始并在10:51:20之后结束的呼叫。 -计算为:20瞬间。
我建议的解决方案可以实现这种修改,并且更简单且在数学上是“正确的”。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.