首页 > 解决方案 > 如何在 SAP HANA 中建立只读一次的实现?

问题描述

背景:我是一名长期的 MSSQL 开发人员......我想知道的是如何从 SAP HANA 实现只读一次选择。

高级伪代码:

  1. 通过 db proc 收集请求(查询)
  2. 使用请求调用 API
  3. 存储请求的结果(响应)

我有一个表 (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 分钟。

目前我的解决方案包括向源表添加一列。我想避免这种情况,但它似乎是一个更简单的实现。我将很快更新代码,但它在插入之前包含一个更新语句。

有用的参考资料:

标签: sqlhanahana-sql-script

解决方案


首先:在任何 RDBMS 中都没有“只读一次”,包括 MS SQL。从字面上看,这意味着给定的记录只能被读取一次,然后对于所有后续读取都会“消失”。(这实际上是队列所做的,或者队列的众所周知的特殊情况:管道)

我认为这不是你要找的。

相反,我相信您希望实现类似于“一次且仅一次”又名“精确一次”消息传递的处理语义。虽然这在潜在的分区网络中是不可能实现的,但在数据库的事务上下文中是可能的。

这是一个常见的要求,例如,对于应该只加载迄今为止尚未加载的数据的批处理数据加载作业(即在最后一个批处理加载作业开始后创建的新数据)。

对于冗长的前置文本感到抱歉,但是任何解决方案都取决于我们是否清楚我们想要实际实现的目标。我现在将采取一种方法。

主要的 RDBMS 早就发现,如果目标是实现高事务吞吐量,那么阻塞读取器通常是一个糟糕的主意。因此,HANA 不会阻止读者 - 永远(好吧,永远不会,但在正常操作设置中)。“exactly-once”处理要求的主要问题实际上不是读取记录,而是处理不止一次或根本不处理的可能性。

这两个潜在问题都可以通过以下方法解决:

  1. SELECT ... FOR UPDATE ...应处理的记录(基于例如未处理的记录、最多 N 条记录、奇偶 ID、邮政编码……)。有了这个,当前会话有一个 UPDATE TRANSACTION 上下文和对选定记录的排他锁。其他事务仍然可以读取这些记录,但没有其他事务可以锁定这些记录——无论是 for UPDATEDELETE还是 for SELECT ... FOR UPDATE ...

  2. 现在您进行处理 - 无论这涉及到:合并、插入、更新其他表、编写日志条目......

  3. 作为处理的最后一步,您希望将记录“标记”为已处理。这究竟是如何实现的,并不重要。可以在表中创建一个-columnprocessed并将其设置为TRUE处理记录的时间。或者可以有一个单独的表,其中包含已处理记录的主键(可能还有一个load-job-id来跟踪多个加载作业)。无论以何种方式实现,processed是需要捕获此状态的时间点。

  4. COMMITROLLBACK(以防出现问题)。这会将COMMIT记录写入目标表,处理状态信息,并将释放源表的排他锁。

如您所见,步骤 1通过选择所有可以处理的所需记录(即它们没有被任何其他进程专门锁定)来处理可能丢失记录的问题。 第 3 步通过跟踪已处理的记录来处理可能多次处理的记录问题。显然,必须在第 1 步中检查此跟踪——这两个步骤是相互关联的,这就是我明确指出它们的原因。最后,所有的处理都发生在同一个数据库事务上下文中,允许保证COMMITROLLBACK跨越整个事务。这意味着,在提交记录处理时,不会丢失任何“记录标记”。

现在,为什么这种方法比使记录“不可读”更可取?因为系统中的其他进程。

也许源记录仍然被事务系统读取,但从未更新。该事务系统不必等待数据加载完成。

或者,也许有人想要对源数据进行一些分析,并且还需要读取这些记录。

或者,也许您想并行化数据加载:很容易跳过锁定的记录,只处理现在“可更新”的记录。请参阅例如在批处理时负载平衡 SQL 读取?为了那个原因。

好吧,我猜你希望有更容易消费的东西;唉,这就是我理解的这种要求的方法。


推荐阅读