Atomic Upsert
To support atomic upsert, an optional ON DUPLICATE KEY clause, similar to the MySQL syntax, has been encorporated into the UPSERT VALUES command as of Phoenix 4.9. The general syntax is described here. This feature provides a superset of the HBase Increment and CheckAndPut functionality to enable atomic upserts. On the server-side, when the commit is processed, the row being updated will be locked while the current column values are read and the ON DUPLICATE KEY clause is executed. Given that the row must be locked and read when the ON DUPLICATE KEY clause is used, there will be a performance penalty (much like there is for an HBase Put versus a CheckAndPut).
In the presence of the ON DUPLICATE KEY clause, if the row already exists, the VALUES specified will be ignored and instead either:
- the row will not be updated if ON DUPLICATE KEY IGNORE is specified or
- the row will be updated (under lock) by executing the expressions following the ON DUPLICATE KEY UPDATE clause.
Multiple UPSERT statements for the same row in the same commit batch will be processed in the order of their execution. Thus the same result will be produced when auto commit is on or off.
Examples
For example, to atomically increment two counter columns, you would execute the following command:
UPSERT INTO my_table(id, counter1, counter2) VALUES ('abc', 0, 0) ON DUPLICATE KEY UPDATE counter1 = counter1 + 1, counter2 = counter2 + 1;
To only update a column if it doesn’t yet exist:
UPSERT INTO my_table(id, my_col) VALUES ('abc', 100) ON DUPLICATE KEY IGNORE;
Note that arbitrarily complex expressions may be used in this new clause:
UPSERT INTO my_table(id, total_deal_size, deal_size) VALUES ('abc', 0, 100) ON DUPLICATE KEY UPDATE total_deal_size = total_deal_size + deal_size, approval_reqd = CASE WHEN total_deal_size < 100 THEN 'NONE' WHEN total_deal_size < 1000 THEN 'MANAGER APPROVAL' ELSE 'VP APPROVAL' END;
Limitations
The following limitations are enforced for the ON DUPLICATE KEY clause usage:
- Primary key columns may not be updated, since this would essentially be creating a new row.
- Transactional tables may not use this clause as atomic upserts are already possible through exception handling when a conflict occurs.
- Immutable tables may not use this clause as by definition there should be no updates to existing rows
- The CURRENT_SCN property may not be set on connection when this clause is used as HBase does not handle atomicity unless the latest value is being updated.
- The same column should not be updated more than once in the same statement.
- No aggregation or references to sequences are allowed within the clause.
- Global indexes on columns being atomically updated are not supported, as potentially a separate RPC across the wire would be made while the row is under lock to maintain the secondary index.
原子更新插入
为了支持原子更新插入,从 Phoenix 4.9 开始,类似于 MySQL 语法的可选 ON DUPLICATE KEY 子句已合并到 UPSERT VALUES 命令中。此处描述了一般语法。此功能提供了 HBase Increment 和 CheckAndPut 功能的超集,以启用原子更新插入。在服务器端,当处理提交时,正在更新的行将被锁定,同时读取当前列值并执行 ON DUPLICATE KEY 子句。鉴于在使用 ON DUPLICATE KEY 子句时必须锁定和读取该行,将会有性能损失(很像 HBase Put 与 CheckAndPut 的情况)。
在存在 ON DUPLICATE KEY 子句的情况下,如果该行已经存在,则指定的 VALUES 将被忽略,而是:
- 如果指定了 ON DUPLICATE KEY IGNORE 或
- 该行将通过执行 ON DUPLICATE KEY UPDATE 子句之后的表达式来更新(锁定)。
同一提交批次中同一行的多个 UPSERT 语句将按照它们的执行顺序进行处理。因此,当自动提交打开或关闭时,将产生相同的结果。
例子
例如,要以原子方式递增两个计数器列,您可以执行以下命令:
仅更新尚不存在的列:
请注意,在这个新子句中可以使用任意复杂的表达式:
限制
对 ON DUPLICATE KEY 子句的使用实施以下限制:
- 主键列可能不会更新,因为这实际上是创建一个新行。
- 事务表可能不使用此子句,因为在发生冲突时通过异常处理已经可以进行原子更新插入。
- 不可变表可能不使用此子句,因为根据定义,不应更新现有行
- 当使用此子句时,可能不会在连接上设置 CURRENT_SCN 属性,因为除非更新最新值,否则 HBase 不处理原子性。
- 同一列不应在同一语句中多次更新。
- 子句中不允许聚合或引用序列。
- 不支持原子更新的列上的全局索引,因为在行处于锁定状态时可能会跨线路进行单独的 RPC 以维护二级索引。