Odoo 14多线程使用基础与常见问题咨询
Odoo 14 多线程操作指南(附批量价格同步场景实践)
一、新线程中操作记录(读写删)
Odoo的数据库游标cr和环境env都是线程不安全的,绝对不能在多线程间共享。每个线程必须独立创建数据库连接、游标和环境:
- 核心步骤:
- 导入依赖模块:
import threading、from odoo.sql_db import db_connect、from odoo.api import Environment - 在线程函数内创建独立的数据库连接与游标
- 构建线程专属的环境
env(需传入cr、用户ID、上下文)
- 导入依赖模块:
- 示例代码:
import threading from odoo.sql_db import db_connect from odoo.api import Environment from odoo import SUPERUSER_ID def sync_supplier_prices(supplier_id): # 创建独立数据库连接 with db_connect('your_db_name') as conn: with conn.cursor() as cr: # 构建线程专属环境 env = Environment(cr, SUPERUSER_ID, {}) try: # 读操作:获取该供应商的待同步产品 products = env['product.product'].search([('supplier_id', '=', supplier_id)]) # 写操作:批量更新价格(推荐批量操作提升性能) update_vals = [(product.id, {'list_price': get_supplier_price(product.default_code)}) for product in products] env['product.product'].browse([pid for pid, _ in update_vals]).write({ 'list_price': {pid: val['list_price'] for pid, val in update_vals} }) # 创建操作:新增产品(按需使用) # env['product.product'].create({'name': 'New Supplier Product', 'list_price': 150}) # 删除操作:移除无效产品(按需使用) # env['product.product'].browse(invalid_product_ids).unlink() # 提交事务 cr.commit() except Exception as e: # 出错回滚当前线程的所有操作 cr.rollback() # 记录错误日志 env['ir.logging'].create({ 'name': 'Supplier Price Sync Error', 'type': 'server', 'message': f"Supplier {supplier_id}: {str(e)}", 'level': 'error' }) cr.commit() # CRON任务中启动多线程 def cron_sync_all_prices(): suppliers = env['res.partner'].search([('is_supplier', '=', True)]) threads = [] for supplier in suppliers: t = threading.Thread(target=sync_supplier_prices, args=(supplier.id,)) threads.append(t) t.start() # 直接返回避免阻塞CRON,线程在后台运行
二、Commit与Rollback的时机
- Commit时机:
- 完成一组独立的操作后提交,比如处理完一个供应商的所有产品后执行
cr.commit() - 大批次数据建议拆分提交(比如2000个产品分4批,每500个提交一次),防止事务过大导致锁表或性能下降
- 线程内操作必须手动提交,Odoo的自动提交机制仅针对主线程的请求上下文
- 完成一组独立的操作后提交,比如处理完一个供应商的所有产品后执行
- Rollback时机:
- 当某一组操作出现异常时回滚,避免部分数据写入导致数据不一致
- 比如单个供应商同步出错,回滚该供应商的所有操作,不影响其他线程的执行
- 注意:线程内的事务完全独立,不会干扰主线程或其他线程的事务
三、cr.savepoint() 是什么?
cr.savepoint()是事务内的保存点,用于在事务中标记一个临时节点,后续如果出现错误,可以只回滚到这个节点,而不是整个事务。
- 适用场景:批量处理数据时,某一条记录出错,跳过该记录继续处理其他记录,无需回滚全部操作
- 示例代码:
# 批量处理产品时使用保存点 cr.savepoint('batch_sync') try: for product in products: try: price = get_supplier_price(product.default_code) product.write({'list_price': price}) except Exception as e: # 记录单产品错误,不中断批量操作 env['ir.logging'].create({ 'name': 'Single Product Sync Error', 'message': f"Product {product.id}: {str(e)}", 'level': 'warning' }) cr.commit() except Exception as e: # 批量操作整体出错,回滚到保存点 cr.rollback('batch_sync') cr.commit()
四、你的批量价格同步场景注意事项
- CRON任务不阻塞:CRON函数启动线程后直接返回,不要调用
thread.join(),让Odoo主线程继续处理其他任务,线程在后台执行 - 线程数量控制:5个供应商对应5个线程即可,避免线程过多耗尽数据库连接(可在Odoo配置文件调整
db_maxconn参数扩容连接数) - 异常与日志:每个线程必须捕获异常并记录详细日志,方便排查同步失败的产品或供应商
- 资源隔离:绝对不要在多线程间共享
cr、env或记录集,每个线程必须独立创建 - 性能优化:
- 批量查询产品,避免循环内重复发起数据库请求
- 用批量
write替代循环单条写入,减少数据库交互次数 - 对HTTP请求做超时控制,避免单个供应商请求阻塞线程
常见陷阱
- 共享游标/环境:直接复用主线程的
cr或env会导致数据混乱、死锁甚至程序崩溃 - 未手动提交/回滚:线程内操作不会自动提交,未提交的数据会永久丢失,未回滚的事务会长期占用数据库锁
- 线程过载:线程数量超过数据库连接上限,导致Odoo主进程无法处理正常请求
- 静默异常:线程内未捕获异常会导致线程直接退出,无法及时发现同步失败问题
内容的提问来源于stack exchange,提问作者xixo222
相关产品推荐
相关产品推荐

