Details
Description
SUMMARY
ANALYZE TABLE ... PERSISTENT FOR ALL on a very large table (~4.46 billion
rows) with a low-cardinality column produces a corrupted JSON histogram
in mysql.column_stats.histogram. The server logs a JSON parse error when
later reading it back, and re-running ANALYZE TABLE reproduces the same
truncation every time.
ENVIRONMENT
- MariaDB version: 11.4.9-MariaDB
- Table storage engine: InnoDB
- Approx. row count: ~4.46 billion (exact count not measured due to table size)
- Column: `type` (small integer/enum-like column, min_value=0, max_value=9)
- histogram_type: JSON_HB (default)
- histogram_size: 254 (default, per target_histogram_size in the output)
STEPS TO REPRODUCE
1. Create/have an InnoDB table with several billion rows and a low-
cardinality integer column (few distinct values, e.g. 0-9).
2. Run:
ANALYZE TABLE db.tbl PERSISTENT FOR ALL;
(takes ~4.5 hours on our hardware for this table size)
3. Query the resulting row:
SELECT * FROM mysql.column_stats
WHERE db_name='db' AND table_name='tbl' AND column_name='type';
4. Observe:
a) avg_frequency is capped at 99999999.9999 (the max value
representable by decimal(12,4)), even though the true value
(rows / ndv) is approximately 446326524.8 — i.e. the true value
overflows the column's precision and gets silently clamped.
b) The `histogram` JSON field is truncated/malformed mid-object, e.g.:
{
|
"target_histogram_size": 254, |
"collected_at": "...", |
"collected_by": "11.4.9-MariaDB-log", |
"histogram_hb": [ |
{ "start": "0", "size": 0.478226398, "ndv": 1 }, |
{ "start": "1", "size": 0.003937008, "ndv": 3 }, |
{ "start": "3" |
]
|
}
|
|
(Note the third bucket is missing "size"/"ndv" and the closing
brackets are unbalanced/incorrect.)
5. On next server startup / optimizer read, this produces:
[ERROR] InnoDB: (or general) Failed to parse histogram for table
db.tbl: JSON parse error at offset 313.
6. Re-running ANALYZE TABLE ... PERSISTENT FOR ALL reproduces the exact
same truncation deterministically — this is not a one-off storage
corruption (confirmed via CHECK TABLE mysql.column_stats EXTENDED,
which reports OK; the mysql.column_stats table itself, an Aria table,
is not corrupted).
EXPECTED BEHAVIOR
- avg_frequency should either use a data type that can hold the true
value for very large tables, or should not silently clamp/overflow
without at least a warning. - The histogram JSON serialization should not produce truncated/invalid
JSON under any circumstance — either it completes correctly, or
ANALYZE TABLE should fail loudly/log a clear error rather than
persisting a broken value.
SUSPECTED ROOT CAUSE
The avg_frequency overflow (decimal(12,4) max = 99999999.9999) for this
table size suggests an internal counter or intermediate calculation
related to row/frequency counts hits a numeric limit during histogram
bucket generation for very large tables with low column cardinality,
which appears to also corrupt the JSON serialization of the histogram
itself (possibly a shared buffer/length calculation that also overflows
or a partial write that isn't rolled back on error).
CREATE TABLE `column_stats` (
|
`db_name` varchar(64) NOT NULL, |
`table_name` varchar(64) NOT NULL, |
`column_name` varchar(64) NOT NULL, |
`min_value` varbinary(255) DEFAULT NULL, |
`max_value` varbinary(255) DEFAULT NULL, |
`nulls_ratio` decimal(12,4) DEFAULT NULL, |
`avg_length` decimal(12,4) DEFAULT NULL, |
`avg_frequency` decimal(12,4) DEFAULT NULL, |
`hist_size` tinyint(3) unsigned DEFAULT NULL, |
`hist_type` enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON_HB') DEFAULT NULL, |
`histogram` longblob DEFAULT NULL,
|
PRIMARY KEY (`db_name`,`table_name`,`column_name`)
|
) ENGINE=Aria DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin PAGE_CHECKSUM=1 TRANSACTIONAL=0 COMMENT='Statistics on Columns' |