Hive学习之Locking

众所周知,数据库必须要能够支持并发。无论在任何时候,允许同一时刻,多个用户能够同时读取或写入。没有必要给用户提供API显示的获取锁,所以所有的锁都是隐式获取的。

在Hive中有两种类型的锁:

  • 共享锁Shared(S)
  • 排它锁Exclusive(X)

就如它们的名字一样,可以在同一时刻,获取多个共享锁,但是只能获取一个排它锁。

对于一些操作,锁还是有层次的。例如对于一些分区操作,表也会被锁住,以确保当一个新的分区创建的时候,表不会被drop掉。

锁操作遵循如下规则:

对于非分区表,锁规则是非常直观的。如果需要读取一个表,就加S锁,其它情况都需要加X锁。

对于分区表,规则如下:

当读取某一个分区上的数据时,需要给这个分区添加S锁,对这个分区的其它操作则需要加X锁。然而,如果操作某个分区仅影响到该分区,则需要在该表上添加S锁,如果影响到所有分区,则需要在该表上添加X锁。因此,如果一个分区正在被转换成RCFile,仍然可以读取或写入其它分区,无论一个分区正在加什么类型的锁,它的父级都会加S锁。

基于以上规则,列举一些HQL加锁情况,如下:

HQL
select .. T1 partition P1 S on T1,T1.P1
insert into T2(partition P2) select.. T1 partition P1 S on T2,T1.p1 和 X on T2.P2
insert into T2(partition P.Q) select.. T1 partition P1 S on T2,T2.P,T1,T1.P1和X on T2.P.Q
alter table T1 rename T2 X on T1
alter table T1 add cols X on T1
alter table T1 replace cols X on T1
alter table T1 change cols X on T1
alter table T1 concatenate X on T1
alter table T1 add partition P1 S on T1,X on T1.P1
alter table T1 drop partition P1 S on T1,X on T1.P1
alter table T1 touch partition P1 S on T1,X on T1.P1
alter table T1 set serdeproperties S on T1
alter table T1 set serializer S on T1
alter table T1 set file format S on T1
alter table T1 set tblproperties X onT1
alter table T1 partition P1 concatenate X on T1.P1
drop table X on T1

为了避免死锁,Hive使用了一个简单的规则,对所有即将加锁的对象按照字典顺序排列,然后按照顺序加上相应的锁。注意,在某些情况下,对象的列表是不确定的。例如,一些动态分区的情况下,将要修改的分区的列表在编译时候不能确定。所以,生成的列表是有保留的。既然分区的数量不确定,那么就应该给表(目前并没有,见HIVE-3509 Bug)或能确定的前缀添加排它锁。

可以通过两个参数配置加锁失败重试的次数和每次重试之间的时间间隔。如果重试的次数非常高,将会导致活锁的出现。可以参看Zookeeper了解如何使用zookeeper的api实现读写锁。需要注意的事,如果没有得到锁,加锁的请求将会被拒绝,已经存在的锁将会被释放,所有的锁都会有重试间隔后再次请求加锁。

由于锁具有层级特性,所以并不总是按照上表中的规则进行加锁。

给表添加S锁的规则如下:

  • 调用create()方法创建一个路径名是"/warehouse/T/read-"的节点,这是一个锁节点,将会在后面使用。要确保设置sequence和ephemeral 标识。
  • 调用这个锁节点的getChildren()方法,不设置watch标识
  • 如果有一个子节点,路径名以"write-"开头,并且序列号比自己获取的要小,那么就不能取得锁,并删除在第一步创建的节点然后返回。
  • 否则获取到锁。

给表添加X锁的规则如下:

  • 调用create()方法创建一个路径名是"/warehouse/T/write-"的节点,这是一个锁节点,将会在后面使用。要确保设置sequence和ephemeral 标识。
  • 调用这个锁节点的getChildren()方法,不设置watch标识
  • 如果有一个子节点,路径名以"read-"或"write-"开头,并且序列号比自己获取的要小,那么就不能取得锁,并删除在第一步创建的节点然后返回。
  • 否则获取到锁。

推荐的原则是读优先,如果读取需要花很长时间,将会导致"写操作"的长时间"饥饿"。

Hive默认情况下不支持并发,如果想支持并发,需要将参数: hive.support.concurrency设置为true.

如果出现锁问题,可以使用如下命令调试:

SHOW LOCKS <TABLE_NAME>;

SHOW LOCKS <TABLE_NAME> EXTENDED;

SHOW LOCKS <TABLE_NAME> PARTITION(<PARTITION_DESC>);

SHOW LOCKS <TABLE_NAME> PARTITION(<PARTITION_DESC>) EXTENDED;

上一篇:【iOS开发】在ARC项目中使用非ARC文件


下一篇:Linux系统 vi/vim文本编辑器