提高 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] 的索引碎片并定期重建索引。
我在最新的 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] 的索引碎片并定期重建索引。