Link Search Menu Expand Document Documentation Menu

随机化查询

默认情况下,OpenSearch Benchmark 会对多次基准测试迭代运行相同的查询。然而,在所有测试场景中重复运行相同的查询并不理想。例如,对相同查询进行多次迭代来模拟真实世界的缓存,会导致一次缓存未命中,随后是多次命中。OpenSearch Benchmark 允许您以可配置的方式随机化查询。

例如,在以下 nyc_taxis 操作中更改 "gte""lt" 会创建不同的查询,从而产生唯一的缓存条目

{
    "name": "range",
    "operation-type": "search",
    "body": {
    "query": {
        "range": {
        "total_amount": {
            "gte": 5,
            "lte": 15
        }
        }
    }
    }
}

您不能完全随机化这些值,因为缓存将不会获得任何命中。为了获得缓存命中,缓存有时必须遇到相同的值。为了在随机化时考虑相同的值,OpenSearch Benchmark 在基准测试开始时为每个随机操作生成 \(N\) 对值。OpenSearch Benchmark 将这些值存储在一个已保存的列表中,其中每对值都被分配了一个从 \(1\) 到 \(N\) 的索引。

每当 OpenSearch 发送查询时,OpenSearch Benchmark 都会决定是否在查询中使用此已保存列表中的一对值。它会在可配置的时间比例下执行此操作,这称为重复频率 (rf)。如果 OpenSearch 之前遇到过该值对,这可能会导致缓存命中。例如,如果 rf = 0.7,则缓存命中率可能高达 70%。此比率可能会导致命中,具体取决于基准测试的持续时间和缓存大小。

OpenSearch Benchmark 使用 Zipf 概率分布选择已保存的值对,其中选择对 \(i\) 的概率与 \(1 \over i^\alpha\) 成正比。在此公式中,\(i\) 表示已保存值对的索引,\(\alpha\) 控制分布的集中程度。此分布反映了在真实缓存中观察到的使用模式。具有较低 \(i\) 值(更接近 \(1\))的对被更频繁地选择,而具有较高 \(i\) 值(更接近 \(N\))的对被选择的频率较低。

在剩余的 \(1 -\) rf 时间内,将生成一对新的随机值。由于 OpenSearch Benchmark 之前没有遇到过这些值对,因此这些值对应该会未命中缓存。

用法

要在工作负载中使用此功能,您必须对 workload.py 进行一些更改,并在运行 OpenSearch Benchmark 时提供一些 CLI 标志。

修改 workload.py

通过为每个操作注册一个“标准值源”来指定如何生成该操作的已保存值对。此 Python 函数不接受任何参数并返回一个字典。键与输入查询中的键相同,但已随机化。最后,更改 register() 方法,使其将此函数与随机化的操作名称和字段名称一起注册。

例如,用于随机化前述 "range" 操作中 "total_amount" 字段的标准值源可能类似于以下函数

def random_money_values(max_value):
    gte_cents = random.randrange(0, max_value*100)
    lte_cents = random.randrange(gte_cents, max_value*100)
    return {
        "gte":gte_cents/100,
        "lte":lte_cents/100
    }

def range_query_standard_value_source():
    return random_money_values(120.00)

同样,您可以使用以下函数随机化注册行为

def register(registry):
    registry.register_standard_value_source("range", "total_amount", range_query_standard_value_source)

此函数可能已包含代码。如果是,请保留它。如果 workload.py 不存在或缺少 register(registry) 函数,您可以创建它们。

随机化非范围查询

默认情况下,OpenSearch Benchmark 假定要随机化的查询是 "range" 查询,其值包括 "gte"/"gt""lte"/"lt",以及可选的 "format"。如果情况并非如此,您可以将其配置为使用不同的查询类型名称和不同的值。

例如,要随机化以下工作负载操作

{
  "name": "bbox", 
  "operation-type": "search", 
  "index": "nyc_taxis",
  "body": { 
    "size": 0,
    "query": {
      "geo_bounding_box": {
        "pickup_location": {
          "top_left": [-74.27, 40.92],
          "bottom_right": [-73.68, 40.49]
        }
      }
    }
  }
}

您将在 workload.py 中注册以下函数

registry.register_query_randomization_info("bbox", "geo_bounding_box", [["top_left"], ["bottom_right"]], [])

第一个参数 "bbox" 是操作的名称。

第二个参数 "geo_bounding_box" 是查询类型名称。

第三个参数是一个列表的列表:[[“top_left”], [“bottom_right”]]。外部列表的条目指定了随机化参数,因为可能存在相同名称的不同版本,它们大致表示相同的参数,例如 "gte""gt"。在这里,每个参数名称只有一个选项。原始查询中必须至少存在每个参数名称的一个版本才能进行随机化。

最后一个参数是一个可选参数列表。如果随机标准值源中存在可选参数,OpenSearch Benchmark 会将该参数插入到查询的随机化版本中。如果不在源中,则忽略。以下示例中没有可选参数,但典型用例是范围查询中的 "format"

如果没有注册,则使用默认注册:registry.register_query_randomization_info(<operation_name>, “range”, [[“gte”, “gt”], [“lte”, “lt”]], [“format”])

标准值源返回的 dict 应该与您要随机化的参数名称匹配。例如,以下是前述示例的标准值源

def bounding_box_source(): 
    top_longitude = random.uniform(-74.27, -73.68)
    top_latitude = random.uniform(40.49, 40.92)

    bottom_longitude = random.uniform(top_longitude, -73.68)
    bottom_latitude = random.uniform(40.49, top_latitude)

    return { 
        "top_left":[top_longitude, top_latitude],
        "bottom_right":[bottom_longitude, bottom_latitude]
    }

CLI 标志

使用以下 CLI 标志自定义随机化

  • --randomization-enabled 用于开启和关闭随机化。如果未启用随机化,则不会应用任何随机化标志。

  • --randomization-repeat-frequency-rf 设置从基准测试开始时生成的已保存值对中抽取的对的比例。该值应介于 0.01.0 之间。默认值为 0.3

  • --randomization-n 设置为每个操作生成的值对数量 N。默认值为 5000

  • --randomization-alpha 设置 alpha 参数,该参数控制 Zipf 分布的扩散。该值应 >=0。较低的值会增加分布的扩散。默认值为 1.0

剩余 350 字符

有问题?

想贡献?