1、Hive事务核心流程
1.1、时序图
1.2、核心流程图
2、Hive事务HDFS文件存储剖析
2.1、表结构
前提是设置 set hive.txn.manager=org.apache.hadoop.hive.ql.lockmgr.DbTxnManager;
使用DbTxnManager Db事务管理器
CREATE TABLE employee (id int, name string, salary int)
STORED AS ORC TBLPROPERTIES ('transactional' = 'true');
2.2、插入数据解析
INSERT INTO employee VALUES
(1, 'Jerry', 5000),
(2, 'Tom', 8000),
(3, 'Kate', 6000);
INSERT语句会在一个事务中运行。它会创建名为delta的目录,存放事务的信息和表的数据
/user/hive/warehouse/employee/delta_0000001_0000001_0000
/user/hive/warehouse/employee/delta_0000001_0000001_0000/_orc_acid_version
/user/hive/warehouse/employee/delta_0000001_0000001_0000/bucket_00000
目录名称的格式为delta_minWID_maxWID_stmtID,即 delta前缀、写事务的 ID 范围、以及语句ID。具体来说:
- 所有INSERT语句都会创建delta目录。UPDATE语句也会创建delta目录,但会先创建一个delete目录,即先删除、后插入。delete 目录的前缀是 delete_delta
- Hive会为所有的事务生成一个全局唯一的ID,包括读操作和写操作。针对写事务(INSERT、DELETE等),Hive还会创建一个写事务ID(Write ID),该ID在表范围内唯一。写事务ID会编码到delta和delete目录的名称中
- 语句ID(Statement ID)则是当一个事务中有多条写入语句时使用的,用作唯一标识。(MERGE语句可能会生成多个Statement ID)
再看文件内容,_orc_acid_version 的内容是 2,即当前 ACID 版本号是 2。它和版本 1 的主要区别是 UPDATE 语句采用了 split-update 特性,即上文提到的先删除、后插入。这个特性能够使 ACID 表支持条件下推等功能,具体可以查看 HIVE-14035。bucket_00000 文件则是写入的数据内容。由于这张表没有分区和分桶,所以只有这一个文件。事务表都以 ORC 格式存储的,我们可以使用 hive自带工具 来查看文件的内容:
hive --orcfiledump -d /user/hive/warehouse/employee/delta_0000001_0000001_0000
{"operation":0,"originalTransaction":1,"bucket":536870912,"rowId":0,"currentTransaction":1,"row":{"id":1,"name":"Jerry","salary":5000}}
{"operation":0,"originalTransaction":1,"bucket":536870912,"rowId":1,"currentTransaction":1,"row":{"id":2,"name":"Tom","salary":8000}}
{"operation":0,"originalTransaction":1,"bucket":536870912,"rowId":2,"currentTransaction":1,"row":{"id":3,"name":"Kate","salary":6000}}
输出内容被格式化为了一行行的 JSON 字符串,我们可以看到具体数据是在 row 这个键中的,其它键则是 Hive 用来实现事务特性所使用的,具体含义为:
- operation : 0 表示插入,1 表示更新,2 表示删除。由于使用了 split-update,UPDATE 是不会出现的
- originalTransaction : 是该条记录的原始写事务 ID。对于 INSERT 操作,该值和 currentTransaction 是一致的。对于 DELETE,则是该条记录第一次插入时的写事务 ID
-
bucket : 是一个 32 位整型,由 BucketCodec 编码,各个二进制位的含义为:
- 1-3 位:编码版本,当前是 001
- 4 位:保留
- 5-16 位:分桶 ID,由 0 开始。分桶 ID 是由 CLUSTERED BY 子句所指定的字段、以及分桶的数量决定的。该值和 bucket_N 中的 N 一致
- 17-20 位:保留
- 21-32 位:语句 ID
- 举例来说,整型 536936448 的二进制格式为 00100000000000010000000000000000,即它是按版本 1 的格式编码的,分桶 ID 为 1
- rowId : 是一个自增的唯一 ID,在写事务和分桶的组合中唯一
- currentTransaction : 当前的写事务 ID
- row : 具体数据。对于 DELETE 语句,则为 null
我们可以注意到,文件中的数据会按 (originalTransaction,bucket,rowId) 进行排序,这点对后面的读取操作非常关键,这些信息还可以通过 row__id 这个虚拟列进行查看:
SELECT row__id, id, name, salary FROM employee;
{"writeid":1,"bucketid":536870912,"rowid":0} 1 Jerry 5000
{"writeid":1,"bucketid":536870912,"rowid":1} 2 Tom 8000
{"writeid":1,"bucketid":536870912,"rowid":2} 3 Kate 6000
2.3、更新数据
UPDATE employee SET salary = 7000 WHERE id = 2;
这条语句会先查询出所有符合条件的记录,获取它们的 row__id 信息,然后分别创建delete和 delta目录:
/user/hive/warehouse/employee/delta_0000001_0000001_0000/bucket_00000
/user/hive/warehouse/employee/delete_delta_0000002_0000002_0000/bucket_00000
/user/hive/warehouse/employee/delta_0000002_0000002_0000/bucket_00000
delete_delta_0000002_0000002_0000/bucket_00000包含了删除的记录 :
原始 originalTransaction 为1,currentTransaction 为2(简单的说,就是当前事务比之前的事务大,其实说白了就是之后对之前的数据进行一些变更操作,否则就是原始事务和当前事务是相同的)
{"operation":2,"originalTransaction":1,"bucket":536870912,"rowId":1,"currentTransaction":2,"row":null}
delta_0000002_0000002_0000/bucket_00000 包含更新后的数据:
{"operation":0,"originalTransaction":2,"bucket":536870912,"rowId":0,"currentTransaction":2,"row":{"id":2,"name":"Tom","salary":7000}}
DELETE 语句的工作方式类似,同样是先查询,后生成delete目录
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
整理了一份ChatGPT插件清单,部分插件可能已下线,也可能不在列表中。除了这份清单外,还整理了一份:ChatGPT学习指南 欢迎大家转发、收藏、点赞支持!谨防失联! 插件名称 功能 基本提示 高级提示 KeyMate.AI搜索 使用AI驱动的自定义搜索引擎查…