1. 使用 hive-cli 执行以下语句
create table t1(c1 string) stored as textfile;
load data local inpath '/etc/profile' overwrite into table t1;
create table t2(c1 int) partitioned by (pt string);
insert overwrite table t2 partition (pt=1) select length(c1) from t1;
在一个会话 session 1 执行时:
insert overwrite table t2 partition (pt=2) select length(c1) from t1;
在会话 session 1 的语句执行时, 另一个会话 session 2 中执行:
select * from t2 where pt=1;
会发现 session 2 在 session 1 执行结束后才开始执行。
2. 原因分析
当前配置:
<property>
<name>hive.txn.manager</name>
<value>org.apache.hadoop.hive.ql.lockmgr.DbTxnManager</value>
</property>
在 session 1 执行时,会获取 t2 表 partition (pt=2) 这个分区的 exclusive write 权限,现在没有其他会话操作表 t2,获取锁。 session 2的语句,会获取 t2 表的两个权限,分别是 t2 表的 shared read 权限和 t2 表 partition(pt=1) 的 shared read 权限这两个权限。 原因 t2 表的 shared read 权限获取不了。
3. 解决方案
3.1 insert overwrite 的解决方案
hive-site.xml 修改以下参数的值
<property>
<name>hive.txn.manager</name>
<value>org.apache.hadoop.hive.ql.lockmgr.DummyTxnManager</value>
</property>
<property>
<name>hive.support.concurrency</name>
<value>false</value>
</property>
以上配置,当调用获取锁、释放锁、开启事务、关闭事务等调用时直接返回。
3.2 insert overwrite 的解决方案2
hive-site.xml 修改以下参数的值
<property>
<name>hive.txn.manager</name>
<value>org.apache.hadoop.hive.ql.lockmgr.DummyTxnManager</value>
</property>
<property>
<name>hive.lock.manager</name>
<value>org.apache.hadoop.hive.ql.lockmgr.zookeeper.ZooKeeperHiveLockManager</value>
</property>
3.3 insert into 的解决方案
如果是 insert into 而不是insert overwrite 时,可以设置 hive.txn.strict.locking.mode=false 解决,这时 insert into 获取共享锁。 各配置项如下:
<property>
<name>hive.txn.manager</name>
<value>org.apache.hadoop.hive.ql.lockmgr.DbTxnManager</value>
</property>
<property>
<name>hive.txn.strict.locking.mode</name>
<value>false</value>
</property>
4 机制说明
4.1 总体说明
txn manager 里实例化一个 lock mamanger。
Hive txn manager 一共两种实现,一种是 DbTxnManager,一种是 DummyTxnManager.
4.2 DbTxnManager
当 hive.txn.manager=org.apache.hadoop.hive.ql.lockmgr.DbTxnManager 时,固定使用 DbLockManager,不论hive.lock.manager 配置为何值。
hive.txn.strict.locking.mode 的作用。仅当 hive.txn.manager=org.apache.hadoop.hive.ql.lockmgr.DbTxnManager 并且是内部表生效。当值为 false 时,insert into ${TABLE_NAME} partition(不是 insert overwrite table ${TABLE_NAME} partition) 获取 shared 锁。当值为 true 时,insert into ${TABLE_NAME} partition 获取 exclusive 锁。
往分区写数据时,插入一条记录,内容如下:
HL_LOCK_EXT_ID: 2234
HL_LOCK_INT_ID: 2
HL_TXNID: 2721
HL_DB: default
HL_TABLE: t2
HL_PARTITION: pt=3
HL_LOCK_STATE: a
HL_LOCK_TYPE: e
HL_LAST_HEARTBEAT: 0
HL_ACQUIRED_AT: 1658285505000
HL_USER: houzhizhen
HL_HOST: localhost
HL_HEARTBEAT_COUNT: NULL
HL_AGENT_INFO: houzhizhen_20220720105145_3c787b27-94d0-44d1-be62-10965ac3258e
HL_BLOCKEDBY_EXT_ID: NULL
HL_BLOCKEDBY_INT_ID: NULL
当读取一个分区时(pt=1),需要两个权限,一个是 表的 shared 权限,一个是分区的 shared 权限。表的权限拿不到,因为按数据库名和表名拿到的 lock 记录时exclusive。
HL_LOCK_EXT_ID: 2235
HL_LOCK_INT_ID: 1
HL_TXNID: 2722
HL_DB: default
HL_TABLE: t2
HL_PARTITION: NULL
HL_LOCK_STATE: w
HL_LOCK_TYPE: r
HL_LAST_HEARTBEAT: 0
HL_ACQUIRED_AT: NULL
HL_USER: houzhizhen
HL_HOST: localhost
HL_HEARTBEAT_COUNT: NULL
HL_AGENT_INFO: houzhizhen_20220720105214_f33da326-e509-47af-a208-ea1a7b6b9783
HL_BLOCKEDBY_EXT_ID: NULL
HL_BLOCKEDBY_INT_ID: NULL
4.3 DummyTxnManager
当 hive.txn.manager=org.apache.hadoop.hive.ql.lockmgr.DummyTxnManager 并且 hive.support.concurrency=false 的时候,不会实例化一个 lock manager。调用获取锁、释放锁、开启事务、关闭事务等调用时直接返回。
当 hive.txn.manager=org.apache.hadoop.hive.ql.lockmgr.DummyTxnManager 并且 hive.support.concurrency=true 时, lock manager 根据 hive.lock.manager 的值创建对应的对象。这时,没有事务的功能。开启事务、关闭事务调用时直接返回。但是获取锁和释放锁操作,转给 lock manager 对应的方法处理。
DummyTxnManager 会把获取的权限的请求进行拆分。 当写分区(pt=3) 时,锁对象(db=default, table=t2, pt=3),拆成以下3个锁对象。
dbLock = (pathName=["default"], type="shared");
tableLock = (pathName=["default", "t2"], type="shared");
partitionLock = (pathName=["default", "t2", "pt=3"], type="exclusive");
把这些锁对象的 pathName 转成 zookeeper 的 znode,申请之后的 znode 为:
[zk: localhost:2181(CONNECTED) 6] ls /hive_zookeeper_namespace/default
[LOCK-SHARED-0000000000, t2]
[zk: localhost:2181(CONNECTED) 10] ls /hive_zookeeper_namespace/default/t2
[LOCK-SHARED-0000000000, pt=3]
[zk: localhost:2181(CONNECTED) 11] ls /hive_zookeeper_namespace/default/t2/pt=3
[LOCK-EXCLUSIVE-0000000000]
当读分区(pt=1) 时,需要两个锁对象(db=default, table=t2, pt=1)和(db=default, table=t2)。
(db=default, table=t2, pt=1)拆成以下3个锁对象。
dbLock = (pathName=["default"], type="shared");
tableLock = (pathName=["default", "t2"], type="shared");
partitionLock = (pathName=["default", "t2", "pt=1"], type="shared");
(db=default, table=t2)拆成以下2个锁对象。
dbLock = (pathName=["default"], type="shared");
tableLock = (pathName=["default", "t2"], type="shared");
对这5个对象进行去重。 得到3个对象
dbLock = (pathName=["default"], type="shared");
tableLock = (pathName=["default", "t2"], type="shared");
partitionLock = (pathName=["default", "t2", "pt=1"], type="shared");
把这些锁对象的 pathName 转成 zookeeper 的 znode,申请之后的 znode 为:
[zk: localhost:2181(CONNECTED) 6] ls /hive_zookeeper_namespace/default
[LOCK-SHARED-0000000000, t2]
[zk: localhost:2181(CONNECTED) 10] ls /hive_zookeeper_namespace/default/t2
[LOCK-SHARED-0000000000, pt=3]
[zk: localhost:2181(CONNECTED) 11] ls /hive_zookeeper_namespace/default/t2/pt=1
[LOCK-SHARED-0000000000]
所以当写 pt=3 时,读取 pt=1 是没有问题。
|