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 目录