随机化查询
默认情况下,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.0
和1.0
之间。默认值为0.3
。 -
--randomization-n
设置为每个操作生成的值对数量N
。默认值为5000
。 -
--randomization-alpha
设置alpha
参数,该参数控制Zipf
分布的扩散。该值应>=0
。较低的值会增加分布的扩散。默认值为1.0
。