sql - 如何在 SAP HANA 中建立只读一次的实现?
问题描述
背景:我是一名长期的 MSSQL 开发人员......我想知道的是如何从 SAP HANA 实现只读一次选择。
高级伪代码:
- 通过 db proc 收集请求(查询)
- 使用请求调用 API
- 存储请求的结果(响应)
我有一个表 (A),它是流程输入的来源。一旦一个过程完成,它会将结果写入另一个表 (B)。
如果我只是在表 A 中添加一列以避免并发处理器从 A 中选择相同的记录,也许这一切都解决了?
我想知道如何在不将列添加到源表 A 的情况下执行此操作。
我尝试的是表 A 和 B 之间的左外连接,以从 A 中获取在 B 中没有对应行(尚未)的行。这不起作用,或者我没有实现这样的行只处理 1 次由任何处理器。
我有一个存储过程来处理批量选择:
/*
* getBatch.sql
*
* SYNOPSIS: Retrieve the next set of criteria to be used in a search
* request. Use left outer join between input source table
* and results table to determine the next set of inputs, and
* provide support so that concurrent processes may call this
* proc and get their inputs exclusively.
*/
alter procedure "ACOX"."getBatch" (
in in_limit int
,in in_run_group_id varchar(36)
,out ot_result table (
id bigint
,runGroupId varchar(36)
,sourceTableRefId integer
,name nvarchar(22)
,location nvarchar(13)
,regionCode nvarchar(3)
,countryCode nvarchar(3)
)
) language sqlscript sql security definer as
begin
-- insert new records:
insert into "ACOX"."search_result_v4" (
"RUN_GROUP_ID"
,"BEGIN_DATE_TS"
,"SOURCE_TABLE"
,"SOURCE_TABLE_REFID"
)
select
in_run_group_id as "RUN_GROUP_ID"
,CURRENT_TIMESTAMP as "BEGIN_DATE_TS"
,'acox.searchCriteria' as "SOURCE_TABLE"
,fp.descriptor_id as "SOURCE_TABLE_REFID"
from
acox.searchCriteria fp
left join "ACOX"."us_state_codes" st
on trim(fp.region) = trim(st.usps)
left outer join "ACOX"."search_result_v4" r
on fp.descriptor_id = r.source_table_refid
where
st.usps is not null
and r.BEGIN_DATE_TS is null
limit :in_limit;
-- select records inserted for return:
ot_result =
select
r.ID id
,r.RUN_GROUP_ID runGroupId
,fp.descriptor_id sourceTableRefId
,fp.merch_name name
,fp.Location location
,st.usps regionCode
,'USA' countryCode
from
acox.searchCriteria fp
left join "ACOX"."us_state_codes" st
on trim(fp.region) = trim(st.usps)
inner join "ACOX"."search_result_v4" r
on fp.descriptor_id = r.source_table_refid
and r.COMPLETE_DATE_TS is null
and r.RUN_GROUP_ID = in_run_group_id
where
st.usps is not null
limit :in_limit;
end;
当运行 7 个并发处理器时,我得到了 35% 的重叠。也就是说,在 5,000 个输入行中,生成的行数为 6,755。运行时间约为 7 分钟。
目前我的解决方案包括向源表添加一列。我想避免这种情况,但它似乎是一个更简单的实现。我将很快更新代码,但它在插入之前包含一个更新语句。
有用的参考资料:
解决方案
首先:在任何 RDBMS 中都没有“只读一次”,包括 MS SQL。从字面上看,这意味着给定的记录只能被读取一次,然后对于所有后续读取都会“消失”。(这实际上是队列所做的,或者队列的众所周知的特殊情况:管道)
我认为这不是你要找的。
相反,我相信您希望实现类似于“一次且仅一次”又名“精确一次”消息传递的处理语义。虽然这在潜在的分区网络中是不可能实现的,但在数据库的事务上下文中是可能的。
这是一个常见的要求,例如,对于应该只加载迄今为止尚未加载的数据的批处理数据加载作业(即在最后一个批处理加载作业开始后创建的新数据)。
对于冗长的前置文本感到抱歉,但是任何解决方案都取决于我们是否清楚我们想要实际实现的目标。我现在将采取一种方法。
主要的 RDBMS 早就发现,如果目标是实现高事务吞吐量,那么阻塞读取器通常是一个糟糕的主意。因此,HANA 不会阻止读者 - 永远(好吧,永远不会,但在正常操作设置中)。“exactly-once”处理要求的主要问题实际上不是读取记录,而是处理不止一次或根本不处理的可能性。
这两个潜在问题都可以通过以下方法解决:
SELECT ... FOR UPDATE ...
应处理的记录(基于例如未处理的记录、最多 N 条记录、奇偶 ID、邮政编码……)。有了这个,当前会话有一个 UPDATE TRANSACTION 上下文和对选定记录的排他锁。其他事务仍然可以读取这些记录,但没有其他事务可以锁定这些记录——无论是 forUPDATE
、DELETE
还是 forSELECT ... FOR UPDATE ...
。现在您进行处理 - 无论这涉及到:合并、插入、更新其他表、编写日志条目......
作为处理的最后一步,您希望将记录“标记”为已处理。这究竟是如何实现的,并不重要。可以在表中创建一个-column
processed
并将其设置为TRUE
处理记录的时间。或者可以有一个单独的表,其中包含已处理记录的主键(可能还有一个load-job-id来跟踪多个加载作业)。无论以何种方式实现,这processed
是需要捕获此状态的时间点。COMMIT
或ROLLBACK
(以防出现问题)。这会将COMMIT
记录写入目标表,处理状态信息,并将释放源表的排他锁。
如您所见,步骤 1通过选择所有可以处理的所需记录(即它们没有被任何其他进程专门锁定)来处理可能丢失记录的问题。
第 3 步通过跟踪已处理的记录来处理可能多次处理的记录问题。显然,必须在第 1 步中检查此跟踪——这两个步骤是相互关联的,这就是我明确指出它们的原因。最后,所有的处理都发生在同一个数据库事务上下文中,允许保证COMMIT
或ROLLBACK
跨越整个事务。这意味着,在提交记录处理时,不会丢失任何“记录标记”。
现在,为什么这种方法比使记录“不可读”更可取?因为系统中的其他进程。
也许源记录仍然被事务系统读取,但从未更新。该事务系统不必等待数据加载完成。
或者,也许有人想要对源数据进行一些分析,并且还需要读取这些记录。
或者,也许您想并行化数据加载:很容易跳过锁定的记录,只处理现在“可更新”的记录。请参阅例如在批处理时负载平衡 SQL 读取?为了那个原因。
好吧,我猜你希望有更容易消费的东西;唉,这就是我理解的这种要求的方法。
推荐阅读
- php - 如何在 PHP 中结合 foreach 和 json_decode?
- r - 将颜色分配给从 DF 中选择相应颜色的标志
- java - 在哪里应用 ApplicationContextAware
- c# - Web API 加载内容文件
- c# - 找不到 Net Core NHibernate System.Data.SQLite
- python - 域条件不适用于特定组
- javascript - 禁用 Angular 2+ 中的链接
- react-native - 如何在本机反应中添加对 Animated.sequence 的函数调用
- java - Java8中的谓词和函数接口有什么区别?
- java - Spring Integration Java DSL HTTP在超时错误内没有收到回复