# Kudu Transaction Semantics ( 事务语义 )

原文链接 : [http://kudu.apache.org/docs/transaction_semantics.html](http://kudu.apache.org/docs/transaction_semantics.html)

译文链接 : [http://cwiki.apachecn.org/pages/viewpage.action?pageId=10813638](http://cwiki.apachecn.org/pages/viewpage.action?pageId=10813638)

贡献者 : [小瑶](/display/~chenyao) [ApacheCN](/display/~apachecn) [Apache中文网](/display/~apachechina)

## Apache Kudu 的 Transaction Semantics ( 事务语义 )

这是 **Kudu** 的 交易和一致性语义的简要介绍。对于这里提到的大部分内容的深入技术论述，为什么是正确的，请参阅技术报告[[1]](/pages/viewpage.action?pageId=10813638)。

**Kudu** 的  **transactional semantics** ( 事务语义 )和体系结构受到 **Spanner** [[2]](/pages/viewpage.action?pageId=10813638) 和 **Calvin** [[3]](/pages/viewpage.action?pageId=10813638) 等最先进的系统的启发。 **Kudu** 建立在数十年的数据库研究基础之上。核心理念是通过提供简单，强大的语义的事务，使开发人员的生活更轻松，而不会牺牲性能或调整到不同需求的能力。

**Kudu** 旨在最终完全获得 **ACID** ，但是多 **tablet** 事务尚未实施。因此，本次讨论的重点是单片写入操作，只是简单地触摸多 **tablets** 读取。最终 **Kudu** 将支持完全严格的可序列化语义。事实上，它已经在有限的范围内，但并不是所有的角落都被覆盖，因为这仍然是一个正在进行的工作。

**Kudu** 目前允许以下操作：

*   **Write operations** ( 写入操作 ) 是在具有多个副本的单个 **tablet** 中在存储引擎中插入，更新或删除的 **sets of rows **( 行集 ) 。写操作没有单独的 **“read sets”** ，即它们在执行写入之前不扫描现有数据。每个写入仅涉及即将更改的行的先前状态。用户明确不写 **“committed”** 。相反，它们将在系统完成后自动提交。

*   **Scans** ( 扫描 ) 是可以遍历多个 **tablets** 并读取信息的读取操作，具有一致性或正确性保证。扫描可以执行时间行进读取，即用户能够设置过去的扫描时间戳，并返回在该时间点反映存储引擎的状态的结果。

重要

**开始之前**

*   多次提到术语时间戳来说明功能，但是时间戳是用户最不可见的内部概念，除了在 **KuduScanner** 上设置时间戳。
*   我们通常指的是 **C ++  client** 的方法和类。虽然 **Java** 客户端大多具有类似的方法和类，但是 **API** 的确切名称可能不同。

## Single tablet write operations ( 单 tablet 写入操作 )

**Kudu** 采用 **Multiverion** 并发控制 （**MVCC**） 和 Raft 一致性算法 [[4]](/pages/viewpage.action?pageId=10813638) 。**Kudu** 的每个写入操作必须经过 tablet 的领导。

1.  leader 将为其将更改的行获取所有锁。
2.  leader 在提交写入复制之前分配写入时间戳。这个时间戳是 **MVCC** 中的 **write** 的 **“tag”** 。
3.  在大多数副本确认更改后，实际的行将被更改。
4.  更改完成后，它们可以以原子方式同时进行写入和读取。

**tablets** 的所有副本都遵循相同的操作顺序，如果为写入操作分配时间戳 **n** 并更改 行 **x** ，则保证时间戳 **m &gt;  n** 的第二次写入操作可以看到 x 的新值。

锁定采集和时间戳分配的这种严格排序通过协商一致的方式强制执行 **tablets** 的所有副本。因此，相对于同一个平板电脑中的其他写入，关于时钟分配的时间戳，写入操作是完全排序的。换句话说，写入具有严格的可序列化语义，尽管在一个公认的有限的上下文中。有关这些语义是什么意思，请参阅这篇 [博客文章](http://www.bailis.org/blog/linearizability-versus-serializability) 。

虽然在 **ACID** 意义上的隔离和耐用性，多行写入操作尚未完全原子化。批量操作中单次写入的故障不会回滚操作，而是产生每行错误。

## Writing to multiple tablets ( 写入多个 tablets )

**Kudu** 还不支持跨多个 **tablets** 的事务。但是，一致的快照读取是可能的（如当前实现中的警告），如下所述。

**Kudu** 客户端的写入可选地缓冲在内存中，直到它们被刷新并发送到服务器。当客户端的会话刷新时，每个 **tablet** 的行被批处理在一起，并发送到托管 **tablet** 的 **leader replica** 的 **tablet servers** 。由于没有 **inter-tablet** 事务，这些批次中的每一个代表具有自己的时间戳的单独的独立写入操作。然而，客户端 **API** 提供了对分配的时间戳施加一些约束的选项，以及客户端如何观察对不同 **tablet** 的写入。

**Kudu** 像 **Spanner** 一样被设计为外部一致 [[5]](/pages/viewpage.action?pageId=10813638) ，即使在跨多个平板电脑甚至多个数据中心的情况下也能保持一致性。在实践中，这意味着如果写入操作在平板电脑 **A** 上更改项目 **x** ，并且以下写入操作在平板电脑 **B** 上更改项目 **y** ，则可能需要强制执行，如果观察到 **y** 的更改，则 **x** 的更改也必须为观察到的。有很多例子可以说是重要的。例如，如果 **Kudu** 正在存储用于进一步分析的点击流，并且两次点击相互关联，但存储在不同的 **tablet** 中，则后续点击应该被分配后续时间戳，以便捕获它们之间的因果关系。

**CLIENT_PROPAGATED Consistency**

**Kudu** 的默认外部一致性模式称为 **CLIENT_PROPAGATED** 。有关如何工作的详细说明，请参阅 [[1]](/pages/viewpage.action?pageId=10813638) 。简而言之，此模式会导致单个客户端的写入自动在外部保持一致。在上述 **Clickstream** 场景中，如果两个点击是由不同的客户端实例提交的，则应用程序必须手动将时间戳从一个客户端传播到另一个客户端，以便捕获因果关系。

客户端 **a** 和 **b** 之间的时间戳可以传播如下：

**Java Client**

调用 **AsyncKuduClient#getLastPropagatedTimestamp()** 在客户端 **a** ，将时间戳传播到客户端 **b** ，并在客户端 **b** 上调用**AsyncKuduClient#setLastPropagatedTimestamp()** 。

**C++ Client**

在客户端 **a** 上调用**KuduClient::GetLatestObservedTimestamp()** ，将时间戳传播到客户端 **b** ，并在客户端 **b** 上调用 **KuduClient::SetLatestObservedTimestamp()** 。

**`COMMIT_WAIT` Consistency**

**Kudu** 还有一个在 **Google** 的 **Spanner** 中使用的外部一致性模型的实验实现，称为 **COMMIT_WAIT** 。  **COMMIT_WAIT** 通过在集群中的所有计算机上紧密同步时钟来起作用。然后，当发生写入时，分配时间戳，并且在通过足够的时间之前写入的结果不可见，使得集群中的其他计算机不可能为下一次写入分配较低的时间戳。

当使用此模式时，写入的延迟与所有集群主机上的时钟精度紧密相关，并且使用松散时钟同步的此模式会导致写入需要很长时间才能完成甚至超时。请参阅 [已知问题和限制](/pages/viewpage.action?pageId=10813638) 。

**COMMIT_WAIT**一致性模式可以如下选择：

**Java Client**

调用 **KuduSession#setExternalConsistencyMode(ExternalConsistencyMode.COMMIT_WAIT)**

**C++ Client**

调用 **KuduSession :: SetExternalConsistencyMode(COMMIT_WAIT)**

注意

**COMMIT_WAIT** 一致性被认为是一个实验功能。它可能会返回不正确的结果，展示性能问题或对集群稳定性产生负面影响。不鼓励在生产环境中使用。

## Read Operations (Scans) ( 阅读操作(扫描) )

扫描是由可能跨越一个或多个 **tablets** 跨越一行或多行的客户端执行的读取操作。当服务器接收到扫描请求时，会收到 **MVCC** 状态的快照，然后根据用户选择的读取模式，以两种方式之一进行。该模式可以如下选择：

**Java Client**

调用 **KuduScannerBuilder#setReadMode(...)**

**C++ Client**

调用 **KuduScanner :: SetReadMode()**

以下模式在两个客户端都可用：

**READ_LATEST**

这是默认的读取模式。服务器拍摄 **MVCC** 状态的快照，并立即继续阅读。在此模式下读取只会产生 **“Read Committed”** 隔离。

**READ_AT_SNAPSHOT**

在这种读取模式下，扫描是一致的和可重复的。快照的时间戳由服务器选择，或由用户通过 **KuduScanner :: SetSnapshotMicros()** 显式设置。建议明确设置时间戳;见 [**Recommendations** ( 建议书 )](/pages/viewpage.action?pageId=10813638) 。服务器等待，直到这个时间戳为 **“safe”** （直到所有具有较低时间戳的写入操作都已完成并可见）。这种延迟加上外部一致性方法，最终将允许 **Kudu** 具有完全严格可序列化的读写语义。这仍然是一个正在进行的工作，一些异常仍然是可能的（请参阅 [已知问题和限制](/pages/viewpage.action?pageId=10813638) ）。只有在这种模式下进行扫描可以是容错的。

在读取模式之间进行选择需要平衡权衡并做出适合您工作负载的选择。 例如，需要扫描整个数据库的报告应用程序可能需要执行仔细的计费操作，以便扫描可能需要容错，但可能不需要一个微微小的最新视图 的数据库。 在这种情况下，您可以选择 **READ_AT_SNAPSHOT** ，并选择扫描开始时过去几秒的时间戳。 另一方面，不吸收整个数据集并且已经具有统计性质的机器学习工作负载可能不要求扫描是可重复的，因此您可以选择 **READ_LATEST** 。

## Known Issues and Limitations ( 已知问题和限制 )

目前，在一些情况下，**Kudu** 已经完全严格序列化，存在几个缺陷和角落。以下是详细信息，接下来是一些建议。

### Reads (Scans) ( 阅读（扫描） )

*   对 **COMMIT_WAIT** 的支持是实验性的，需要仔细调整时间同步协议，如 **NTP** （网络时间协议）。在生产环境中不鼓励使用它。

### Writes ( 写入 )

*   在 **leader change** 中，**READ_AT_SNAPSHOT** 扫描时间戳超出最后一次写入的快照，也可能会产生不可重复的读取（参见 **[KUDU-1188](https://issues.apache.org/jira/browse/KUDU-1188)** ）。请参阅 有关解决方法的[建议](/pages/viewpage.action?pageId=10813638)。
*   **Impala** 扫描当前以 **READ_LATEST** 的形式执行，并且没有一致性保证。
*   在 **AUTO_BACKGROUND_FLUSH** 模式下，或者使用 “异步” 刷新机制时，应用于单个客户端会话的写入可能由于将数据刷新到服务器的并发而重新排序。如果用不同的值连续快速更新单个行，这可能会特别明显。这种现象影响所有的客户端 API 实现。解决方法在 API 文档中介绍了 **FlushMode** 或 **AsyncKuduSession** 文档中各自的实现。见** [KUDU-1767](https://issues.apache.org/jira/browse/KUDU-1767)** 。

### Recommendations  ( 建议 )

*   如果可重复的快照读取是必需的，请使用 **READ_AT_SNAPSHOT** ，其时间戳稍微在过去（在 **2-5** 秒之间，理想情况下）。这将绕过 **Reads（Scans）** 中描述的异常。即使异常已被解决，回溯时间戳总是使扫描速度更快，因为它们不太可能被阻止。
*   如果需要外部一致性，并且您决定使用 **COMMIT_WAIT** ，则需要仔细调整时间同步协议。每个事务将在执行时等待 **2x** 最大时钟错误，通常为 **100** 毫秒至 **1** 秒范围与默认设置，也许更多。因此，交易将需要至少 **200** 毫秒至 **2** 秒使用默认设置完成甚至可能会超时。

* 本地服务器应该用作时间服务器。我们使用 Google Compute Engine 数据中心提供的默认 **NTP** 时间源进行实验，并能够获得合理的最大误差范围，通常在 **12-17** 毫秒之间变化。

* 应在 **/etc/ntp.conf** 中调整以下参数以收紧最大错误：

*   *   *   服务器 my_server.org iburst minpoll 1 maxpoll 8

        *   tinker dispersion 500

        *   tinker allan 0

信息

上述参数以估计误差为代价最小化最大误差，后者可能高于 **“normal”** 值的数量级。这些参数也可能对时间服务器造成更大的负担，因为它们使得服务器的轮询频率更高。

## References ( 参考 )

*   [1] David Alves, Todd Lipcon and Vijay Garg. Technical Report: HybridTime - Accessible Global Consistency with High Clock Uncertainty. April, 2014. [http://users.ece.utexas.edu/~garg/pdslab/david/hybrid-time-tech-report-01.pdf](http://users.ece.utexas.edu/~garg/pdslab/david/hybrid-time-tech-report-01.pdf)
*   [2] James C. Corbett, Jeffrey Dean, Michael Epstein, Andrew Fikes, Christopher Frost, J. J. Furman, Sanjay Ghemawat, Andrey Gubarev, Christopher Heiser, Peter Hochschild, Wilson Hsieh, Sebastian Kanthak, Eugene Kogan, Hongyi Li, Alexander Lloyd, Sergey Melnik, David Mwaura, David Nagle, Sean Quinlan, Rajesh Rao, Lindsay Rolig, Yasushi Saito, Michal Szymaniak, Christopher Taylor, Ruth Wang, and Dale Woodford. 2012\. Spanner: Google’s globally-distributed database. In Proceedings of the 10th USENIX conference on Operating Systems Design and Implementation (OSDI'12). USENIX Association, Berkeley, CA, USA, 251-264.
*   [3] Alexander Thomson, Thaddeus Diamond, Shu-Chun Weng, Kun Ren, Philip Shao, and Daniel J. Abadi. 2012\. Calvin: fast distributed transactions for partitioned database systems. In Proceedings of the 2012 ACM SIGMOD International Conference on Management of Data (SIGMOD '12). ACM, New York, NY, USA, 1-12\. DOI=10.1145/2213836.2213838 [http://doi.acm.org/10.1145/2213836.2213838](http://doi.acm.org/10.1145/2213836.2213838)
*   [4] Diego Ongaro and John Ousterhout. 2014\. In search of an understandable consensus algorithm. In Proceedings of the 2014 USENIX conference on USENIX Annual Technical Conference (USENIX ATC'14), Garth Gibson and Nickolai Zeldovich (Eds.). USENIX Association, Berkeley, CA, USA, 305-320.
*   [5] Kwei-Jay Lin, "Consistency issues in real-time database systems," in System Sciences, 1989\. Vol.II: Software Track, Proceedings of the Twenty-Second Annual Hawaii International Conference on , vol.2, no., pp.654-661 vol.2, 3-6 Jan 1989 doi: 10.1109/HICSS.1989.48069