Phoenix的插入語句是Upsert,Update和Insert的組合語義。即,如果數據表中沒有這條記錄那么插入這條記錄,如果有則更新。判斷是否存在相同的數據是使用ON DUPLICATE KEY來驗證的,這里的KEY就是建表時候的主鍵(PRIMARY KEY)。和Oracle的Merge Into以及MySQL的Insert …on duplicate key 是類似的功能。ON DUPLICATE KEY這個語法要到Phoenix 4.9之后才有。這個功能把HBase的Increment和CheckAndPut兩個原子操作合在了一起。當Upsert語句被提交到服務端的時候,所要更新的行會被lock住,同時相關的列會被讀取,ON DUPLICATE KEY語句會被執行。由於會鎖住所在的行,所以會有一些性能損耗,但是這個損耗比較小,類似於HBase中Put和CheckAndPut的之間的性能差異。
由於有了ON DUPLICATE KEY分句,如果行存在,那么VALUES后面的值將會為忽略。在ON DUPLICATE KEY后面還有其他的語法來實現不同的功能:
- 如果分句是ON DUPLICATE KEY IGNORE 的話,那么這行就不會被Update。
- 如果分句是ON DUPLICATE KEY UPDATE 的話,行就會被后面的Update語句更新,這個更新是獲取了行級鎖的。
如果操作同一行的多個Upsert在同一時間被批量提交的話,服務端將會按順序執行它們。所以無論自動提交是開還是關,都會得到一樣的結果。
下面列出了UPSERT的一些常用語法:
UPSERT INTO TEST VALUES('foo','bar',3);
UPSERT INTO TEST(NAME,ID) VALUES('foo',123);
UPSERT INTO TEST(ID, COUNTER) VALUES(123, 0) ON DUPLICATE KEY UPDATE COUNTER = COUNTER + 1;
UPSERT INTO TEST(ID, MY_COL) VALUES(123, 0) ON DUPLICATE KEY IGNORE;
如果是4.9之前的版本的話,ON DUPLICATE KEY 分句是不存在的。只有上例的第一和第二種語法。如果原表中有數據重復的話,就會直接更新。
和Oracle和MySQL一樣,Phoenix也有Upsert Select語法。插入或更新的數據是另外一個查詢結果集。插入或更新的列和查詢結果集的列要一一匹配,如果不顯式的列出列,那么查詢結果集的列要和目標表的列的元數據保持順序一致。如果auto commit開啟的話,會在服務端就提交了,否則會緩存到客戶端,等着顯式提交的時候進行批量upsert。自動提交的話,可以通過客戶端配置“phoenix.mutate.upsertBatchSize”指定大小,默認10000行/次。
示例語法如下:
UPSERT INTO test.targetTable(col1, col2) SELECT col3, col4 FROM test.sourceTable WHERE col5 < 100
UPSERT INTO foo SELECT * FROM bar;
最后,來說一下ON DUPLICATE KEY的一些限制。
1. 主鍵不會被更新,它是創建新行的基礎。
2. 事務表不應該使用這個功能,因為當沖突發生時,原子的Upsert操作有可能已經被異常處理掉了。
3. 不可變表不應該使用這個功能,因為不應該有數據更新不可變表。
4. 如果要使用這個子句,在連接時,就不能設置CURRENT_SCN屬性,因為除非最新的值正在被更新,否則HBase的操作不是原子的。
5. 同一列在同一句語句中不應該被更新超過一次。
6. ON DUPLICATE KEY 子句中不能有聚合或者序列。
7. 雖然在列上的全局索引是支持原子化的更細你的,但是還是不推薦在全局索引表上使用這個子語句。因為當維護二級索引的時候,行會被鎖定,這個子句胡產生一個RPC調用。