# OpenZFS 事务延迟

当后端存储无法容纳传入写入的速率时，OpenZFS 写操作会被延迟。此延迟过程称为 OpenZFS 写入节流。由于不同硬件和工作负载具有不同的性能特性，需针对具体硬件和工作负载进行写入节流的调优。

写入被分组为事务。事务被分组为事务组。当一个事务组同步到磁盘时，该组中的所有事务都被视为完成。当对某个事务应用延迟时，会延迟该事务被分配到事务组。

要检查某个存储池是否启用了写入节流，可监控计数器 kstat `dmu_tx_delay`、 `dmu_tx_dirty_delay`。

如果已经存在一个等待中的写事务，则延迟相对于该事务完成等待的时间。因此，计算得到的延迟时间与并发执行事务的线程数量无关。

如果只有一个等待者，则延迟相对于事务开始的时间，而不是当前时间。这会为该事务计入“已服役时间”。例如，如果一个写事务需要先读取间接块，那么延迟会从事务开始时计入，也就是在读取间接块之前。

事务所需的最小时间计算如下：

```
min_time = zfs_delay_scale * (dirty - min) / (max - dirty)
min_time 随后被限制在 100 毫秒以内
```

延迟具有两个可通过可调参数进行调整的自由度：

1. 开始延迟的脏数据百分比由 `zfs_delay_min_dirty_percent` 定义。该值通常应设置为大于或等于 `zfs_vdev_async_write_active_max_dirty_percent`，以便在全速写入无法跟上传入写入速率之后才发生延迟。
2. 曲线的尺度由 `zfs_delay_scale` 定义。粗略而言，该变量决定曲线中点处的延迟量。

```
延迟
 10ms +-------------------------------------------------------------*+
      |                                                             *|
  9ms +                                                             *+
      |                                                             *|
  8ms +                                                             *+
      |                                                            * |
  7ms +                                                            * +
      |                                                            * |
  6ms +                                                            * +
      |                                                            * |
  5ms +                                                           *  +
      |                                                           *  |
  4ms +                                                           *  +
      |                                                           *  |
  3ms +                                                          *   +
      |                                                          *   |
  2ms +                                              (midpoint) *    +
      |                                                  |    **     |
  1ms +                                                  v ***       +
      |             zfs_delay_scale ---------->     ********         |
    0 +-------------------------------------*********----------------+
      0%                    <- zfs_dirty_data_max ->               100%
```

请注意，由于延迟被加到最近事务剩余的未完成时间上，因此延迟实际上是 IOPS 的倒数。在此，中点 500 微秒对应 2000 IOPS。曲线的形状被设计为：在曲线前四分之三的区间内，累积脏数据量的小幅变化只会导致延迟量的相对小幅变化。

当以对数尺度表示延迟量时，其效果更易理解：

```
延迟
100ms +-------------------------------------------------------------++
      +                                                              +
      |                                                              |
      +                                                             *+
 10ms +                                                             *+
      +                                                           ** +
      |                                              (midpoint)  **  |
      +                                                  |     **    +
  1ms +                                                  v ****      +
      +             zfs_delay_scale ---------->        *****         +
      |                                             ****             |
      +                                          ****                +
100us +                                        **                    +
      +                                       *                      +
      |                                      *                       |
      +                                     *                        +
 10us +                                     *                        +
      +                                                              +
      |                                                              |
      +                                                              +
      +--------------------------------------------------------------+
      0%                    <- zfs_dirty_data_max ->               100%
```

请注意，只有当脏数据量接近其上限时，延迟才会开始迅速增加。经过正确调优的系统目标应当是将脏数据量保持在该区间之外：首先确保为 I/O 调度器设置合适的限制，使其在后端存储上达到最佳吞吐量；然后通过调整 `zfs_delay_scale` 的值来增加曲线的陡峭程度。

参考代码： [dmu\_tx.c](https://github.com/openzfs/zfs/blob/master/module/zfs/dmu_tx.c#L866)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://book.bsdcn.org/zfs/xing-neng-tiao-you/transaction-delay.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
