Laravel 與 ProxySQL 的雷

雖然 Laravel 的 Prepared 準備好了,但 ProxySQL 卻沒有。

前言

在之前的文章 PHP 騙你,PDO prepare 並沒有準備好 中,我們提到了 PHP預設利用 PDO::ATTR_EMULATE_PREPARES = true 的設定,避免有些資料庫不支援 Prepared Statement 的特性。

在 Laravel Framework 中,這個設定已被訂為 false,也就是說 Laravel 能夠完整享受到 Prepared Statement 的益處(與壞處)。

然而,這在 ProxySQL 的開發者眼中頗不以為然,他很訝異 Laravel 居然會使用如此低效率的方式進行 MySQL Access。

Prepared Statement 的兩種形式

MySQL 官方文件指出,Prepared Statement 支援兩種方式:

  • binary protocol:實際意義上的 Prepared Statement
  • SQL level:用 SQL 語法實現的 Prepared Statement

應用於 C 語言的 libmysqlclient、應用於 Java 的 mysql-connector-java,或是 PHP 的 mysqli extension 都是支援 binary protocol 的 prepared statement。

然而在 PDO 的 mysql 實現中,它卻是利用 SQL level 進行實現。

不支援 SQL Level 的 Prepared Statement 原因

據 ProxySQL 的 contributor renecannaoissue#1118 所述,SQL level 的 prepared statement 是相當影響效能的。

另外,我個人的猜測,SQL Level 的 prepared statement 可能會造成 SQL statement 的錯亂:舉例來說,prepare 語句丟到了 MySQL Server A,但後續的參數綁定卻丟進了 MySQL Server B,造成不可預期的錯誤。

如何解決 ProxySQL 不支援的問題

  1. 在 Laravel 的 config/database.php中,加入 'options' => [PDO::ATTR_EMULATE_PREPARES => true]
  2. 將 Laravel 的 MySQL 連接方式改為 mysqli driver,並且利用 binary protocol 層級的 prepares