TiDB Server 端如何处理多条语句

为提高效率,提问时请提供以下信息,问题描述清晰可优先响应。

  • 【TiDB 版本】:3.0.10
  • 【问题描述】: TiDB 给我的印象是不支持多语句代码块的处理的,我注意到即使客户端(MySql-Connector-Python)向 TiDB发送了多条语句,TiDB 端也只能跟踪到单条语句。

现在的问题是,TiDB Server端到底是怎么处理这种一口气发送的多条语句的呢?会不会把前面的忽略,只处理最后一条呢?

p.s. 我翻查了 MySql-Connector-Python 的源码,可以确认它(cursor.execute 方法的 Multi 模式下)确实是把传入的多条语句都打包一口气发送给了服务器端的。

你的多语句,具体是只什么样的sql,能举个例子吗? 另外,能否麻烦简单测试下。

嗯,有点奇怪,

我用 SELECT 1; CREATE TABLE t1(id int); INSERT INTO t1 VALUES (101); SELECT 2 这样的语句去测试的时候,看起来是一条不差的执行成功了。

但是在我们的业务场景下,像这样的语句(作为一个整体的),

set @@tidb_batch_insert=1;
INSERT INTO ods.ods_zfgjj_stat_apply_repay_detail
(product_cid, user_sid, apply_sid, partner_id, period_id, repay_total_amt, repay_principal, repay_service_fee, repay_interest
, repay_overdue_fee, repay_owing_dun, repay_management_fee, repay_over_due_fines, repay_other_fee_total, repay_date, real_repay_date
, sub_repay_state, real_total_amt, real_principal, real_service_fee, real_interest, real_overdue_fee, real_owing_dun
, real_management_fee, real_over_due_fines, real_other_fee_total, repay_type, remain_credit, overdue_days, is_exist
, related_overdue, loan_time)

	select
b.product_cid
,b.user_sid
,a.*,b.loan_time from
(
select apply_id
,null loan_no
,period_id
,owing_amount
,owing_principal
,null service_fee
,owing_interest
,null overdue_fee
,owing_dun
,null owing_management
,null over_due_fines
,null other_fee_total
,due_date
,payment_date
,status
,case when LENGTH(trim(repay_amount))<1 then null else repay_amount end repay_amount
,null real_repay_principal
,null real_service_fee
,null repay_interest
,null real_overdue_fee
,null repay_dun
,null repay_management 
,null real_over_due_fines
,null real_other_fee_total
,null repay_type
,null remain_credit
,null overdue_day
,1 as is_exist
,1 related_overdue
from zfgjj.sys_apply_ppd_detail
union all
select apply_id
,null
,period
,plan_principal+plan_interest+other_fee_total
,plan_principal
,other_fee_total
,plan_interest
,null
,null
,null
,null
,null real_other_fee_total
,plan_repay_date
,payment_date
,status,null
,repay_principal
,null
,repay_interest
,null
,null
,null
,null
,null
,null
,null
,null
,1 as is_exist
,0 related_overdue 
from zfgjj.sys_apply_xiaoying_detail
union all
select apply_id
,apply_serial
,term,null
,principal
,svc_fee
,interest
,null
,penalty
,null
,null
,life_insu_fee+prepay_pkg_fee
,date
,null
,status
,null
,null
,null
,null
,null
,null
,null
,null
,null
,null
,null
,null
,4 as is_exist
,1 related_overdue 
from zfgjj.sys_apply_mashang_plan
union all
select
apply_id
,null
,repayPeriod
,repayAmount
,loanAmount
,null
,shouldRepayProfit
,null
,repayPunishInterest
,null
,null
,shouldRepayOther
,case when planRepayDate = '' then null else planRepayDate end planRepayDate
,case when actualRepayDate = '' then null else actualRepayDate end actualRepayDate
,state
,actualRepayAmount
,null
,null
,null
,null
,null
,null
,null
,null
,null
,null
,null
,1 as is_exist
,1 related_overdue 
from zfgjj.sys_apply_zhongan_detail
union all
select apply_id
,order_id
,loan_period
,null
,loan_amount
,r_serviceAmount
,null
,null
,null
,null
,null
,null
,null
,null
,r_state
,null
,null
,null
,null
,null
,null
,null
,null
,null
,null
,null
,null
,3 as is_exist
,0 related_overdue 
from zfgjj.sys_apply_xh_detail
union all
select apply_id
,null
,period_id
,owing_credit
,null
,null
,null
,null
,null
,null
,null
,null
,due_date
,null
,case when late_notice=1 and status=0 then '逾期未还'
			when late_notice=1 and status=1 then '逾期已还'
			when late_notice=0 and status=0 then '未还清'
			when late_notice=0 and status=1 then '还清' end status
,repay_credit
,null
,null
,null
,null
,null
,null
,null
,null
,null
,null
,null
,2 as is_exist
,0 related_overdue 
from zfgjj.sys_apply_wld_detail
union all
select apply_id
,null
,period_id
,repay_credit
,null
,null
,null
,null
,null
,null
,null
,null
,repay_date
,null
,null
,null
,null
,null
,null
,null
,null
,null
,null
,null
,null
,remain_credit
,null
,2 as is_exist
,0 related_overdue 
from zfgjj.sys_apply_zhongtengxin_detail
)a  
left join zfgjj.sys_apply b on a.apply_id=b.sid
where a.apply_id is not null;

最后却报 mysql.connector.errors.DatabaseError: 8004 (HY000): transaction too large, len:300001 退出了。看起来像 set @@tidb_batch_insert=1; 没有执行过一样。

上面那条业务 SQL 会产生 260多万条记录。

ps. 拎出来,在 Navicat 下交互式执行是可以成功的。

你好,

  1. TiDB 这边对单个事务是有限制的。具体可以参考官方文档: Transaction too large 是什么原因 。建议对单个大事务进行拆分。

  2. tidb_batch_insert 目前并不推荐使用

如果使用的是 3.0.10 版本,建议使用 where 语句对 select 进行拆分,分批插入。

如果navicate 可以执行,是否将程序中 set @@tidb_batch_insert=1; 去掉,仅仅执行 insert to select 即可?

https://pingcap.com/docs-cn/v3.0/faq/tidb/#433-transaction-too-large-是什么原因怎么解决

可否考虑 4.0-rc 的大事务可以看下:

https://pingcap.com/docs-cn/stable/reference/transactions/overview/

嗯,升级到 4.0是一个好办法,但是我们生产环境使用的数据库,还是要等等再升级吧

好的,

等 4.0 ga 是可以升级的