Hive中实现全局唯一自增ID的3种实战方案
1. 为什么Hive需要全局唯一自增ID在金融交易流水号、订单编号等业务场景中全局唯一且有序的ID是刚需。我经手过的支付系统中每天要处理上百万条交易记录每条记录都需要一个不会重复的身份证。但Hive作为分布式计算框架原生并不像MySQL那样提供自增主键功能。这就引出了我们今天要解决的痛点如何在分布式环境下生成既唯一又能保持递增趋势的ID。先说说常见的翻车现场。有次我用row_number()给对账单生成序号结果第二天跑批时发现ID重复了——因为没考虑跨日重置问题。还有次用UDF生成序列由于没处理并发冲突导致同一个ID被多个节点同时分配。这些坑让我意识到简单的序号生成在分布式环境下会变得异常复杂。2. 方案一改造row_number函数2.1 基础用法与局限row_number() over(order by 1)10000这个经典写法大家应该不陌生。我在电商报表中经常用它生成行号SELECT order_id, row_number() over(order by create_time) 1000 AS serial_no FROM orders但实际使用时发现三个致命问题每次查询都会重新计算序号分布式任务中各分区的序号可能重叠无法保证集群范围内全局唯一2.2 金融级改造方案在银行流水系统中我们通过组合时间戳解决了这个问题SELECT CONCAT( DATE_FORMAT(transaction_time, yyyyMMdd), LPAD(row_number() over(order by transaction_time), 10, 0) ) AS transaction_no FROM payment_records关键改进点用日期前缀保证每日ID不重复LPAD固定位数便于排序按业务时间排序而非随机值这个方案在日均百万级的交易系统中稳定运行了两年直到我们遇到需要跨日连续编号的新需求...3. 方案二UDFRedis分布式锁3.1 为什么需要引入Redis当单纯的时间戳序号无法满足需求时比如需要全年连续的订单号我们开发了基于Redis的自增ID生成器。核心原理很简单public class RedisSequenceUDF extends UDF { public String evaluate(String bizType) { Jedis jedis new Jedis(redis-host); try { return String.valueOf(jedis.incr(bizType)); } finally { jedis.close(); } } }3.2 分布式环境下的坑但在实际部署时遇到了两个问题Redis单点故障导致服务不可用网络延迟引发序号跳跃我们最终的解决方案是采用Redis Cluster集群模式增加本地缓存批量获取ID段引入ZooKeeper做故障转移-- 实际调用示例 CREATE TEMPORARY FUNCTION redis_seq AS com.xxx.RedisSequenceUDF; SELECT redis_seq(payment) AS payment_id;这个方案虽然性能稍差TPS约5000/秒但保证了金融场景下的绝对唯一性。4. 方案三基于ZooKeeper的序列服务4.1 ZK的天然优势ZooKeeper的持久顺序节点特性特别适合做分布式序号生成[zk: localhost:2181(CONNECTED) 0] create /seq/payment_ payment_ sequential Created /seq/payment_0000000001我们在Hive中通过UDF封装了ZK客户端public class ZkSequenceUDF extends UDF { private ZooKeeper zk; public String evaluate() throws Exception { if(zk null) { zk new ZooKeeper(zk-host:2181, 3000, null); } return zk.create(/seq/order_, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); } }4.2 性能优化实践直接调用的性能惨不忍睹约200次/秒后来我们做了三点改进预生成ID段缓存到内存改用Curator框架的DistributedAtomicLong针对不同业务类型划分znode空间最终优化后的架构支持了证券交易系统每秒2万次的ID生成需求且保证了全局严格递增。5. 三种方案对比选型维度row_number改造UDFRedisZK序列服务唯一性会话级唯一全局唯一全局唯一连续性会话内连续可能跳跃严格连续性能(TPS)10万50002万复杂度低中高适用场景离线报表在线交易金融核心系统在保险账单系统中我们根据业务特点做了混合方案用ZK生成年度前缀再用row_number生成日内序号既避免了ZK的性能瓶颈又保证了年度范围内的唯一性。