Ting Chen
03/30/2023, 11:31 PMSELECT * FROM tableA WHERE
text_match(
"json_data", '"instance*50331649"'
)
AND json_extract_scalar(
"json_data", '$.instance', 'INT',
0
) = 50331649
For queries with a boolean condition for which one side has index (e.g., text index) and the other side is costly matching. Does Pinot allow filtering using the text index first before evaluating the more costly json function? Looks like today we do not evaluation in parallel (using EXPLAIN PLAN). Note that we can not build json index on the column due to large json index size.Kishore G
Kishore G
Ting Chen
03/31/2023, 12:01 AMKishore G
Ting Chen
03/31/2023, 12:16 AMJackie
03/31/2023, 12:53 AMJack Luo
03/31/2023, 12:55 AMfor json index, we can not build it only for specific paths.What Ting meant is that although we can specify which paths to build json index in the table-config, in practice, because there's 8000+ tables with unique json column content, it's not possible to manually customize which specific paths we want to include or exclude. We can't use json-index in production due to the huge space overhead and high heap memory usage. JSON index takes significant % of the the entire segment size. We have already filtered out json fields deeper than 3 levels, ignored arrays. For example: A table with 8 table columns with data (7 non-json columns, 1 json column). We picked a random segment to inspect its data size. The segment (columns.psf file) is 158MB and when inspecting the
index_map file , json index took 98% of the segment. In contrast, if we perform text-index on the same json column, the text-index consumes 12MB.Jack Luo
03/31/2023, 12:57 AMjson column field is the following:
{
"cluster": "dca11-stateless01",
"duration_in_sec": 604800,
"instance": "50331648",
"need_raw_data": "false",
"build_time": "2023-02-15T15:17:01Z",
"sgroupby_attributes": null,
"language": "go",
"source": "/opt/uber/mesos-agent/udocker-log-links/sawmill-query/jaeger/compute-0/50331648/thermos-7e71262a-97cf-4819-97cc-39c27887cd32-0-9/stdlogs/stdout",
"type": "log",
"clickhouse_query": "WITH if(indexOf(\"string.names.17\", 'process.service_name') > 0, \"string.values.17\"[indexOf(\"string.names.17\", 'process.service_name')], NULL) AS \"process.service_name\" SELECT DISTINCT `process.service_name` FROM \"default\".\"Distributed.jaeger-service-operation-index-staging.20221213cecd1qrhjh5vp8fuq2ng\" PREWHERE \"_namespace\" = 'jaeger-service-operation-index-staging' AND (_timestampMillis > 1679445726410) SETTINGS max_threads=5, optimize_monotonous_functions_in_order_by = 0, timeout_before_checking_execution_speed = 0, max_execution_time=55, timeout_overflow_mode='throw' FORMAT JSONCompact",
"build_hash": "c8ba2aba90",
"sql": "SELECT DISTINCT `process.service_name` FROM `jaeger-service-operation-index-staging` WHERE `_timestampMillis` > 1679445726410",
"orderby_attributes": null,
"@reserved": {
"rawLogSize": 1635,
"latencyTimestamps": {
"collectorPickup": 1680050526791368400
},
"collector": {
"inode": 64357155,
"pipeline": "up-stdlogs-with-mesos-executor-id",
"path": "/opt/uber/mesos-agent/udocker-log-links/sawmill-query/jaeger/compute-0/50331648/thermos-7e71262a-97cf-4819-97cc-39c27887cd32-0-9/stdlogs/stdout",
"filename": "stdout",
"kafka_topic": "sawmill-query",
"env": "production"
}
},
"condition_attributes": [
"_timestampMillis"
],
"partition": "compute-0",
"select_attributes": [
"process.service_name"
],
"fields_count": 8,
"processed_query": "{\n \"query\": \"SELECT DISTINCT `process.service_name` FROM `jaeger-service-operation-index-staging` WHERE `_timestampMillis` \\u003e 1679445726410\"\n}",
"mesos_executor_id": "thermos-7e71262a-97cf-4819-97cc-39c27887cd32-0-9",
"deployment": "3",
"namespaceCount": 1,
"offset": 95004648,
"service_name": "sawmill-query",
"datacenter": "dca11",
"query_type": "sql",
"env": "jaeger",
"pipeline": "us1",
"caller": "query/query_pattern.go:26",
"component": "query-translator",
"@timestamp": "2023-03-29T00:42:06.791Z",
"application": "sawmill-query",
"ts": 1680050526.4303305,
"namespaces": [
"jaeger-service-operation-index-staging"
]
}Jack Luo
03/31/2023, 1:03 AMJackie
03/31/2023, 1:03 AMJack Luo
03/31/2023, 1:04 AM"jsonIndexConfigs": {
"json_data": {
"maxLevels": 3,
"excludeArray": true,
"disableCrossArrayUnnest": true,
"excludePaths": [
"$.@reserved"
],
"excludeFields": []
}
},Jack Luo
03/31/2023, 1:04 AMexecludeArray = true, disableCrossArrayUnnest = true.Jackie
03/31/2023, 1:05 AMJack Luo
03/31/2023, 1:05 AMJackie
03/31/2023, 1:05 AMJack Luo
03/31/2023, 1:06 AMJack Luo
03/31/2023, 1:07 AMJackie
03/31/2023, 1:10 AMJackie
03/31/2023, 1:12 AMJack Luo
03/31/2023, 1:15 AMjson_index is
SELECT * FROM tableA WHERE
text_match(
"json_data", '"instance*50331649"'
)
AND json_extract_scalar(
"json_data", '$.instance', 'INT',
0
) = 50331649
from emperical observation seems like text_match (with index) and json_extract_scalar (scan) is executing concurrently instead of sequentially.Jack Luo
03/31/2023, 1:16 AM# Query finished in 27ms
SELECT
runtime_env,
count(*)
FROM
"sawmill-query-for-pinot"
WHERE
(
_timestampMillis <= 1679691885000
AND _timestampMillis > 1679432712000
)
AND text_match(
"json_data", '"instance*33554433"'
)
GROUP BY
runtime_env
ORDER BY
count(*) desc
LIMIT
10Jack Luo
03/31/2023, 1:16 AM# Query finished in 1880ms
SELECT
runtime_env,
count(*)
FROM
"sawmill-query-for-pinot"
WHERE
(
_timestampMillis <= 1679691885000
AND _timestampMillis > 1679432712000
)
AND (
text_match(
"json_data", '"instance*33554433"'
)
AND json_extract_scalar(
"json_data", '$.instance', 'INT',
0
) = 33554433
)
GROUP BY
runtime_env
ORDER BY
count(*) desc
LIMIT
10Jack Luo
03/31/2023, 1:17 AMAND json_extract_scalar(
"json_data", '$.instance', 'INT',
0
) = 33554433
slowed down query by 50x+Jack Luo
03/31/2023, 1:18 AMtext_match is around 1800ms as well.Jack Luo
03/31/2023, 1:21 AM{
"name": "json_data",
"encodingType": "RAW",
"indexType": "TEXT",
"indexTypes": [
"TEXT"
],
"compressionCodec": "ZSTANDARD",
"properties": {
"enableQueryCacheForTextIndex": "true" <-- add this
}
},
If we enable queryCacheForTextIndex, it seem to force pinot query to be executed in sequential order (text_match first, then json_extract_scalar). Perhaps there are some bug with the query planner. In which case, even with json_extract_scalar, query now completes <50ms rather than the previous 1000ms+.Jackie
03/31/2023, 1:25 AMJackie
03/31/2023, 1:25 AMJack Luo
03/31/2023, 1:27 AMtext_match. For example, json_match + json_extract_scalar seems to be bug free.Jackie
03/31/2023, 3:57 AMFilterOperatorUtils.reordeAndFilterChildOperators(), and we do order ExpressionFilterOperator after TextMatchFilterOperatorJackie
03/31/2023, 3:57 AMJack Luo
03/31/2023, 3:17 PMJack Luo
03/31/2023, 6:50 PMjson_match + json_extract_scalar runs even slower than json_extract_scalar by itself!Jack Luo
03/31/2023, 6:50 PMtext_match + json_extract_scalar => 3568msJack Luo
03/31/2023, 6:51 PMjson_extract_scalar only (no index at all) => 620msJack Luo
03/31/2023, 6:51 PMtext_match only => 53msJack Luo
03/31/2023, 6:54 PMtext_match + json_scalar_extract together is significantly slower than running either of them individually. It's something worse.