高并发场景下,用SpannerClient Mutation插入后获取主键的方案咨询
解决方案与最佳实践
方案1:用DML INSERT ... THEN RETURN替代Mutation
这是最直接的原生解决方案,利用Spanner支持的INSERT ... THEN RETURN语法,在插入实体的同时直接获取引擎生成的bit reverted sequence主键,完美匹配你的需求。
示例Kotlin代码:
val insertSql = """ INSERT INTO your_entity_table (field1, field2) VALUES (@fieldVal1, @fieldVal2) THEN RETURN id """ val params = mapOf("fieldVal1" to "sample_val1", "fieldVal2" to 123) val generatedId = databaseClient .sql(insertSql) .bindAll(params) .execute() .single() .getLong("id") // 直接用generatedId访问刚插入的实体
- 优势:无需额外同步逻辑,一步完成写入+主键获取,性能足以支撑数千QPS的并发需求;批量插入时也能返回所有生成的主键。
- 注意:如果你的代码之前大量使用Mutation,只需针对需要返回主键的写入场景切换为DML即可,两者可以共存。
方案2:客户端预生成唯一主键(替代自动序列)
如果想继续使用Mutation而不切换到DML,可以在客户端生成全局唯一的主键值,比如UUID(虽然bit reverted sequence是Spanner的优化序列,但UUID在高并发下也能保证唯一性,且无需依赖数据库生成)。
示例Kotlin代码:
val clientGeneratedId = UUID.randomUUID().toString() val mutation = Mutation.newInsertBuilder("your_entity_table") .set("id").to(clientGeneratedId) .set("field1").to("sample_val1") .build() databaseClient.write(listOf(mutation)) // 直接用clientGeneratedId访问实体
- 优势:Mutation写入完成后立刻拥有主键,无需额外查询;代码风格统一,避免DML和Mutation的切换成本。
- 变通方案:如果必须保留Spanner自动生成的bit reverted sequence作为主键,可以在表中新增一个客户端可控的唯一索引列(比如
client_unique_id),插入时生成并设置该列,之后通过client_unique_id查询实体获取主键。
方案3:利用Commit Timestamp做延迟查询(特殊场景)
如果业务允许短暂的查询延迟,可以在表中添加commit_timestamp列(定义为TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)),插入时不设置该值,由Spanner自动填充提交时间。之后客户端通过插入时的唯一业务属性+时间范围定位实体,获取主键。
示例:
// 插入时不设置commit_timestamp,由Spanner自动生成 val mutation = Mutation.newInsertBuilder("your_entity_table") .set("unique_business_field").to("only_one_this_val") .build() databaseClient.write(listOf(mutation)) // 用业务属性+时间范围查询主键 val querySql = """ SELECT id FROM your_entity_table WHERE unique_business_field = @bizVal AND commit_timestamp >= @startTime """ val params = mapOf( "bizVal" to "only_one_this_val", "startTime" to Instant.now().minusSeconds(5) ) val generatedId = databaseClient .sql(querySql) .bindAll(params) .execute() .single() .getLong("id")
- 优势:保持Mutation的写入性能,适合对写入延迟要求极高、查询可接受短暂等待的场景。
- 注意:必须确保业务属性本身具备唯一性(或结合时间范围后唯一),否则可能查到多条数据。
不推荐同步ID空间方案的原因
你提到的多实例同步ID空间方案,会引入分布式锁、ID分配服务等额外复杂度,不仅增加运维成本,还可能成为性能瓶颈——完全没必要,Spanner原生提供的方案已经能更高效地解决问题。
内容的提问来源于stack exchange,提问作者Matteo Bertucelli
相关产品推荐
相关产品推荐

