改进GUID作为主键时的问题

改进GUID作为主键时的问题

在接手某个项目的时候, 该项目数据库使用了GUID(UniqueIdentifier)作为大部分表的主键,同时也默认使用了聚集索引。 当数据量增加的时候, 发现会产生大量的磁盘碎片。 因此运维还专门写了个job任务来整理碎片。
这主要是由于GUID的不连续造成的。 当新的数据行插入时,GUID可能会造成大量数据的移动, 因此影响了性能。
为什么使用GUID作为主键?主要是从后台来讲, 在插入数据前,可以生成一个唯一的Id,这个还是有很多实用场景的。

所以需要对该问题进行改进。
我们在项目中使用的是Timestamp Based Guid, 或者简称为TBGuid。其实是一个字符串,长度40: 前8位是基于Unix Time的相对时间秒,转16进制后的文本;后32位是GUID转文本后去除“-”连接号。也就是时间+Guid的格式。
利用时间的连续性加Guid的唯一性, 使其即具有连续性,不至于在新增数据的时候对数据连续存储产生影响;又具有唯一性, 满足主键要求。

下面是C#中的实现方法:

/// <summary>
        /// 返回唯一的40位长度的Id (Hex(UnixTime)+Guid)
        /// </summary>
        /// <returns></returns>
        public static string NewId()
        {
            string result = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString("x", CultureInfo.CurrentUICulture.NumberFormat)
                        + Guid.NewGuid().ToString("N", CultureInfo.CurrentUICulture.NumberFormat);
            return result;
        }

下面是SQL中的实现方法:

/*
 * Author        : Harvey Hu
 * Created On    : 2021-04-02
 * Description   :
        返回一个40位长度的字符串Id: 前8位数为UTC时间相对值, 后32位为GUID
 * Sample
        call        [dbo].[fn_NewID40](NEWID()) ,
        return      '6066AFB6020C76DC0AC0445F8E882E3801D0F239'
*/
CREATE FUNCTION [dbo].[fn_NewID40] (@uid uniqueidentifier)
RETURNS nvarchar(40)
AS
  BEGIN
      -- 注意UTC时区, 目前是东8, 所以
      RETURN FORMAT(Datediff(s, '1970-1-1', Dateadd(hour, -8, Getdate())), 'X')
             + Replace( Cast(@uid AS nvarchar(36)), '-', '')
  END

原文链接:https://www.cnblogs.com/huwz/p/14982119.html

发表评论