提高 SQL 服务器对大表 MsSQL 与 MySQL 的查询性能

Improve SQL Server query performance on large tables MsSQL vs MySQL

我在最新的 MsSQL 上有生产系统 运行,但是当有很多行 (10M+) 时,我对简单 select 语句的性能非常差。

MsSQL 中没有哈希索引这样的东西(据我所知)所以在我的问题中我比较了 MySQL 社区数据库和 MSSQL 2017 运行 在同一台机器上,除了 MySQL 在 windows ubuntu 虚拟机下运行。

在示例脚本中,我创建了 1 亿行并执行了它。在 MS SQL 中,性能非常糟糕,简单的查询花费了将近 2 秒。在 MySQL 中,查询耗时 0.0011 秒。我认为 MS SQL 应该比免费 MySQL 更好,所以我希望我错过了一些索引。

有人可以告诉我如何使用 MsSQL 创建更高效​​的索引吗?

测试tableMSSQL:

drop table if exists [dbo].[TEST_TRADE];


CREATE TABLE [dbo].[TEST_TRADE](
    [ID] [bigint] IDENTITY(1,1) NOT NULL,
    [TradeID] [varchar](250) NOT NULL,
    [ValidFrom] [datetimeoffset](7) NULL,
    [ValidUntil] [datetimeoffset](7) NULL,
)
GO  

CREATE CLUSTERED INDEX IX_Primary   
    ON [dbo].[TEST_TRADE] ([ID]);   
GO  

CREATE NONCLUSTERED INDEX IX_TradeID   
    ON [dbo].[TEST_TRADE] ([TradeID]);   
GO

CREATE NONCLUSTERED INDEX IX_ValidUntil   
    ON [dbo].[TEST_TRADE] ([ValidUntil]);   
GO

Declare @Id int
Set @Id = 1

While @Id <= 100000000
Begin 
   Insert Into [dbo].[TEST_TRADE] ([TradeID]) values (
   'ID'+CAST(@Id as nvarchar(10))),(
   'ID'+CAST(@Id+1 as nvarchar(10))),(
   'ID'+CAST(@Id+2 as nvarchar(10))),(
   'ID'+CAST(@Id+3 as nvarchar(10))),(
   'ID'+CAST(@Id+4 as nvarchar(10))),(
   'ID'+CAST(@Id+5 as nvarchar(10))),(
   'ID'+CAST(@Id+6 as nvarchar(10))),(
   'ID'+CAST(@Id+7 as nvarchar(10))),(
   'ID'+CAST(@Id+8 as nvarchar(10))),(
   'ID'+CAST(@Id+9 as nvarchar(10))),(
   'ID'+CAST(@Id+10 as nvarchar(10))),(
   'ID'+CAST(@Id+11 as nvarchar(10))),(
   'ID'+CAST(@Id+12 as nvarchar(10))),(
   'ID'+CAST(@Id+13 as nvarchar(10))),(
   'ID'+CAST(@Id+14 as nvarchar(10))),(
   'ID'+CAST(@Id+15 as nvarchar(10))),(
   'ID'+CAST(@Id+16 as nvarchar(10))),(
   'ID'+CAST(@Id+17 as nvarchar(10))),(
   'ID'+CAST(@Id+18 as nvarchar(10))),(
   'ID'+CAST(@Id+19 as nvarchar(10))
   )
   Print @Id
   Set @Id = @Id + 20
End

MySQL 测试 table:

drop table if exists `Test`.`TEST_TRADE`;
CREATE TABLE `Test`.`TEST_TRADE` ( `ID` BIGINT NOT NULL AUTO_INCREMENT, `TradeID` VARCHAR(250) NULL default null, `ValidFrom` TIMESTAMP NULL default null, `ValidUntil` TIMESTAMP NULL default null, PRIMARY KEY (`ID`), INDEX (`TradeID`) USING HASH, INDEX (`ValidUntil`)) ENGINE = InnoDB;

drop PROCEDURE if EXISTS InsertRand;
DELIMITER $$
CREATE PROCEDURE InsertRand(IN NumRows INT)
    BEGIN
        DECLARE i INT;
        SET i = 1;
        START TRANSACTION;
        WHILE i <= NumRows DO
            INSERT INTO TEST_TRADE(TradeID) VALUES 
            (concat("ID",i)),
            (concat("ID",i+1)),
            (concat("ID",i+2)),
            (concat("ID",i+3)),
            (concat("ID",i+4)),
            (concat("ID",i+5)),
            (concat("ID",i+6)),
            (concat("ID",i+7)),
            (concat("ID",i+8)),
            (concat("ID",i+9)),
            (concat("ID",i+10)),
            (concat("ID",i+11)),
            (concat("ID",i+12)),
            (concat("ID",i+13)),
            (concat("ID",i+14)),
            (concat("ID",i+15)),
            (concat("ID",i+16)),
            (concat("ID",i+17)),
            (concat("ID",i+18)),
            (concat("ID",i+19))
            ;
            SET i = i + 20;
        END WHILE;
        COMMIT;
    END$$
DELIMITER ;

CALL InsertRand(100000000);

如果您选择 10m + 行,则 SQL 服务器 table 每行的数据大小为 42 字节。
(bigint = 8 字节) + (12 个字符的 varchar = 14 字节) + (datetimeoffset = 10 字节)*2 所以 1000 万行应该是 420 000 000 字节,大约是 400MB。

所以2秒读取400MB的数据就是400MB/2s = 200MB/s,这是访问硬盘的合理速度。 但是 400MB/0.0011s 是 363 636,36MB/s,这远远超过任何硬盘驱动器的速度并且与 RAM 访问速度非常相似。

因此 MySQL 中的 table 必须完全缓存在内存中,这就是为什么您的查询可以在 0.0011 秒内完成。

您需要找到一种方法将 MsSQL table 也完全缓存在内存中,以达到类似的速度。

已编辑: 如果您的查询是

Select * FROM [dbo].[TEST_TRADE]
where [TradeId] = 'ID99999999'

首先在列 [TradeId] 上创建聚簇索引,然后在 [ID] 上创建 PK,如果可能,使用固定长度的 char(12) 代替 varchar 的 [TradeId]。

已编辑:

经测试,在列 [TradeId] 上创建聚簇索引将使查询速度提高 50%。

我建议先检查 [IX_TradeID] 的索引碎片并定期重建索引。