一、什么是ACID?为什么要使用它
ACID代表数据库事务的四个特征:
1、原子性(一个操作要么完全成功,要么失败,它不会留下部分数据)、
2、一致性(一旦应用程序执行了一个操作,该操作的结果在随后的每个操作中都是可见的)、
3、隔离性(一个用户的不完整操作不会对其他用户造成意外的副作用)
4、持久性(一旦操作完成,即使面对机器或系统故障,它也会保留下来)。
这些特性长期以来一直被期望作为数据库系统事务功能的一部分。Hive 0.13,在分区级别提供了原子性、一致性和持久性,现在可以在行级别提供完整的ACID语义。
已将具有ACID语义的事务添加到配置单元中,以解决以下用例:
1、数据的流式摄取。许多用户使用诸如Apache Flume、Apache Storm或Apache Kafka之类的工具将数据流式传输到Hadoop集群中。虽然这些工具可以以每秒数百行或更多行的速度写入数据,但Hive只能每十五分钟到一小时添加一次分区。更频繁地添加分区会很快导致表中的分区数量过多。这些工具可以将数据流式传输到现有的分区中,但这会导致读取器读取不干净的数据(也就是说,他们会看到在开始查询后写入的数据),并在目录中留下许多小文件,这会给NameNode带来压力。有了这个新功能,这个用例将得到支持,同时允许读者获得一致的数据视图,并避免过多的文件
2、尺寸变化缓慢。在典型的星型模式数据仓库中,维度表随时间变化缓慢。例如,零售商将开设新的商店,这些商店需要添加到商店表中,或者现有商店可能会改变其平方英尺或其他一些跟踪特征。这些更改会导致插入单个记录或更新记录(取决于所选择的策略)。从0.14开始,Hive能够支持这一点。
3、数据重述。有时发现收集的数据不正确,需要更正。或者,数据的第一个实例可以是稍后提供的完整数据的近似值(90%的服务器报告)。或者,业务规则可能要求某些交易因后续交易而被重述(例如,在进行购买后,客户可能会购买会员资格,从而有权获得折扣价格,包括之前的购买)。或者,合同可能要求用户在终止关系时删除其客户的数据。从配置单元0.14开始,可以通过INSERT、UPDATE和DELETE来支持这些用例。
4、使用SQL MERGE语句进行大容量更新
二、局限性
1、BEGIN、COMMIT和ROLLBACK还不受支持。所有语言操作都是自动提交的
2、只有存储格式为ORC时可以确定更新或删除如何应用于基本记录(具有显式或隐式行id)
3、默认情况下,事务被配置为关闭,需要手动开启。
4、不允许从非ACID会话读取/写入ACID表。换句话说,Hive事务管理器必须设置为org.apache.hadoop.hive.ql.lockmgr.DbTxnManager,以便使用ACID表。
5、此时仅支持快照级别的隔离。当一个给定的查询启动时,它将被提供一个一致的数据快照。不支持脏读取、提交读取、可重复读取或可序列化。引入BEGIN的目的是在事务的持续时间内支持快照隔离,而不仅仅是一个查询。可以根据用户请求添加其他隔离级别。
6、现有的ZooKeeper和内存中的锁管理器与事务不兼容
7、使用Oracle作为Metastore DB和“datanucleus.connectionPoolingType=BONECP”可能会生成间歇性的“No such lock..”和“No such-transaction…”错误。在这种情况下,建议设置“datanucleus.connectionPoolingType=DBCP”。
8、事务表不支持 load data 语句
三、流式API
Hive提供用于流式数据摄取和流式突变的API:
1、Hive HCatalog Streaming API
此API适用于Flume和Storm等连续生成数据的流式客户端。流式处理支持建立在配置单元中基于ACID的插入/更新支持之上。Hive流API的类和接口部分大致分为两组。第一组提供对连接和事务管理的支持,而第二组提供I/O支持。事务由元存储管理。直接写入HDFS。
2、Hive Streaming API(hive3之后)
此API适用于NiFi、Flume和Storm等连续生成数据的流式客户端。流式处理支持建立在配置单元中基于ACID的插入/更新支持之上。Hive流API的类和接口部分大致分为两组。第一组提供对连接和事务管理的支持,而第二组提供I/O支持。事务由元存储管理。直接向表(HDFS、S3A等)定义的目标文件系统执行写入。
3、HCatalog Streaming Mutation API (hive2.0之后)
此API包括两个主要关注点:事务管理和向数据集写入变异操作。这两个问题具有最小的耦合,因为预计事务将从单个作业启动器类型的进程启动,而突变的写入将在任何数量的工作节点上扩展。
四、语法变化
可以使用INSERT...VALUES, UPDATE, 和 DELETE 语法
在Hive的DDL中添加了几个新命令,以支持ACID和事务,此外还修改了一些现有的DDL。
SHOW TRANSACTIONS 、 SHOW COMPACTIONS 和 ABORT TRANSACTIONS 是新命令
SHOW LOCKS命令已更改为提供有关与事务相关联的新锁的信息
ALTER TABLE中添加了一个新选项,用于请求压缩表或分区。可以使用SHOW COMPACTIONS查看压缩的进度
五、基础设计
HDFS不支持对文件进行就地更改。在写入程序附加到用户正在读取的文件时,它也不能提供读取一致性。为了在HDFS之上提供这些功能,我们遵循了其他数据仓库工具中使用的标准方法。表或分区的数据存储在一组基本文件中。新记录、更新和删除都存储在增量文件中。为更改表或分区的每个事务(或者在流式代理(如Flume或Storm)的情况下,为每批事务)创建一组新的增量文件。在读取时,读取器合并基本文件和增量文件,在读取时应用任何更新和删除。
基本目录和增量目录
以前,一个分区(如果表未分区,则为表)的所有文件都位于一个目录中。如果有了更改,使用ACID感知编写器编写的任何分区(或表)都将有一个基本文件目录和一个增量文件目录。以下是未分区表“t”的情况:
六、Compactor压缩者
Compactor是一组在Metastore中运行的后台进程,用于支持ACID系统。它由Initiator、Worker、Cleaner、AcidHouseKeeperService和其他几个部分组成。
1、增量文件压缩
随着操作修改表,会创建越来越多的增量文件,并且需要压缩这些文件以保持足够的性能。有三种类型的契约,次要契约、主要契约和再平衡契约。
1、次要压缩获取一组现有的增量文件,并将它们重写为每个bucket的单个增量文件。
2、主要压缩获取一个或多个增量文件和存储桶的基本文件,并将它们重写为每个存储桶的新基本文件。主要压缩成本更高,但更有效。
3、再平衡压缩:为了提高性能,Hive在后台创建bucket文件,即使是对于非显式的bucket表也是如此。根据使用情况,加载到这些非显式桶ORC表中的数据可能会导致分布不平衡,其中一些桶比其他桶大得多(>100倍)。不平衡的表会带来性能损失,因为较大的存储桶需要更多的读取时间。重新平衡压缩通过在隐式存储桶文件之间平等地重新分配数据来解决这个问题。
所有的压缩都是在后台完成的。次要和主要压缩不会阻止数据的并发读写。重新平衡压缩使用独占写锁,因此可以防止并发写操作。压缩后,系统等待,直到所有旧文件的读取器都完成,然后删除旧文件。
2、Initiator(发起人)
此模块负责发现哪些表或分区应进行压缩。这应该使用hive.compactor.ininitiator.on在元存储中启用。下面的“事务的新配置参数”表中有几个形式为*.threshold的属性,用于控制何时创建压缩任务以及执行哪种类型的压缩。每个压缩任务处理一个分区(如果表未分区,则处理整个表)。如果给定分区的连续压缩失败次数超过hive.compactor.ininitiator.failed.compacts.threshold,则此分区的自动压缩调度将停止。
3、Worker
每个Worker处理一个压缩任务。压缩是一个MapReduce作业,其名称格式如下: <hostname>-compactor-<db>.<table>.<partition>。每个Worker都将作业提交到集群(如果已定义,则通过hive.compactor.job.queue),并等待作业完成。hive.compactor.worker.threads确定每个元存储中worker的数量。Hive Warehouse中的Workers总数决定了并发契约的最大数量。
4、Cleaner
这个过程是一个在压缩后以及确定不再需要增量文件后删除增量文件的过程。
5、AcidHouseKeeperService
此过程查找在hive.txn.timeout时间内未检测到心跳的事务并中止它们。系统假设启动事务的客户端停止心跳崩溃,并且应该释放其锁定的资源。
6、SHOW COMPACTIONS
此命令显示有关当前运行的压缩和压缩的最近历史记录(可配置的保留期)的信息
七、事务管理器
添加了一个名为“事务管理器”的新逻辑实体,它包含了以前的“数据库/表/分区锁管理器”概念(hive.lock.manager,默认值为org.apache.hadop.hive.ql.lockmgr.zookeeper.ZooKeeperHiveLockManager)。事务管理器现在还负责管理事务锁。默认的DummyTxnManager模拟旧配置单元版本的行为:没有事务,并使用Hive.lock.manager属性为表、分区和数据库创建锁管理器。新添加的DbTxnManager使用DbLockManager管理配置单元元存储中的所有锁/事务(事务和锁在服务器故障时是持久的)。这意味着当启用事务时,ZooKeeper中以前的锁定行为将不再存在。为了避免客户端死亡并使事务或锁悬空,会定期从锁持有者和事务启动器向元存储发送心跳。如果在配置的时间内未接收到检测信号,则锁定或事务将中止。
从配置单元1.3.0开始,DbLockManger将继续尝试获取锁的时间长度可以通过Hive.lock.numretires和Hive.lock.sleep.between.retries进行控制。当DbLockManager无法获取锁(由于存在竞争锁)时,它将退出并在一段时间后重试。为了支持短时间运行的查询,同时又不会淹没元存储,DbLockManager将在每次重试后将等待时间增加一倍。初始回退时间为100ms,上限为hive.lock.sleep.between.retries。hive.lock.numretries是它将重试给定锁定请求的总次数。因此,获取锁的调用将被阻止的总时间(给定100次重试和60秒睡眠时间的值)为(100ms+200ms+400ms+…+51200ms+60s+60s+…+60s)=91m:42s:30ms。
请注意,DbTxnManager使用的锁管理器将获取所有表的锁,即使是那些没有“transactional=true”属性的表。默认情况下,插入到非事务性表中的操作将获得独占锁,从而阻止其他插入和读取。虽然技术上是正确的,但这与Hive传统的工作方式(即没有锁管理器)有所不同。为了向后兼容,提供了hive.txn.strict.locking.mode,这将使该锁管理器在非事务表的插入操作中获得共享锁。这恢复了以前的语义,同时仍然提供了锁管理器的好处,例如在读取表时防止表丢失。请注意,对于事务表,insert总是获取共享锁,因为这些表在存储层实现MVCC体系结构,并且即使在存在并发修改操作的情况下也能够提供强大的读取一致性(快照隔离)。
配置
至少,必须适当设置这些配置参数才能在配置单元中启用事务支持:
Client 侧
hive.support.concurrency – true
hive.enforce.bucketing – true (Not required as of Hive 2.0)
hive.exec.dynamic.partition.mode – nonstrict
hive.txn.manager – org.apache.hadoop.hive.ql.lockmgr.DbTxnManager
Server 侧(Metastore)
hive.compactor.initiator.on – true
hive.compactor.cleaner.on – true
hive.compactor.worker.threads – Thrift元存储服务的至少一个实例上的正数