提高Mysql总和查询性能

时间:2014-11-17 07:08:22

标签: mysql

所以,我有一个这样的表,大约有4800万行。

User    Count
1232    12
12331   4534
...     ...
...     ....

这是一个只读表。不会再添加任何数据了。我需要找到两个 userid ...

之间所有用户的计数列总和

我现在的查询是:

Select sum(count) from table where user between x and Y.

但每次查询需要超过2秒。我需要彻底降低它。有什么办法可以做到吗?我是否需要创建聚簇索引或您能想到的任何其他配置?

更新:我已经在用户列上有一个索引

2 个答案:

答案 0 :(得分:1)

如果您在索引到位时性能不理想,并且您的数据库是只读的,您可能会考虑我在评论中提到的方法:预计算块的总和。它比分区更进一步:分区将能够并行计算您的总和,但是预先计算的总和会大大超过它。理想的块大小大约是表中行数的平方根。

说这是你的桌子:

CREATE TABLE foo (
  user INTEGER AUTO_INCREMENT PRIMARY KEY,
  cnt INTEGER
);

INSERT INTO foo (cnt) VALUES (1), (4), (9), (16), (25), (36), (49), (64), (81), (100);

现在制作一个预先计算的块总和表。为了清楚起见,我在这里使用SQL变量,你可能不需要使用它们,因为你将从另一种编程语言构造你的查询:

SET @block = 3;

CREATE TABLE foosums (
  block INTEGER PRIMARY KEY,
  cntsum INTEGER
)
SELECT FLOOR((user - 1) / @block) AS block, SUM(cnt) AS cntsum
FROM foo GROUP BY block;

现在,要计算@from@to之间的总和,您将获取这两者之间所有完整块的总和,并添加块之前和块之后的所有单独行。在这个例子中,要添加行1..10,我们将获取块1 ... 3,块4..6,块7..9和单个行10。

SET @from=1, @to=10;

SELECT
  COALESCE((
    SELECT SUM(cnt)
    FROM foo
    WHERE user >= @from AND user < CEILING((@from - 1) / @block) * @block + 1
  ), 0)
+ COALESCE((
    SELECT SUM(cntsum)
    FROM foosums
    WHERE block >= CEILING((@from - 1) / @block) AND block < FLOOR(@to / @block)
  ), 0)
+ COALESCE((
    SELECT SUM(cnt)
    FROM foo
    WHERE user > FLOOR(@to / @block) * @block AND user <= @to
  ), 0)
AS blocked_total;

要验证所有内容是否正常工作,这里是未经优化的查询,而不是使用块总和:

SELECT SUM(cnt) AS individual_total FROM foo WHERE user >= @from AND user <= @to;

最后,可视化可帮助您准确查看优化查询所包含的数据:

SELECT * FROM foo WHERE user >= @from AND user < CEILING((@from - 1) / @block) * @block + 1;
SELECT * FROM foosums WHERE block >= CEILING((@from - 1) / @block) AND block < FLOOR(@to / @block);
SELECT * FROM foo WHERE user > FLOOR(@to / @block) * @block AND user <= @to;

SQLFiddle


*)“chunk”==“block”。我在文本之前编写了代码,并且不想改变术语:p

答案 1 :(得分:0)

一个小小的技术回答可能来自一个自学成才的人,他不熟悉一些更先进的技术。免责声明完整。这就是我所做的,如果我知道数据不会改变。

我创建一个脚本将其拆分为几十个甚至一百个单独的表,根据范围进行聚类和命名。例如,表一可以命名为#34; cluster_1_to_10000&#34;或者基于你对范围和用户数的了解。

这样,当您进行查询时,您可以根据范围使代码中的表名动态化,并节省时间过滤无聊的数据。它会使PHP?更复杂,特别是如果范围落在多个表之间,但我认为额外的30行左右的PHP代码值得每次跳过几十万行。

不确定这是否得到了解释,如果您愿意,我可以提供一些伪代码示例。

编辑伪代码

名为:&#34; table_1_to_499999&#34;,&#34; table_500000_to_999999&#34;等 您需要编写一个脚本来拆分它们并创建每个表格,并且显然保留原文,以防万一。

伪代码(抱歉不熟悉java):

Var StartTable="";
Var EndTable="";

var Table=array();

Table=//populate table from select tables statement to get them in order
//make each tables key the start value as it pulls the table list from your database
//for example 
Table[1]="table_1_to_499999"
Table[500000]="table_500000_to_999999", etc.

//now you have your two user id's
user-id1 and user-id2;

var table1='';
var table2='';

var key1=0;
var key2=0;

foreach(Table as key=>val)
{
   if(user_id1>key)
   {
      table1=val;
      key1=key;
   }
}

foreach(Table as key=>val)
{
   if(user_id2>key)
   {
      table2=val;
      key2=key;
   }
}

if(key1==key2)
{
   //do your query here, all from the same table, both id's are in the same table, so you can query either table1 or table2 to get your data
}
else if(key1>key2)
{
   //query all results greater than or equal to user-id1 in table 1
   //query all results less than or equal to user-id2 in table 2
   //add the two results together
}
else if(key1<key2)
{
   //query all results less than or equal to user-id1 in table 1
   //query all results greater than or equal to user-id2 in table 2
   //add the two results together
}

我认为这涵盖了它......也许我已经去吃午餐,但我认为这会增加一次复杂性,大大节省查询。你可能不得不做两个查询来提取数据,增加了一些开销,但你只需要查询一百万行而不是4800万行。减少取决于你如何拆分它们。