We run PHP 8.2.14 on Ubuntu 22.04, PHP-FPM with 32 workers, MySQL 8.0 on the same host. After upgrading a staging box to 8.3.0, one endpoint went from p95 180ms to p95 2.4s. It served paginated JSON: 50 rows, each with nested relations, roughly 120KB per response. The slow log pointed at a SELECT that hadn’t changed and ran in 8ms on its own. That was the first lie.
I reproduced with a route that called the repository directly and timed each stage:
$t0 = microtime(true);
$rows = $repo->findPage($page, 50);
$t1 = microtime(true);
$json = json_encode(['data' => $rows], JSON_THROW_ON_ERROR);
$t2 = microtime(true);
fwrite(STDERR, sprintf("db=%.1fms json=%.1fms\n", ($t1-$t0)*1000, ($t2-$t1)*1000));
On 8.2: db=7.9ms json=0.6ms. On 8.3: db=8.1ms json=2320.0ms. So the query was innocent; the response serialization was burning seconds. I dumped one row with var_export and found a decimal(10,2) column arriving as a string like "199.00" and, more importantly, a JSON column we had been selecting raw. In 8.2 that column came back as a plain string, and json_encode escaped it as a string. In 8.3 the mysqlnd driver returns it as an array, and we were re-encoding a deeply nested structure with 50 rows of 200+ elements. The cost wasn’t the size; it was that our code had been silently treating it as an opaque blob.
The fix was to stop assuming the driver’s type: I made the repository cast the column explicitly and, for list endpoints, select only the keys we render:
$stmt->bind_result($id, $payload);
$rows[] = ['id' => (int)$id, 'payload' => json_decode($payload, true, 512, JSON_THROW_ON_ERROR)];
That got p95 back to 210ms. The tradeoff is real: we now decode on read, which costs CPU on every request, but it makes the serialization cost visible and bounded instead of hidden inside json_encode. We also pinned PDO::ATTR_STRINGIFY_FETCHES in the test suite so future driver changes fail loudly.
This breaks if the JSON column is large and only occasionally needed; decoding eagerly will hurt. It also doesn’t help if the bottleneck is network or client-side parsing. And I’m still unsure whether the 8.3 behavior is a documented change or an artifact of our build; I haven’t found the exact changelog line, so treat that as unverified.
If you’ve upgraded past 8.3, did any of your JSON or decimal columns change type under PDO, and how did you catch it before production?


0 条评论
还没有评论,写下第一条。