The optimiser chooses a plan from statistics it samples, and a table whose distribution has changed since the last sample gets a plan chosen for the old data.
-- the estimate the optimiser is working from
EXPLAIN SELECT ... ; -- rows: 8100
EXPLAIN ANALYZE ... ; -- actual rows: 94012
SELECT last_update FROM mysql.innodb_table_stats
WHERE table_name = 'orders';
-- 2020-06-14 ← three months ago
ANALYZE TABLE orders;
A large gap between the estimate and the actual count is the diagnosis, and stale statistics are one of the two causes — the other being a predicate the optimiser cannot estimate at all. ANALYZE TABLE is cheap on InnoDB because it samples rather than scans, and innodb_stats_persistent_sample_pages is the knob when the default sample is too small for a skewed table. Histograms in 8.0 are the better tool for a genuinely skewed column and have to be created explicitly.