如何获得大表计数?

时间:2019-04-05 11:59:28

标签: mysql sql database postgresql performance

样品表:

+----+-------+-------+-------+-------+-------+---------------+
| id | col1  | col2  | col3  | col4  | col5  | modifiedTime  |
+----+-------+-------+-------+-------+-------+---------------+
|  1 | temp1 | temp2 | temp3 | temp4 | temp5 | 1554459626708 |
+----+-------+-------+-------+-------+-------+---------------+ 

上表具有5000万条记录

  1. (col1,col2,col3,col4,col5这些是VARCHAR列)
  2. (id是PK)
  3. (modifiedTime)

每个列都被索引

对于Ex:我的网站上有两个标签。

FirstTab-我使用以下条件打印上表的计数[col1如“ value1%”和col2如“ value2%”]

SeocndTab-我使用以下条件[col3,例如“ value3%”]打印上表的计数


因为我有5000万条记录,所以按照这些条件进行计数需要太多时间才能得出结果。

注意:我有时会更改记录数据(表中的行)。插入新行。删除不需要的记录。

我需要一个可行的解决方案,而不是查询整个表。例如:像缓存较早的计数。像这样可能吗?

10 个答案:

答案 0 :(得分:4)

虽然我确定MySQL可行,但这是使用触发器的Postgres解决方案。

计数存储在另一个表中,每个插入/更新/删除操作都有一个触发器,用于检查新行是否满足条件,如果满足,则将计数加1。触发器的另一部分检查旧行是否满足条件,如果满足,则减去1。

这是触发器的基本代码,该触发器用<HorizontalScrollView android:layout_weight="50" android:layout_width="0dp" android:layout_height="150dp" > <ListView android:minWidth="25px" android:minHeight="25px" android:layout_width="150dp" android:layout_height="150dp" android:id="@+id/BarcodeChorusLocalizationListView" /> </HorizontalScrollView> 对行进行计数:

temp2 = '5'

Here's a working example on dbfiddle

您当然可以修改触发代码,使其具有动态的where表达式和表中存储的计数,例如:

CREATE OR REPLACE FUNCTION updateCount() RETURNS TRIGGER AS 
$func$
BEGIN
   IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
      EXECUTE 'UPDATE someTableCount SET cnt = cnt + 1 WHERE 1 = (SELECT 1 FROM (VALUES($1.*)) x(id, temp1, temp2, temp3) WHERE x.temp2 = ''5'')'
      USING NEW;
   END IF;
   IF TG_OP = 'DELETE' OR TG_OP = 'UPDATE' THEN
      EXECUTE 'UPDATE someTableCount SET cnt = cnt - 1 WHERE 1 = (SELECT 1 FROM (VALUES($1.*)) x(id, temp1, temp2, temp3) WHERE x.temp2 = ''5'')'
      USING OLD;
   END IF;
   RETURN new;
END
$func$ LANGUAGE plpgsql;

然后在触发器中循环查看条件并进行相应的更新。

答案 1 :(得分:2)

  

FirstTab-我使用以下条件打印上表的计数[col1如“ value1%”和col2如“ value2%”]

这将受益于“复合”索引:

INDEX(col1, col2)

因为它将被“覆盖”。 (也就是说,查询中需要的所有列都在一个索引中找到。)

SeocndTab-我使用以下条件[col3,例如“ value3%”]打印上表的计数

您显然已经具有最佳的(覆盖)索引:

INDEX(col3)

现在,让我们从不同的角度来看它。您是否注意到搜索引擎不再为您提供匹配的确切行数?您正在找出原因-无论使用哪种技术,进行统计都需要很长时间。

由于“ col1”无法告诉我您的应用程序,也不了解所计算的内容,因此我只能提出一些通用建议:

  • 不要计数。
  • 预先计算计数,将其保存在某个位置并提供“陈旧的”值。如果只计算几个不同的“值”,这将很方便。对于任意字符串,这可能不切实际。
  • 在输出中说“大约nnnn”。
  • 玩一些技巧来决定计算精确值还是说“约”是可行的。
  • 说“超过1000个”。

如果您想描述应用程序和专栏,也许我可以提供一些巧妙的技巧。

您对“插入速度”表示关注。这通常不是问题,并且为SELECTs设置'正确'索引的好处胜过INSERTs.

带来的轻微性能损失

答案 2 :(得分:1)

听起来好像您在需要螺丝刀时正在尝试使用锤子。如果您不想运行批处理计算,建议您使用流式框架(例如Flink或Samza)在添加或删除记录时从计数中进行加减。这些正是这些框架的基础。

如果致力于使用SQL,则可以设置一个作业,该作业在每个给定的时间窗口执行所需的计数操作,并将这些值存储到第二个表中。这样,您不必在相同的行上进行重复计数。

答案 3 :(得分:1)

作为优化的一般经验法则(是的,每个表需要一个SQL Server node @ 50mio条目一个!),这里列出了一些可能的优化技术,其中一些很容易实现,有些可能需要更认真的修改:

  • 优化MYSQL字段类型和大小,例如如果数据可以用数字表示,请使用INT代替VARCHAR,使用SMALL INT代替BIG INT,等等。如果您确实需要VARCHAR,则请使用每个字段尽可能小的长度,

  • 查看您的数据集; 是否有重复值?假设您的任何字段在50mio行中只有5个唯一值,然后将这些值保存到单独的表中,只需将PK链接到此样本表,

  • MYSQL分区,基本理解如this link所示,因此通常的想法是实施某种分区方案,例如CRONJOB每天在“晚上”,当服务器使用率达到最低时,或者当您到达另外50k INSERT时,就会创建一个新分区(顺便说一句,在不同分区上执行UPDATE / DELETE操作也需要付出额外的努力),

  • 缓存是另一种非常简单且有效的方法,因为在之上和之上都请求(几乎)相同的数据(我假设您的value1%,value2%,value3%始终相同?)再次。因此,请每隔一段时间执行一次SELECT COUNT(),然后使用差异索引计数来获取所选行的实际数量,

  • 内存数据库可以与传统的SQL DB一起使用以获取经常需要的数据:简单的键值对样式就足够了:Redis,Memcached,VoltDB,MemSQL只是他们中的几个。另外,MYSQL还知道in-memory engine

  • 如果您的数据集/系统可以使用不同的概念,请
  • 使用其他类型的数据库,例如像MongoDB这样的NoSQL数据库。

答案 4 :(得分:0)

在Postgres中,您可以从由查询计划者管理的内部统计信息中获取估算的行数:

SELECT reltuples AS approximate_row_count FROM pg_class WHERE relname = 'mytable';

这里有更多详细信息:https://wiki.postgresql.org/wiki/Count_estimate

您可以先创建实例化视图。像这样:

CREATE MATERIALIZED VIEW mytable AS SELECT * FROM the_table WHERE col1 like "value1%" and col2 like "value2%";`

您还可以直接实现计数查询。如果您有10个标签,则必须实现10个视图:

CREATE MATERIALIZED VIEW count_tab1 AS SELECT count(*) FROM the_table WHERE col1 like "value1%" and col2 like "value2%";`
CREATE MATERIALIZED VIEW count_tab2 AS SELECT count(*) FROM the_table WHERE col2 like "value2%" and col3 like "value3%";`
...

每次插入后,您应该(异步)刷新视图:

REFRESH MATERIALIZED VIEW count_tab1
REFRESH MATERIALIZED VIEW count_tab2
...

答案 5 :(得分:0)

如果您正在寻找聚合性能而又不太在意插入时间,则可以考虑将Column DBMS更改为Row DBMS。

列RDBMS将数据存储为列,这意味着每个列都独立于其他列进行索引。这样可以实现更快的聚合,我从Postgres切换到MonetDB(一个开放源代码列DBMS),并且对6百万行表中的一个字段求​​和,从约60s降为50ms。我之所以选择MonetDB,是因为它支持SQL查询和odbc连接,这对我的用例是一个加分,但是您将在其他Column DBMS上获得类似的性能改进。

列存储有一个缺点,即您在插入,更新和删除查询时会失去性能,但是从您所说的来看,我相信这不会对您造成太大影响。

答案 6 :(得分:0)

this will work:

select count(*) from (
select * from tablename where col1 like 'value1%' and col2 like 'value2%' and col3 
like'value3%')
where REGEXP_LIKE(col1,'^value1(.*)$') and REGEXP_LIKE(col2,'^value2(.*)$') and 
REGEXP_LIKE(col1,'^value2(.*)$');

try not to apply index on all the columns as it slows down the processing of a sql query and have it in required columns only.

答案 7 :(得分:0)

如评论中所述,您尚未发布尝试过的内容。因此,我认为问题的局限性就是您发布的内容。因此,请报告准确的结果

  1. 您当前花费在该问题子集上的时间是多少,即[col1如“ value1%”和col2如“ value2%”的计数)和第二[col3如“ value3%”的计数”
  2. 诀窍是扫描一次数据源,并通过创建索引来缩小数据源。因此,首先在col1,col2,col3,id上创建一个索引。 col3和id的目的是使数据库仅扫描索引。而且我会在相同的SQL中得到两个计数
select sum
       (
           case 
               when col1 like 'value1%' and col2 like 'value2%' then 1
               else 0
           end
       ) cnt_condition_1,
       sum
       (
           case 
               when col3 like 'value3%' then 1
               else 0
           end
       ) cnt_condition_2
from table
where (col1 like 'value1%' and col2 like 'value2%') or
      (col3 like 'value3%')
```
So the 50M row table is probably very wide right now.  This should trim it down - on a reasonable server I would expect above to return in a few seconds.  If it does not and each condition returns < 10% of the table, second option will be to create multiple indexes for each scenario and do count for each so that index is used in each case.

答案 8 :(得分:0)

如果系统中没有批量插入/批量更新,是否可以在表中尝试垂直分区?通过垂直分区,您可以将col1,col2的数据块与表的其他数据分开,从而减少搜索空间。

此外,在每列上建立索引似乎也不是最好的方法。在绝对需要的地方进行索引。在这种情况下,我会说Index(col1,col2)和Index(col3)。

即使在建立索引之后,您也需要查看这些索引的碎片并相应地对其进行修改以获得最佳结果。因为有时一列的5000万个索引可能会占据一大块,这将限制SQL Server的多处理能力。

答案 9 :(得分:0)

每个数据库在如何“增强” RDBMS方面都有其独特之处。我不能说MySQL或SQL Server,但对于PostgreSQL,您应该考虑将要搜索的索引作为基于GIN(通用反向索引)的索引。

CREATE INDEX name ON table USING gin(col1);
CREATE INDEX name ON table USING gin(col2);
CREATE INDEX name ON table USING gin(col3);

更多信息here

-HTH

相关问题