大数据开发之Hive篇17-Hive锁机制
备注:
Hive 版本 2.1.1
一.Hive锁概述
Hive支持如下两种类型的锁:
1.Shared (S)
2.Exclusive (X)
顾名思义,可以同时获取多个共享锁,而X锁阻塞所有其他锁。
兼容性矩阵如下:
image.png
对于某些操作,锁本质上是分层的——例如,对于某些分区操作,表也是锁定的(以确保在创建新分区时表不会被删除)。
获取锁模式背后的理性如下:
对于非分区表,锁模式非常直观。当表被读取时,需要一个S锁,而其他所有操作(插入表、修改表等)都需要一个X锁。
对于分区表,思路如下:
在执行读操作时,表和相关分区上需要一个'S'锁。对于所有其他操作,在分区上取'X'锁。但是,如果更改只适用于更新的分区,则在表上获得一个'S'锁,而如果更改适用于所有分区,则在表上获得一个'X'锁。因此,可以对旧的分区进行读写,同时将新分区转换为RCFile。当一个分区以任何模式被锁定时,它的所有父分区都以'S'模式被锁定。
基于此,操作获得的锁如下:
image.png
为了避免死锁,这里提出了一个非常简单的方案。所有要锁定的对象都按字典顺序排序,并获取所需的模式锁定。注意,在某些情况下,对象列表可能是未知的——例如,在动态分区的情况下,正在修改的分区列表在编译时是未知的——因此,该列表是保守生成的。因为分区的数量可能不知道,所以应该在表上取一个排他锁,或者已知的前缀。
将添加两个新的可配置参数,以决定锁的重试次数和每次重试之间的等待时间。如果重试次数非常高,则可能导致激活锁。查看ZooKeeper recipes (http://hadoop.apache.org/zookeeper/docs/r3.1.2/recipes.html#sc_recipes_Locks),了解如何使用ZooKeeper api实现读/写锁。请注意,锁定请求将被拒绝,而不是等待。现有的锁将被释放,所有锁都将在重试间隔后重试。
由于锁的层次结构特性,上面列出的配方将不能按照指定的方式工作。
表T的'S'锁指定如下:
调用create()创建路径名为"/warehouse/T/read-"的节点。这是稍后在协议中使用的锁节点。确保设置了sequence和ephemeral标志。
在不设置监视标志的情况下在锁节点上调用getChildren()。
如果有一个子路径名以“write-”开头,并且序列号比所获得的序列号更低,则无法获得锁。删除第一步中创建的节点并返回。
否则将授予锁。
表T的'X'锁指定如下:
调用create()创建路径名为"/warehouse/T/write-"的节点。这是稍后在协议中使用的锁节点。确保设置了sequence和ephemeral标志。
在不设置监视标志的情况下在锁节点上调用getChildren()。
如果有一个子路径名以“read-”或“write-”开头,并且序列号比所获得的序列号更低,则无法获得锁。删除第一步中创建的节点并返回。
否则将授予锁。
提议的方案使作者缺乏读者。如果长时间阅读,可能会导致作者挨饿。
二.Hive 锁相关操作
2.1 Hive的并发性
如果要关闭Hive的并发操作,需要调整如下参数:
set hive.support.concurrency=false;
2.2 查看表的锁
当我们查询一个表卡住的时候,可以看下这个表上是否存在锁。
语法:
SHOW LOCKS <TABLE_NAME>;
SHOW LOCKS <TABLE_NAME> EXTENDED;
SHOW LOCKS <TABLE_NAME> PARTITION (<PARTITION_DESC>);
SHOW LOCKS <TABLE_NAME> PARTITION (<PARTITION_DESC>) EXTENDED;
我们在执行一个操作之前,不确定这个操作会有哪些锁,可以通过explain命令来查看
EXPLAIN LOCKS UPDATE target SET b = 1 WHERE p IN (SELECT t.q1 FROM source t WHERE t.a1=5)
输出如下:
LOCK INFORMATION:
default.source -> SHARED_READ
default.target.p=1/q=2 -> SHARED_READ
default.target.p=1/q=3 -> SHARED_READ
default.target.p=2/q=2 -> SHARED_READ
default.target.p=2/q=2 -> SHARED_WRITE
default.target.p=1/q=3 -> SHARED_WRITE
default.target.p=1/q=2 -> SHARED_WRITE
想要格式化输出,可以输入如下命令:
EXPLAIN FORMATTED LOCKS <sql>
2.3 解锁
unlock table 表名; -- 解锁表
unlock table 表名 partition(dt='2014-04-01'); -- 解锁某个分区
高版本hive默认插入数据时,不能查询,因为有锁
三.Hive 事务表锁机制
Hive事务中的锁机制是通过事务管理器来控制的
它包含了以前的概念“数据库/表/分区锁管理器”(hive.lock.manager)。默认为org.apache.hadoop.hive.ql.lockmgr.zookeeper.ZooKeeperHiveLockManager)。事务管理器现在还负责管理事务锁。默认的DummyTxnManager模拟旧Hive版本的行为:没有事务,使用hive.lock.manager。属性为表、分区和数据库创建锁管理器。新增加的DbTxnManager通过DbLockManager管理Hive metastore中的所有锁/事务(事务和锁在服务器故障时是持久的)。这意味着当事务被启用时,ZooKeeper中先前的锁定行为不再存在。为了避免客户端死亡并留下事务或锁悬空,会定期从锁持有者和事务启动器向metastore发送心跳。如果在配置的时间内没有接收到心跳,锁或事务将被中止。
从Hive 1.3.0开始,DbLockManger将继续尝试获取锁的时间长度可以通过hive.lock.numretires 和 hive.lock.sleep.between.retries来控制。当DbLockManager无法获得一个锁(由于存在一个竞争锁)时,它将后退,并在一段时间后再次尝试。为了支持短时间运行的查询,同时不会淹没metastore, DbLockManager会在每次重试后将等待时间加倍。初始回退时间是100ms,并由hive.lock.sleep.between.retries来限制。hive.lock。numretries是重试给定锁请求的总次数。因此,获取锁的调用被阻塞的总时间(给定100次重试和60秒睡眠时间)是(100ms + 200ms + 400ms +…+ 5120ms + 60s + 60s +…+ 60s) = 91m:42s:300ms。
关于此锁管理器使用的锁的更多细节。
注意,DbTxnManager使用的锁管理器将获取所有表上的锁,即使是那些没有“transactional=true”属性的表。默认情况下,插入到非事务性表中的操作将获得一个排他锁,从而阻塞其他插入和读取操作。虽然技术上是正确的,但这背离了Hive传统的工作方式(即不使用锁槽)。为了向后兼容,请使用hive.txn.strict.locking。提供的模式(见下表)将使这个锁管理器在非事务性表的插入操作上获得共享锁。这恢复了以前的语义,同时仍然提供了锁管理器的好处,比如在读取表时防止表被删除。请注意,对于事务性表,insert总是会获得共享锁,因为这些表在存储层实现了MVCC架构,并且能够提供强读一致性(快照隔离),即使存在并发的修改操作。
四.Hive 锁测试
在不同的客户端执行如下两个hive sql
select count() from ods_fact_sale ;
select count() from ods_fact_sale_partion;
可以看到此时有如下的锁,分区表 分区和锁都会有锁
hive> show locks;
OK
test@ods_fact_sale SHARED
test@ods_fact_sale_partion SHARED
test@ods_fact_sale_partion@sale_date=2010-04-12 SHARED
Time taken: 0.198 seconds, Fetched: 3 row(s)
hive>
> unlock table test.ods_fact_sale;
OK
Time taken: 0.182 seconds
hive> show locks;
OK
test@ods_fact_sale_partion SHARED
test@ods_fact_sale_partion@sale_date=2010-04-12 SHARED
Time taken: 0.036 seconds, Fetched: 2 row(s)
hive>
参考:
1.https://cwiki.apache.org/confluence/display/Hive/Locking
2.https://www.cnblogs.com/data-magnifier/p/14170173.html