-- -- 雪花算法 ID 生成器Mahjong Snowflake-- 支持动态传入类型 ID服务器 ID 在初始化时固定。-- 默认布局控制在 53 位以内避免旧 Lua double-number 运行时丢失低位精度。-- 默认纪元2026-01-01 00:00:00 UTC---- 当同一秒内序列号耗尽时借用未来时间戳继续生成避免死循环-- localtostring(mahjong_dumpandmahjong_dump.dump_null)ortostringlocaltoString(mahjong_dumpandmahjong_dump.dump_null)ortoString-- 局部化系统库函数提升性能避免全局查找localos_timeos.time-- 获取系统时间秒localmath_floormath.floor-- 取整函数localmath_maxmath.max-- 最大值函数用于位数裁剪localLogRichDebugMJLOGTlocalLogRichWarningMJLOGWlocalLogRichErrMJLOGE-- 定义雪花算法模块全局可见-- 定义雪花算法模块全局可见热更新时直接替换重建禁 or 留旧表mahjong_snowflake{}-- -- 默认配置常量-- -- 默认配置常量注意在 Lua 中直接使用数字字面量而不是 local 变量-- 遵循 C 宏全大写下划线命名风格全局可见。SNOWFLAKE_DEFAULT_EPOCH1767225600-- 默认纪元秒数2026-01-01 00:00:00 UTCSNOWFLAKE_DEFAULT_TIMESTAMP_BITS28-- 时间戳占用的位数从2026年起约可覆盖8.5年SNOWFLAKE_DEFAULT_SERVER_ID_BITS7-- 服务器ID占用的位数最大128个服务器SNOWFLAKE_DEFAULT_TYPE_BITS6-- 类型ID占用的位数最大64种类型SNOWFLAKE_DEFAULT_SEQUENCE_BITS12-- 序列号占用的位数每秒每类型最多4096个ID-- 进程内兜底持久化key: server_id:type_idg_tMahjongSnowflakeLastSecStoreg_tMahjongSnowflakeLastSecStoreor{}-- -- 根据位数计算最大值-- --- 根据给定的位数计算可表示的最大值--- param bits integer 位数--- return integer 最大值2^bits - 1localfunctionmax_value(bits)return(2^bits)-1end-- -- 创建雪花 ID 生成器实例-- --- 创建雪花 ID 生成器实例--- param server_id integer 服务器ID必须唯一取值范围根据 server_id_bits 决定--- param opts table|nil 可选配置--- field epoch integer 纪元秒数默认为2026-01-01 00:00:00--- field timestamp_bits integer 时间戳位数--- field server_id_bits integer 服务器ID位数--- field type_bits integer 类型ID位数--- field sequence_bits integer 序列号位数--- field get_time function 获取当前秒数的函数默认为 os.time--- return table 包含 next_id 方法的对象functionmahjong_snowflake.new(server_id,opts)optsoptsor{}localepochopts.epochorSNOWFLAKE_DEFAULT_EPOCHlocalts_bitsopts.timestamp_bitsorSNOWFLAKE_DEFAULT_TIMESTAMP_BITSlocalsid_bitsopts.server_id_bitsorSNOWFLAKE_DEFAULT_SERVER_ID_BITSlocaltype_bitsopts.type_bitsorSNOWFLAKE_DEFAULT_TYPE_BITSlocalseq_bitsopts.sequence_bitsorSNOWFLAKE_DEFAULT_SEQUENCE_BITS-- 检查总位数是否超过53位保证旧 Lua double-number 运行时也能精确表示每个整数ID。ifts_bitssid_bitstype_bitsseq_bits53then-- 修复降级为警告裁剪而非直接崩溃防止服务因配置错误被中断LogRichWarning(string.format(total bits exceed 53 (got %d), clamping to 53 to preserve integer precision,ts_bitssid_bitstype_bitsseq_bits))localnOverflowts_bitssid_bitstype_bitsseq_bits-53seq_bitsmath_max(1,seq_bits-nOverflow)nOverflowts_bitssid_bitstype_bitsseq_bits-53ifnOverflow0thentype_bitsmath_max(1,type_bits-nOverflow)nOverflowts_bitssid_bitstype_bitsseq_bits-53endifnOverflow0thensid_bitsmath_max(1,sid_bits-nOverflow)endend-- 检查服务器ID是否超出范围localmax_server_idmax_value(sid_bits)ifserver_id0orserver_idmax_server_idthenerror(string.format(server_id out of range [0, %d],max_server_id))endlocalmax_type_idmax_value(type_bits)-- 类型ID最大值localmax_sequencemax_value(seq_bits)-- 序列号最大值-- 移位量左移位数用于组合各部分localseq_shift0-- 序列号放在最低位无需移位localtype_shiftseq_bits-- 类型ID左移 seq_bits 位localsid_shiftseq_bitstype_bits-- 服务器ID左移 (seq_bits type_bits) 位localts_shiftseq_bitstype_bitssid_bits-- 时间戳左移 (seq_bits type_bits sid_bits) 位localtype_multiplier2^type_shift-- 类型ID段乘数替代左移语法localsid_multiplier2^sid_shift-- 服务器ID段乘数替代左移语法localts_multiplier2^ts_shift-- 时间戳段乘数替代左移语法-- 获取当前时间的函数可自定义用于测试localget_timeopts.get_timeoros_timelocalfnLoadLastSecopts.load_last_seclocalfnSaveLastSecopts.save_last_sec-- 存储每个类型的私有状态用于序列号-- state 结构{ last_sec 上次时间戳秒, sequence 当前序列号 }localstates{}localfunctionget_store_key(type_id)returnstring.format(%d:%d,server_id,type_id)end--- 加载上次记录的秒数和序列号从内存磁盘恢复--- param type_id integer 类型ID--- return integer nSec, integer nSeq 上次记录的秒数和恢复的序列号localfunctionload_last_sec(type_id)localnDiskniliffnLoadLastSecthenlocalbOk,nValpcall(fnLoadLastSec,server_id,type_id)ifbOkandtype(nVal)numberthennDiskmath_floor(nVal)endendlocaltMemg_tMahjongSnowflakeLastSecStore[get_store_key(type_id)]localnMem,nMemSeqnil,0iftype(tMem)tablethennMemtype(tMem.sec)numberandmath_floor(tMem.sec)ornilnMemSeqtype(tMem.seq)numberandmath_floor(tMem.seq)or0elseiftype(tMem)numberthennMemmath_floor(tMem)nMemSeq0endifnDiskandnMemthenlocalnSecnDisknMemandnDiskornMemreturnnSec,(nSecnMemandnMemSeqor0)endreturn(nDiskornMemor0),(nDiskand0ornMemSeq)endlocalfunctionpersist_last_sec(type_id,nSec,nSeq)localnSecIntmath_floor(nSec)localnSeqIntmath_floor(nSeqor0)g_tMahjongSnowflakeLastSecStore[get_store_key(type_id)]{secnSecInt,seqnSeqInt}iffnSaveLastSecthenlocalbOk,errpcall(fnSaveLastSec,server_id,type_id,nSecInt)ifnotbOkthenLogRichErr(string.format(Snowflake: persist_last_sec failed for server %d type %d: %s,server_id,type_id,tostring(err)))endendend--- 生成下一个 ID--- param type_id integer 类型ID如匹配类型范围 0 ~ max_type_id--- return integer 64位整数ID实际使用53位有效位适配 Lua double-number 精度范围localfunctionnext_id(type_id)-- 检查类型ID是否有效iftype_id0ortype_idmax_type_idthenerror(string.format(type_id out of range [0, %d],max_type_id))end-- 获取该类型的状态localstatestates[type_id]ifnotstatethenlocalnRecovered,nRecoveredSeqload_last_sec(type_id)localnNowget_time()ifnRecoverednNowthennRecoverednNow nRecoveredSeq0elseifnRecoveredSeq0then-- 崩溃重启在同一秒内且序列号已丢失推进1秒避免ID碰撞nRecoverednRecovered1end-- 修复崩溃重启后 nRecoveredSeq 为 nil 的问题确保序列号有初始值state{last_secnRecovered,sequence(type(nRecoveredSeq)numberandnRecoveredSeqor0)}states[type_id]stateendlocalcur_secget_time()-- 当前秒数相对于系统时间ifcur_secstate.last_secthen-- 时钟回拨使用上次的时间戳并递增序列号cur_secstate.last_sec state.sequencestate.sequence1ifstate.sequencemax_sequencethen-- 序列号耗尽进入下一秒cur_seccur_sec1state.sequence0state.last_seccur_secpersist_last_sec(type_id,state.last_sec,state.sequence)else-- 序列号未耗尽但时钟已回拨仍需持久化以防止崩溃后ID重复persist_last_sec(type_id,state.last_sec,state.sequence)endelseifcur_secstate.last_secthen-- 同一秒内序列号递增state.sequencestate.sequence1ifstate.sequencemax_sequencethen-- 序列号耗尽借用下一秒cur_seccur_sec1state.sequence0state.last_seccur_secpersist_last_sec(type_id,state.last_sec,state.sequence)endelse-- 新的一秒序列号重置state.sequence0state.last_seccur_sec-- HACK: persist_last_sec 如绑定磁盘IO每秒调用在高并发下可能成为瓶颈-- 若使用进程内持久化仅内存则无此问题persist_last_sec(type_id,state.last_sec,state.sequence)end-- 计算相对于纪元的偏移localts_offsetcur_sec-epochifts_offset0thenLogRichErr(string.format(mahjong_snowflake: current time %d before epoch %d, clamping to 0,cur_sec,epoch))ts_offset0end-- 组合各部分生成ID使用算术乘法替代位运算避免旧 Lua 运行时语法失败。localidts_offset*ts_multiplierserver_id*sid_multipliertype_id*type_multiplierstate.sequencereturnidendreturn{next_idnext_id}end
阅读完成 · 觉得有帮助?