使用GCP Spanner Python客户端插入数据时遇重复行事务错误
问题排查与解决
错误原因分析
你遇到的400 Row [...] was already inserted in this transaction错误,主要有两个核心诱因:
- 主键冲突:表中已存在对应ID的行(可能是你之前通过UI插入的数据),重复插入时触发主键约束报错。错误信息中的
4503837324345344是实际冲突的主键值,说明代码中可能存在参数传递错误,比如时间戳误用导致主键被意外赋值。 - 时间戳使用错误:你的表中
curr_timestamp列启用了allow_commit_timestamp,这要求使用Spanner专属的提交时间戳机制,而非普通的CURRENT_TIMESTAMP()。误用时间戳写法会引发类型解析错误,进而导致主键列被错误赋值,触发奇怪的重复插入报错。
解决方案
1. 先确认表中已有数据
执行以下查询查看表中已存在的主键:
SELECT Id FROM MyTable;
如果发现代码中要插入的Id=1/Id=2或错误信息中的4503837324345344已存在,可选择删除这些行,或者改用UPSERT(覆盖现有数据)。
2. 修正时间戳的使用方式
针对你的两种插入方法,分别修正代码:
方法1(SQL插入)修正
使用Spanner的PENDING_COMMIT_TIMESTAMP()函数自动填充提交时间,替换原来的CURRENT_TIMESTAMP():
from google.cloud import spanner spanner_db = instance.database(DATABASE_ID) insert_query = """INSERT INTO MyTable (Id, name, curr_timestamp) VALUES (1, 'name1', PENDING_COMMIT_TIMESTAMP()), (2, 'name2', PENDING_COMMIT_TIMESTAMP())""" def insert_data(transaction): row_ct = transaction.execute_update(insert_query) print(f"插入了 {row_ct} 行数据") spanner_db.run_in_transaction(insert_data)
如果需要覆盖已有数据,把INSERT改成UPSERT即可。
方法2(批量插入)修正
使用spanner.COMMIT_TIMESTAMP常量替代代码中的CURRENT_TIMESTAMP():
from google.cloud import spanner spanner_db = instance.database(DATABASE_ID) def insert_batch_data(): with spanner_db.batch() as batch: batch.insert( table='MyTable', columns=("Id", "name", "curr_timestamp"), values=[ [1, 'name1', spanner.COMMIT_TIMESTAMP], [2, 'name2', spanner.COMMIT_TIMESTAMP] ] ) print("数据插入完成") insert_batch_data()
同样,若要覆盖数据,将batch.insert()替换为batch.upsert()。
3. 验证修正效果
执行修正后的代码前,确保:
- 已导入
google.cloud.spanner模块; - 表中不存在与插入数据重复的主键;
- 时间戳写法符合Spanner的
allow_commit_timestamp列要求。
内容的提问来源于stack exchange,提问作者Ankit Seth
相关产品推荐
相关产品推荐

