对包含 2 亿行的 SQL 表进行性能查询?

Posted

技术标签:

【中文标题】对包含 2 亿行的 SQL 表进行性能查询?【英文标题】:Performance Queries on a SQL table that has 200 million rows in it? 【发布时间】:2013-12-13 12:02:21 【问题描述】:

我有一个有 2 亿行的表,它每天会增加 150 万行。

我需要查询它以获取一段时间内的数据,比如 3 个月的数据,这需要至少半小时的时间来检索它。

有没有什么方法可以在不到一分钟或两分钟内对表或查询进行性能微调以更快地执行和获取数据。

CREATE TABLE [dbo].[Chnl](
    [Id] int IDENTITY(1,1) NOT NULL 
        CONSTRAINT [PK_Chnl] PRIMARY KEY CLUSTERED
    ,[ChnlNo] int NOT NULL
    ,[ChnlName] varchar(50) NULL
    ,[Active] bit NULL
)

CREATE TABLE [dbo].[ChnlData]( 
    [Id] [int] IDENTITY(1,1) NOT NULL
        CONSTRAINT [PK_ChnlData] PRIMARY KEY CLUSTERED
    ,[ChnlId] [int] NOT NULL
    ,[ChnlValue] [decimal](6, 2) NOT NULL
    ,[ChnlDataLogTime] [datetime] NOT NULL
    ,[Comments] [varchar](max) NULL
    ,[Active] [bit] NULL
    ,CONSTRAINT [FK_ChannelData_Channel] FOREIGN KEY([ChnlId]) REFERENCES [dbo].[Chnl] ([Id]) 
)

这只是一个简单的查询:

SELECT * 
FROM [ChnlData]
WHERE  ChnlId in (519, 520)

它获取了 700 万条记录,并且花了 9 分钟才完成。目前数据库大小为 32 GB

【问题讨论】:

请添加有关表结构和示例数据的详细信息。您尝试过什么来提高性能?什么是“千万”和“十万”? “微调表或查询”。什么表定义?什么查询? 调整很大程度上取决于您的数据结构。尝试发布更具体的问题。一个简短的回答是:可能是的,但你需要付出大量的工作和学习努力。尝试微调索引、重新设计表、视图、重写 SP、调整每个查询的隔离级别等。 很抱歉,表中每天将插入150万条记录,而表中已经有2000万条记录。表结构是, @Rama 150 万 * 30 天 * 3 个月 = 1.35 亿行。如果我们假设平均行大小约为 40 字节并且对一张表进行简单查询,则意味着服务器需要处理大约 5.5 GB。你的硬件是什么?如果没有发布查询,这个问题仍然毫无意义 【参考方案1】:

对表进行分区(基于年或月)将是可能的解决方案之一。您可能需要为动态分区创建脚本。

除了以前的方法之外,您还可以实施 DataWarehousing 风格的解决方案。 就像您可以为每条记录创建一个代理键(唯一键 - 可能是一个序列),并准备一个类似结构的查找表。

例如:密钥 1234M - 1235M 的密钥集将在 XX 分区中......等等。

这可能不容易实现。但这是一个干净的解决方案。

对于 OLTP 环境,只有 patition table 会有很大帮助。

为 tis 数据分配一个单独的数据库。并且使用并行查询(使用多个节点处理器),我们可以加快查询输出。

【讨论】:

【参考方案2】:

首先,我将创建一个索引来涵盖您的搜索参数,至少这应该涵盖包含您的日期的列。如果这还不够,您可能需要研究 Maheswaran 的建议并使用分区和文件组,这对索引特别适用,因为它们可以分别覆盖每个分区。

但总而言之,这真的很难说,因为你的问题太宽泛了。从表中提取了多少列和哪些类型的数据,表中的列总数是多少? WHERE 子句中的过滤器是什么(您的索引将使用这些过滤器)。您的 3 个月批次将包含多少大小的数据(每 3 个月创建文件组是可行的,从而更容易存档和对所述数据使用批量操作)。等等。

现在有太多的猜测要做。

编辑:由于数字比原来的要少得多,所以现在一个简单的索引就足够了。试试这个:

CREATE NONCLUSTERED INDEX CHLNDATA_QUARTER_IDX ON ChnlData (ChnlId, ChnlDataLogTime)

然后如果你想要过去三个月的数据,你可以像这样得到它:

SELECT * 
FROM [ChnlData]
WHERE  ChnlId in (519, 520)
AND YEAR(ChnlDataLogTime) IN (YEAR(DATEADD(MONTH, -3, GETDATE())), YEAR(GETDATE()))
AND MONTH(ChnlDataLogTime) BETWEEN MONTH(DATEADD(MONTH, -3, GETDATE())) AND MONTH(GETDATE())

没有检查语法,但应该是正确的或足够接近。

【讨论】:

在函数中包装ChnlDataLogTime 是不可分割或最优的。

以上是关于对包含 2 亿行的 SQL 表进行性能查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 AWS 快速对 100 亿行 SQL 表进行分区?

具有超过十亿行的表的 Postgres 性能

mysql在具有1亿行的表上创建索引

插入select查询的MySql太慢,无法复制1亿行

无法理解数十亿行的更新计数 INSERT

SQL 查询性能对 MySQL 来说太差了