Snow Flake 是 Twitter 开源的分布式 ID 生成算法,结果是一个 long 型的 ID。其核心思想是:
如下图所示:
Snow Flake ID 算法的优点是:
关于 Snow Flake ID 算法, 网上已经有太多的介绍, 因此不再做过多的描述。
关于 Snow Flake ID 算法的实现, 已经有多种语言版本的实现, 这里以 PostgreSQL 为例, 使用 sql 实现个简化版。
先创建一个序列, 生成毫秒内的流水号, sql 语句如下:
CREATE SEQUENCE public.snow_flake_id_seq;
ALTER SEQUENCE public.snow_flake_id_seq
OWNER TO postgres;
再创建一个函数, 实现 Snow Flake ID 算法, sql 语句如下:
CREATE OR REPLACE FUNCTION public.snow_flake_id()
RETURNS bigint
LANGUAGE 'sql'
COST 100
VOLATILE
AS $BODY$
SELECT (EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) * 1000)::bigint * 1000000 -- 将时间戳(精确到毫秒)放在最高位, 便于排序
+ 5 * 10000 -- 数据库实例 id , 可以根据数据库进行修改
+ nextval('public.snow_flake_id_seq') % 1000 -- 毫秒内的序列号, 求 1000 的余数, 保证在 0 ~ 999 的范围内
as snow_flake_id
$BODY$;
ALTER FUNCTION public.snow_flake_id()
OWNER TO postgres;
COMMENT ON FUNCTION public.snow_flake_id()
IS 'snow flake id ';
测试生成的 ID , 执行 sql 语句 select public.snow_flake_id()
可以得到下面的结果:
1534042025838050074
说明如下:
因此, 这个结果基本上符合 Snow Flake ID 算法。
在数据库使用这个 ID 也很容容易, 只要设置数据表的对应的列的默认值即可, 示例如下:
CREATE TABLE public.snow_flake_test
(
id bigint NOT NULL DEFAULT public.snow_flake_id(), -- 设置 ID 列的默认值, 自动生成 Snow Flake ID
name character varying(32) COLLATE pg_catalog."default",
CONSTRAINT snow_flake_test_pkey PRIMARY KEY (id)
)
现在执行插入语句
insert into public.snow_flake_test (name)
values ('snow flake id test')
数据库的到的结果为
id
bigint
|
name
character varying(32)
|
---|---|
1534042851390050075 | snow flake id test |
为了能够在 NHibernate 中使用, 需要根据上面的 snow_flake_test
表创建一个实体类, 代码如下:
public class SnowFlakeTestEntity {
public virtual long Id { get; set; }
public virtual string Name { get; set; }
}
Id 是在数据库生成的, 所以应该使用的生成器为 trigger-identity
, 对应的 xml 映射文件为如下:
<class name="SnowFlakeTestEntity" table="snow_flake_test" schema="public">
<id name="Id" column="id" type="long">
<generator class="trigger-identity">
</generator>
</id>
<property name="Name" column="name" type="string" length="32" />
</class>
最后, 写一个单元测试来验证一下配置, 代码如下:
[Fact]
public void _02_CanInsertSnowFlakeId() {
using (var session = factory.OpenSession()) {
var entity = new SnowFlakeTestEntity {
Name = "snow flake id test"
};
session.Save(entity);
Assert.True(entity.Id > 0);
Console.WriteLine($"Id: {entity.Id}");
}
}
运行测试代码, 可以得到如下的输出:
Starting test execution, please wait...[xUnit.net 00:00:01.19]
NHibernate:
INSERT
INTO
public.snow_flake_test
(name)
VALUES
(:p0) returning id;
:p0 = 'd0f7198ea8ab4faa982a16df7cc8e96b' [Type: String (0:0:0)], :nhIdOutParam = NULL [Type: Int64 (0:0:0)]
Id: 1534043843651050079
Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.
Test Run Successful.
Test execution time: 4.5339 Seconds
毫无悬念, 单元测试通过, 可以在 NHibernate 中愉快的使用 Snow Flake ID 了。