在 Sql 服务器中查找重复的唯一标识符的最快方法是什么?
What is the fastest way to look for duplicate uniqueidentifier in Sql Server?
我们对一个非常大的数据库中的每条记录使用 uniqueidentifier
。出于业务原因,我们需要确保 uniqueidentifier
永远不会被使用超过一次,但出于性能原因,我们将 bigint
作为 primary key
.
在 Sql 服务器 table 中测试 uniqueidentifer
是否存在的最快方法是什么?
**
< 0.05ms 在单个标准 S0 Sql Azure 实例上验证来自 100,000,000 行 的唯一标识符。
**
免责声明: 以下方法可能需要根据您的业务需求进行调整,目前仅 运行 在 Sql S0 实例(10 个 DTU)上的 Azure 暂存环境。目标是概念验证。
(请参阅下面的样板 CREATE To
脚本)
方法:
创建一个 table 专门用于存储数据库中存在的任何 uniqueidentifiers
。这个 table 将进行大量优化,以增强我们的 uniqueidentifer
验证器。
table 将有四列。一个用于 uniqueidentifier
,另外三个作为单个数字 binary
计算列,用于存储 uniqueidentifier
.
的三个数字中的每一个
然后我们将在前三位数字列上创建聚集索引,uniqueidentifer
作为聚集索引的最后一列。
我们最后将创建一个存储过程,它将采用 uniqueidentifer
并将前三位数字分解为 binary
数据类型并执行查找,利用 B 树结构根据我们的聚簇索引磁盘上的数据。
Table 创建到(带聚簇索引):
USE [MyDatabase]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ARITHABORT ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[UniqueIds](
[Guid] [uniqueidentifier] NOT NULL,
[Char01] AS (CONVERT([char](1),substring(CONVERT([char](36),[Guid]),(1),(1)))) PERSISTED NOT NULL,
[Char02] AS (CONVERT([char](1),substring(CONVERT([char](36),[Guid]),(2),(1)))) PERSISTED NOT NULL,
[Char03] AS (CONVERT([char](1),substring(CONVERT([char](36),[Guid]),(3),(1)))) PERSISTED NOT NULL,
CONSTRAINT [PK_UniqueIds] PRIMARY KEY CLUSTERED
(
[Char01] ASC,
[Char02] ASC,
[Char03] ASC,
[Guid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
GO
SET ANSI_PADDING OFF
GO
验证存储过程:
CREATE PROCEDURE [dbo].[UniqueIds.CountIds]
@Guid uniqueidentifier,
@IdCount bigint OUTPUT
AS
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Break out uniqueidentifier down into the first three characters for indexed lookup.
DECLARE @SubChar char(3) = CONVERT([char](36),@Guid);
DECLARE @Char01 char(1) = @SubChar;
DECLARE @Char02 char(1) = SUBSTRING(@SubChar,2,1);
DECLARE @Char03 char(1) = RIGHT(@SubChar,1);
-- Check if GUID already exists
(SELECT TOP 1 @IdCount=1 FROM UniqueIds WHERE Char01=@Char01 AND Char02=@Char02 AND Char03=@Char03 AND [Guid]=@Guid);
INSERTS 的存储过程:
CREATE PROCEDURE [dbo].[UniqueIds.Insert]
@Guid uniqueidentifier
AS
BEGIN TRY
BEGIN TRAN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Check if GUID already exists
DECLARE @IdCount bigint;
EXEC [UniqueIds.CountIds]
@Guid=@Guid,
@IdCount = @IdCount OUTPUT
IF @IdCount IS NULL
INSERT INTO UniqueIds
(
[Guid]
)
VALUES
(
@Guid
)
ELSE
THROW 60000, '[Guid] must be unique. Another unique identifier with the same signature exists.', 1;
COMMIT
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK;
THROW;
END CATCH
插入新的 uniqueidentifier
(示例):
DECLARE @id uniqueidentifier
SET @id=NEWID()
EXEC [UniqueIds.Insert]
@Guid=@id
关于实施的最终决定:
每次在数据库中的任何位置插入新的 uniqueidentifier
时,只需将其包装在回滚事务中并调用用于插入 uniqueidentifer
的存储过程。这将调用我们的验证存储过程,如果它失败,它会抛出一个错误。您的回滚将确保不会保留任何其他内容。
示例:
CREATE PROCEDURE [dbo].[Bases.Insert]
@Guid uniqueidentifier,
@AccountId bigint=0,
@ModifierId bigint=0,
@ScopeIdentity bigint OUTPUT
AS
**BEGIN TRY
BEGIN TRAN**
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
**EXEC [dbo].[UniqueIds.Insert]
@Guid = @Guid;**
-- Insert the base row
INSERT INTO Bases
(
[Guid],
[State],
Utc,
AccountId,
ModifierId
)
VALUES
(
@Guid,
0,
GETUTCDATE(),
@AccountId,
@ModifierId
);
**COMMIT;**
SELECT @ScopeIdentity = CAST(SCOPE_IDENTITY() As bigint);
**END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK;
THROW;
END CATCH**
**
Result: validating the uniqueness of a uniqueidentifier from 100
MILLION rows consistently takes < 0.05ms
**
对我来说,我将模型 属性 从我的核心项目更改为 ICollection<>,然后我添加了迁移和更新数据库,这解决了我的问题
我们对一个非常大的数据库中的每条记录使用 uniqueidentifier
。出于业务原因,我们需要确保 uniqueidentifier
永远不会被使用超过一次,但出于性能原因,我们将 bigint
作为 primary key
.
在 Sql 服务器 table 中测试 uniqueidentifer
是否存在的最快方法是什么?
**
< 0.05ms 在单个标准 S0 Sql Azure 实例上验证来自 100,000,000 行 的唯一标识符。
**
免责声明: 以下方法可能需要根据您的业务需求进行调整,目前仅 运行 在 Sql S0 实例(10 个 DTU)上的 Azure 暂存环境。目标是概念验证。
(请参阅下面的样板 CREATE To
脚本)
方法:
创建一个 table 专门用于存储数据库中存在的任何 uniqueidentifiers
。这个 table 将进行大量优化,以增强我们的 uniqueidentifer
验证器。
table 将有四列。一个用于 uniqueidentifier
,另外三个作为单个数字 binary
计算列,用于存储 uniqueidentifier
.
然后我们将在前三位数字列上创建聚集索引,uniqueidentifer
作为聚集索引的最后一列。
我们最后将创建一个存储过程,它将采用 uniqueidentifer
并将前三位数字分解为 binary
数据类型并执行查找,利用 B 树结构根据我们的聚簇索引磁盘上的数据。
Table 创建到(带聚簇索引):
USE [MyDatabase]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ARITHABORT ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[UniqueIds](
[Guid] [uniqueidentifier] NOT NULL,
[Char01] AS (CONVERT([char](1),substring(CONVERT([char](36),[Guid]),(1),(1)))) PERSISTED NOT NULL,
[Char02] AS (CONVERT([char](1),substring(CONVERT([char](36),[Guid]),(2),(1)))) PERSISTED NOT NULL,
[Char03] AS (CONVERT([char](1),substring(CONVERT([char](36),[Guid]),(3),(1)))) PERSISTED NOT NULL,
CONSTRAINT [PK_UniqueIds] PRIMARY KEY CLUSTERED
(
[Char01] ASC,
[Char02] ASC,
[Char03] ASC,
[Guid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
GO
SET ANSI_PADDING OFF
GO
验证存储过程:
CREATE PROCEDURE [dbo].[UniqueIds.CountIds]
@Guid uniqueidentifier,
@IdCount bigint OUTPUT
AS
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Break out uniqueidentifier down into the first three characters for indexed lookup.
DECLARE @SubChar char(3) = CONVERT([char](36),@Guid);
DECLARE @Char01 char(1) = @SubChar;
DECLARE @Char02 char(1) = SUBSTRING(@SubChar,2,1);
DECLARE @Char03 char(1) = RIGHT(@SubChar,1);
-- Check if GUID already exists
(SELECT TOP 1 @IdCount=1 FROM UniqueIds WHERE Char01=@Char01 AND Char02=@Char02 AND Char03=@Char03 AND [Guid]=@Guid);
INSERTS 的存储过程:
CREATE PROCEDURE [dbo].[UniqueIds.Insert]
@Guid uniqueidentifier
AS
BEGIN TRY
BEGIN TRAN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Check if GUID already exists
DECLARE @IdCount bigint;
EXEC [UniqueIds.CountIds]
@Guid=@Guid,
@IdCount = @IdCount OUTPUT
IF @IdCount IS NULL
INSERT INTO UniqueIds
(
[Guid]
)
VALUES
(
@Guid
)
ELSE
THROW 60000, '[Guid] must be unique. Another unique identifier with the same signature exists.', 1;
COMMIT
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK;
THROW;
END CATCH
插入新的 uniqueidentifier
(示例):
DECLARE @id uniqueidentifier
SET @id=NEWID()
EXEC [UniqueIds.Insert]
@Guid=@id
关于实施的最终决定:
每次在数据库中的任何位置插入新的 uniqueidentifier
时,只需将其包装在回滚事务中并调用用于插入 uniqueidentifer
的存储过程。这将调用我们的验证存储过程,如果它失败,它会抛出一个错误。您的回滚将确保不会保留任何其他内容。
示例:
CREATE PROCEDURE [dbo].[Bases.Insert]
@Guid uniqueidentifier,
@AccountId bigint=0,
@ModifierId bigint=0,
@ScopeIdentity bigint OUTPUT
AS
**BEGIN TRY
BEGIN TRAN**
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
**EXEC [dbo].[UniqueIds.Insert]
@Guid = @Guid;**
-- Insert the base row
INSERT INTO Bases
(
[Guid],
[State],
Utc,
AccountId,
ModifierId
)
VALUES
(
@Guid,
0,
GETUTCDATE(),
@AccountId,
@ModifierId
);
**COMMIT;**
SELECT @ScopeIdentity = CAST(SCOPE_IDENTITY() As bigint);
**END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK;
THROW;
END CATCH**
**
Result: validating the uniqueness of a uniqueidentifier from 100 MILLION rows consistently takes < 0.05ms
**
对我来说,我将模型 属性 从我的核心项目更改为 ICollection<>,然后我添加了迁移和更新数据库,这解决了我的问题