数据存储主键类的选择-雪花漂移算法

数据存储主键类的选择-雪花漂移算法

常用主键类型:

  • 整型:int,long。优点:可以自增,占用空间小,存取速度快。缺点:难于扩展,需要合并、分表、分库或者数据迁移会相当痛苦。不大适合分布式存储。
  • 字符串。性能差不推荐使用。
  • GUID/UUID:优点:全局唯一,合并、分表、分库,迁移相当方便。缺点:无序,存储存储空间大,性能差,每次新增数据会导致索引重新排序。

从上可以看出整型和GUID/UUID的优缺点正好相反。

想要一种同时具有整型和GUID/UUID的优点的主键,可以参考Twitter公开的雪花算法(Snowflake),Twitter原版https://github.com/twitter-archive/snowflake,网上有很多国内写的实现,如:https://github.com/cloudyan/snowflake。生成一个 64 bit 的整数,保证不同进程主键的不重复性以及相同进程主键的有序性。

优点:生成的 ID 都是趋势递增的;不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成 ID 的性能也是非常高的;根据自身业务特性分配 bit 位,非常灵活

缺点:强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。

个人更喜欢用国人写的雪花漂移https://github.com/yitter/IdGenerator,优点巴拉巴拉介绍了一大堆,主要用起来简单。

在.Net项目中用法如下:

1、NuGet包管理器中添加IdGenerator包引用

2、在程序初始化时配置工作站编号。

打开项目的Program.cs文件,添加以下代码。

// 配置雪花漂移算法
var options = new IdGeneratorOptions(1);
YitIdHelper.SetIdGenerator(options);`

其中“IdGeneratorOptions(1)”中的数字“1”是机器码,不同的系统使用不同的机器码避免生成的id重复,默认最大值为63,可以配置64个系统。通过配置WorkerIdBitLength参数增加机器码长度。

3、在模型类中生成id。

新建模型类:Entity

类中的Id属性添加Key, DatabaseGenerated(DatabaseGeneratedOption.None)标注,表示Id为主键且生成行为不进行任何处理,也就不自增。并在Entity类的构造函数中使用YitIdHelper.NextId()生成id。代码如下

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Yitter.IdGenerator;

namespace Ledger.Entity
{
    /// <summary>
    /// 保存到数据库的根实体。
    /// </summary>
    public class Entity
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="createNewId">创建新ID</param>
        public Entity(bool createNewId)
        {
            if(createNewId) Id = YitIdHelper.NextId();
        }
        public Entity(long id)
        {
            Id = id;
        }

        [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
        public long Id {  get; set; }

        /// <summary>
        /// 为Id创建一个新值
        /// </summary>
        public void CreateNewId()
        {
            Id = YitIdHelper.NextId();
        }
    }
}

为避免每次实例化时都自动创建Id影响性能,这里有两个构造函数:

  • public Entity(bool createNewId),明确传入createNewId=true时才创建id。主要用于实例化一个需要保存到数据库的新对象时采用。

  • public Entity(long id),传入一个Id值,用在非创建新对象时用。

另外还写了一个public void CreateNewId()方法,在需要时为类生成新id。

为了使用方面Entity可以做基类,其他模型类继承自这个类。

如:

using System.ComponentModel.DataAnnotations;

namespace Ledger.Entity
{
    /// <summary>
    /// 商户
    /// </summary>
    public class Shop : Entity
    {
        /// <summary>
        /// 创建新商户
        /// </summary>
        /// <param name="bookId">账本Id</param>
        /// <param name="name">名称</param>
        /// <param name="createNewId">是否创建新id</param>
        public Shop(long bookId, string name,bool createNewId = true) : base(createNewId)
        {
            BookId = bookId;
            Name = name;
        }
        /// <summary>
        /// 设置已有商户
        /// </summary>
        /// <param name="id">商户Id</param>
        /// <param name="bookId">账本Id</param>
        /// <param name="name">名称</param>
        public Shop(long id,long bookId, string name) : base(id)
        {
            BookId = bookId;
            Name = name;
        }

        /// <summary>
        /// 图标
        /// </summary>
        public string? Icon {  get; set; }
        /// <summary>
        /// 名称
        /// </summary>
        [StringLength(20)]
        public string Name { get; set; }
    }
}

其他没有什么不同。

posted @ 2023-11-03 16:36  洞庭夕照  阅读(123)  评论(2编辑  收藏  举报