使用指数退避有什么好处?

What is the benefit of using exponential backoff?

当代码在等待某个延迟时间不确定的条件时,貌似很多人选择使用指数退避,即等待N秒,检查条件是否满足;如果不是,请等待 2N 秒,检查条件等。在 constant/linearly 增加的时间跨度中进行检查有什么好处?

这是TCP拥塞控制的行为。如果网络极其拥塞,实际上没有流量通过。如果每个节点在检查之前等待一个恒定的时间,那么仅仅用于检查的流量将继续阻塞网络,并且拥塞永远不会解决。类似地,对于检查之间线性增加的时间,可能需要很长时间才能解决拥塞问题。

指数退避在同时尝试做某事会相互干扰从而 none 成功的情况下很有用。在这种情况下,让设备随机尝试 window 中的操作太小将导致大多数尝试失败并且必须重试。只有当 window 变得足够大时,尝试才有可能成功。

如果事先知道有 16 个设备想要进行通信,则可以 select window 的大小,这对于该负载级别是最佳的。但在实践中,竞争设备的数量通常是未知的。 window 大小在每次重试时加倍的指数退避的优点是 无论竞争实体的数量如何:

  1. 大多数操作成功的window大小通常在大多数操作成功的最小window大小的两倍之内,

  2. 大多数在 window 大小下失败的操作将在下一次尝试时成功(因为大多数较早的操作都会成功,所以只有不到一半的操作会竞争window 是原来的两倍)和

  3. 所有尝试所需的总时间最终只会是最后一次所需时间的两倍。

如果 window 不是每次都加倍,而是简单地增加一个常数,那么在 window 达到可用大小之前重试操作所花费的时间将与需要任何 window 大小的正方形。虽然最终的 window 大小可能比使用指数回退时使用的要小,但所有尝试的总成本会大得多。

假设您指的是在执行操作之前测试条件:

  • 当测试条件的成本与执行操作的成本相当时(例如在网络拥塞中),指数退避是有益的。
  • 如果测试条件的成本要小得多(或可以忽略不计),那么线性或恒定等待可以更好地工作,前提是条件更改所需的时间也可以忽略不计。

例如,如果您的条件是针对数据库的复杂(缓慢)查询,并且操作是同一数据库的更新,那么每次条件检查都会对数据库性能产生负面影响,并且在某些时候,没有指数退避,多个参与者检查条件可能足以使用所有数据库资源。

但如果条件只是一个轻量级的内存检查(f.i.一个临界区),动作仍然是一个数据库的更新(最多比检查慢千分之几) ,并且如果在操作开始时条件在可忽略的时间内翻转(通过进入临界区),那么常数或线性回退就可以了。实际上在这种特定情况下,指数退避将是有害的,因为它会在低负载情况下引入延迟,并且更有可能在高负载情况下导致超时(即使处理带宽足够)。

总而言之,指数退避是一把锤子:它对钉子很有效,对螺丝不太有效:)