12 月 4 2017
PHP 的 MySQL driver/library 與記憶體耗用
再來還些債…
前陣子我們家在看一些 PHP 的版本差異,也順便拆一些地雷。
先說記憶體耗用,我們用下面這段程式在一些 PHP 版本上執行,觀察記憶體耗用:
<?php $a = memory_get_usage(); echo "begin:\t$a" . PHP_EOL; $arr = array(); for ( $i = 0; $i < 100000; $i++ ) { array_push($arr, $i); } $b = memory_get_usage(); echo "end:\t$b" . PHP_EOL; echo "diff:\t" . ($b - $a) . PHP_EOL;
在 PHP 5.1 ( on 32-bit Linux ) 觀察到的記憶體差距值大約是 5.8 MB,在 PHP 5.3 ~ 5.6 ( on 64-bit Linux ) 觀察到的記憶體差距值大約是 14 MB,而 PHP 7.1 ~ 7.2 ( on 64-bit Linux ) 觀察到的記憶體差距值大約是 4 MB。
至於 PHP 的 MySQL driver/library 方面…
參考官方提供的 Overview of the MySQL PHP drivers : Buffered and Unbuffered queries ;PHP 的所有的 MySQL extensions 預設使用 buffered mode,所以在撈取大量資料時,PHP 執行完 SQL statement 就會耗用大量記憶體。
另外,在官方提供的 Overview of the MySQL PHP drivers : Choosing a library 可以看到,PHP 5.3 以前的預設 MySQL driver ( libmysqlclient ) 沒使用 PHP 的原生記憶體管理,PHP 5.3 之後的預設 MySQL driver ( mysqlnd; MySQL Native Driver) 才開始用。
對這些行為,我們另外生了一段測試程式:
<?php $a = memory_get_usage(); echo "begin:\t\t$a" . PHP_EOL; $dbConn = new PDO(.....); $b = memory_get_usage(); echo "new PdoMySQL:\t$b\t\tDiff: " . ($b - $a) . PHP_EOL; $st = $dbConn->query("SELECT * FROM testTable LIMIT 30000"); $b = memory_get_usage(); echo "get Stmt:\t$b\tDiff: " . ($b - $a) . PHP_EOL; $row1 = $st->fetch(); $b = memory_get_usage(); echo "fetch once:\t$b\tDiff: " . ($b - $a) . PHP_EOL; $row2 = $st->fetch(); $b = memory_get_usage(); echo "fetch twice:\t$b\tDiff: " . ($b - $a) . PHP_EOL; $st->closeCursor(); $b = memory_get_usage(); echo "closeCursor:\t$b\t\tDiff: " . ($b - $a) . PHP_EOL; $row3 = $st->fetch(); $b = memory_get_usage(); echo "fetch:\t\t$b\t\tDiff: " . ($b - $a) . PHP_EOL; var_dump($row3);
在 PHP 7.1 ( with mysqlnd , 64-bit Linux ) 的數據如下:
begin: 355552 new PdoMySQL: 378784 Diff: 23232 get Stmt: 25866944 Diff: 25511392 fetch once: 25868752 Diff: 25513200 fetch twice: 25870568 Diff: 25515016 closeCursor: 384104 Diff: 28552 fetch: 384104 Diff: 28552 bool(false)
在 PHP 5.1 ( with libmysqlclient , 32-bit Linux ) 的數據則是:
begin: 50432 new PdoMySQL: 85752 Diff: 35320 get Stmt: 92184 Diff: 41752 fetch once: 93584 Diff: 43152 fetch twice: 95248 Diff: 44816 closeCursor: 95312 Diff: 44880 fetch: 95400 Diff: 44968 bool(false)
應該有為數不少的 PHP developers 曾經踩過這種記憶體相關的地雷…
7 月 31 2020
MySQL 8.0.20 升級後記(踩 & 拆雷記)
續前一篇,這陣子除了表訂工作之外,就是觀察 MySQL 升級後有哪些地雷被引爆,接著開始救傷。
一顆跟 Java/JDBC 有關,錯誤訊息如下圖;原因是 MySQL 8 沒有 query_cache_size 等等的參數,而解決方式是更新 MySQL Connector/J。
另外一顆的成因/故事就比較長了…
MySQL 8 之前的版本,我們習慣把 character_set_server 設為 utf8 ,而 collation_server 設為 utf8_general_ci ;升版前看到這兩個變數預設值都換掉了,就很開心的拿掉… 於是部份(年紀比較大)的 PHP web server 就爆了,錯誤訊息如下圖。
初步研判以為是 PHP 認不得 utf8mb4 ,仔細追查後發現真因是「認不得 utf8mb4_0900_ai_ci」,解決方式是把 collation_server 設為 utf8mb4_general_ci 。
附帶一提, PHP 5.3 的 ext/mysqlnd 沒這問題,僅 5.4 ~ 5.6 受影響,而 PHP 7 之後都支援 UCA Ver.9 的 collations …
By Joe Horn • Database, JAVA, PHP 0 • Tags: charset, collation, JAVA, JDBC, MySQL, mysqlnd, PHP, uca