morningman commented on issue #65423:
URL: https://github.com/apache/doris/issues/65423#issuecomment-4933757895

   Adding a usage summary for anyone landing here from a search, since the 
feature is undocumented on the website. Everything below reflects `master` with 
#63974 (Part 3) applied.
   
   ## What it is for
   
   Query Stats answers "which columns does anyone actually read, and which ones 
do people filter on?" The two counters serve different design decisions:
   
   | Counter | Column shown by `SHOW` | Incremented when the column appears in 
| Typical use |
   |---|---|---|---|
   | `queryHit` | `QueryCount` | SELECT output, GROUP BY, ORDER BY, window 
PARTITION/ORDER keys, aggregate inputs, UNION branches, LATERAL VIEW generator 
inputs | Find never-read columns; decide what to drop, or move to a slower 
codec |
   | `filterHit` | `FilterCount` | WHERE predicates, JOIN ON conditions (hash / 
other / mark), HAVING | Choose prefix-key / bucket-key columns; decide where to 
build an index |
   
   Rolled up: per column → per index (base table and each ROLLUP/MV) → per 
table → per database.
   
   ## Turning it on
   
   Off by default. It is a mutable FE config, so no restart is needed — but set 
it on **every** FE, because each frontend only counts the queries it planned 
itself:
   
   ```sql
   ADMIN SET ALL FRONTENDS CONFIG ("enable_query_hit_stats" = "true");
   ```
   
   Note `ADMIN SET FRONTEND CONFIG` (singular) only affects the FE you are 
connected to; a query routed to another frontend then records nothing, which is 
the most common reason for "it still shows 0".
   
   The switch gates **writing** only. Turning it off later stops recording but 
leaves the accumulated counters readable.
   
   ## Reading the counters
   
   ```sql
   -- No database selected: per-database counts for the current catalog (needs 
ADMIN_PRIV)
   SHOW QUERY STATS;
   
   -- With a database selected (or given explicitly): per-table counts in that 
database
   USE test_db;
   SHOW QUERY STATS;
   SHOW QUERY STATS FOR test_db;
   
   -- Per column, base index and all MVs summed together
   SHOW QUERY STATS FROM stats_table;
   
   -- Per index (base table + each ROLLUP / MV)
   SHOW QUERY STATS FROM stats_table ALL;
   
   -- Per index, per column
   SHOW QUERY STATS FROM stats_table ALL VERBOSE;
   ```
   
   Careful with the no-argument form: it resolves to the **database** level 
whenever a database is selected, and only falls back to the catalog level when 
none is. `VERBOSE` is only accepted together with `ALL`.
   
   Privileges: catalog level needs `ADMIN_PRIV`; database and table levels need 
`SHOW_PRIV` on the object.
   
   ## Clearing the counters
   
   ```sql
   CLEAN ALL QUERY STATS;                 -- whole catalog, ADMIN_PRIV
   CLEAN QUERY STATS FOR test_db;         -- one database, DB ALTER_PRIV
   CLEAN QUERY STATS FROM stats_table;    -- one table, table ALTER_PRIV
   ```
   
   These forward to the Master and are journaled, so every frontend is cleared. 
(The `DELETE /api/query_stats/...` REST endpoints are **not** equivalent — they 
clear only the frontend that serves the request. See the tracking list above.)
   
   ## Worked example
   
   There is no time window built into this feature — counters accumulate since 
FE startup or the last `CLEAN`. So the intended workflow is to bracket a 
representative workload with `CLEAN` and `SHOW`:
   
   ```sql
   ADMIN SET ALL FRONTENDS CONFIG ("enable_query_hit_stats" = "true");
   SET enable_query_cache = false;   -- see caveats: cache hits are never 
counted
   
   CLEAN ALL QUERY STATS;
   
   -- ... let the real workload run, or replay it ...
   SELECT k1, k2 FROM stats_table WHERE k3 > 100;
   SELECT k1, SUM(k2) FROM stats_table GROUP BY k1 HAVING SUM(k2) > 0;
   SELECT a.k1 FROM stats_table a JOIN stats_table b ON a.k1 = b.k1;
   
   SHOW QUERY STATS FROM stats_table;
   ```
   
   Illustrative output (`stats_table` is the schema used by 
`regression-test/suites/query_p0/stats/query_stats_test.groovy`):
   
   ```
   +-------+------------+-------------+
   | Field | QueryCount | FilterCount |
   +-------+------------+-------------+
   | k1    | 3          | 1           |
   | k2    | 2          | 1           |
   | k3    | 0          | 1           |
   | k4    | 0          | 0           |
   ...
   ```
   
   Reading it: `k1` was output or grouped by in all three queries and filtered 
once (the join key). `k3` never appears in a select list, only in `WHERE` — a 
good prefix-key or index candidate that costs nothing to keep out of the 
projection. `k4` is untouched by this workload.
   
   Per-index breakdown, useful for checking whether a ROLLUP is actually being 
hit:
   
   ```sql
   SHOW QUERY STATS FROM stats_table ALL;
   +-------------+------------+
   | IndexName   | QueryCount |
   +-------------+------------+
   | stats_table | 3          |
   | rollup_k3   | 0          |     <-- nothing routed here; candidate for 
removal
   +-------------+------------+
   ```
   
   ## How to read the numbers
   
   - **At most one increment per column per query.** `WHERE k1 > 0 AND k1 < 
100` bumps `k1`'s `FilterCount` by 1, not 2. A self-join scanning the same 
table twice also counts once. The counters measure "how many queries touched 
this column," not "how many times."
   - **Table `QueryCount` is the sum over its indexes**, and each index is 
incremented once per query routed to it.
   - A column can bump both counters in one query: `SELECT k1 FROM t WHERE k1 > 
0` gives `k1` both a `QueryCount` and a `FilterCount`.
   
   ## Caveats worth knowing before you act on the numbers
   
   1. **`FilterCount` is a lower bound.** Several plan shapes are still not 
attributed (see the tracking checklist above). `FilterCount = 0` does **not** 
prove a column was never filtered on — do not use it alone to justify dropping 
a column from the sort key. A high `FilterCount`, on the other hand, is 
reliable.
   2. **Counting happens at plan time, not execution time.** A query that plans 
and then fails or is cancelled is still counted. A query answered from the FE 
SQL cache is *not* counted (its physical plan holds no scan node), which is why 
the regression suite sets `enable_query_cache = false`. Read the counter as 
"queries planned."
   3. **In-memory only.** Nothing is written to the image or the editlog (only 
`CLEAN` is journaled). An FE restart resets that frontend's counters, and a 
Master switchover does not migrate them. For trend analysis, scrape and store 
externally.
   4. **Internal (OlapTable) only.** External tables — Hive, Iceberg, JDBC, 
ODBC, ES — are not recorded at all, by design.
   5. **`show tablets` `QueryHits` is always 0** and is unrelated to this 
switch. See the tracking checklist.
   6. **Multi-FE: use the SQL statements, not the REST endpoints.** `SHOW` 
merges every live frontend; `GET /api/query_stats/...` reads only the local one.
   
   ## Cost
   
   Recording adds one full traversal of the physical plan per planned query, 
proportional to the number of plan nodes times the slots per expression — 
light, but not free — and the counters live in FE memory for the lifetime of 
the process. Reasonable practice is to switch it on for a representative period 
while doing schema or index design work, rather than leaving it on permanently.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to