apt-get install libmysql
command. It implements the MySQL API: #include <stdio.h> #include <stdlib.h> #include <mysql/mysql.h> #include "mysql_example.h" /* Pour MYSQL_HOST, MYSQL_USER, MYSQL_PASS */ int main(int argv, char *argc[]) { MYSQL_RES *results = NULL; MYSQL_ROW row; MYSQL *db = NULL; db = (MYSQL *)malloc(sizeof(MYSQL)); mysql_init(db); mysql_options(db, MYSQL_INIT_COMMAND, "SET NAMES UTF8;"); if(!mysql_real_connect(db, MYSQL_HOST, MYSQL_USER, MYSQL_PASS, NULL, 0, NULL, 0)) { fprintf(stderr, "Failed to connect to host %s (%s)", MYSQL_HOST, mysql_error(db)); exit(EXIT_FAILURE); } mysql_set_character_set(db, "utf8"); mysql_select_db(db, "my_database"); mysql_query(db , "SELECT user_id AS id FROM users WHERE user_description='member' LIMIT 10000"); results = mysql_use_result(db); while(row = mysql_fetch_row(results)) { printf("%s\n", row[0]); } mysql_free_result(results); exit(EXIT_SUCCESS); }
/* PDO */ $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);
/* , 0, 1 */ $pdo->setAttribute(PDO::MYSQL_ATTR_DIRECT_QUERY, 0);
$stmt = $pdo->prepare("SELECT user_id FROM users LIMIT :limit"); $stmt->bindValue('limit', 10); $stmt->execute(); $result = $stmt->fetch(); var_dump($result); /* PHP Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''10'' */
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0); /* Disable prepared statement emulation layer */ $stmt = $pdo->prepare("SELECT user_id FROM users LIMIT :limit"); /* A true prepare() will be sent to the RDBMS, it has to support it */ $stmt->bindValue('limit', 10); $stmt->execute(); $result = $stmt->fetch(); var_dump($result); /* array(4) { ["user_id"]=> string(7) "18" [0]=> string(7) "18" } */
/* Tells the PDO prepared statement emulation layer that this column is of type integer (SQL type) */ $stmt->bindValue('limit', 10, PDO::PARAM_INT);
<?php mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); try { $db = mysqli_connect('myserver', 'myuser', 'secret', 'unknown_database'); } catch (mysqli_sql_exception $e) { exit($e->getMessage()); } try { mysqli_query($db, "SELECT foo FROM bar"); /* ... */ } catch(mysqli_sql_exception $e) { }
<?php mysqli_report(MYSQLI_REPORT_INDEX); $db = mysqli_connect('myserver', 'myuser', 'secret', 'my_database'); mysqli_query($db, "SELECT photo FROM Users WHERE source ='web' LIMIT 1000"); /* PHP Warning: mysqli_query(): (00000/0): No index used in query/prepared statement ... */
mysqli_set_charset()
. You should never use the "SET NAMES" query. You can read more about this on php.net or dev.mysql.com .mysqli_query()
, the entire result set will already be transferred to memory on the PHP side and released on the MySQL side. You can count it by mysqli_num_rows()
, you can search for mysqli_data_seek()
, and you can also make another request until the set is released. For example: $mysqli = mysqli_connect(/*...*/); /* : PHP */ $result = mysqli_query($mysqli, "SELECT id, nom, email FROM members"); $line1 = mysqli_fetch_row($result); mysqli_data_seek($result, mysqli_num_rows($result)); /* */ $last_result = mysqli_fetch_row($result); /* */ /* , , :*/ mysqli_free_result($result);
memory_get_usage()
, and it will not be taken into account in memory_limit until you use mysqlnd as a low-level connector. $mysqli = mysqli_connect(/*...*/); /* MySQL */ $result = mysqli_query($mysqli, "SELECT id, email FROM members", MYSQLI_USE_RESULT); $line1 = mysqli_fetch_row($result); /* */ /* , - “ ” */ mysqli_data_seek($result, mysqli_num_rows($result)); /* , , */ $result2 = mysqli_query($mysqli, "SELECT name FROM membres", MYSQLI_USE_RESULT);
mysqli_free_result()
. By default, any direct query is done in buffered mode, because the MySQL server has something to do instead of allocating memory to store each set. $m = mysqli_connect(/* params */); $q = mysqli_query($m, 'SELECT * FROM Users LIMIT 1'); while($r = mysqli_fetch_row($q)) { /* do something */ } mysqli_free_result($r);
$m = mysqli_connect(/* params */); $ps = mysqli_prepare($m, 'SELECT * FROM Users LIMIT 1'); mysqli_stmt_execute($ps); while(mysqli_stmt_fetch($ps)) { /* */ } mysqli_stmt_close($ps);
$m = mysqli_connect(/* params */); $ps = mysqli_prepare($m, 'SELECT id FROM Users LIMIT 10'); /* id INTEGER */ mysqli_stmt_execute($ps); mysqli_stmt_bind_result($ps, $id); /* $id */ while(mysqli_stmt_fetch($ps)) { var_dump($id); } /* int(62) int(64) */
$m = mysqli_connect(/* */); $q = mysqli_query($m, 'SELECT id FROM users LIMIT 10'; while($r = mysqli_fetch_row($q)) { var_dump($r[0]); } /* string(2) "62" string(2) "64" */ $m = mysqli_connect(/* */); mysqli_options($m, MYSQLI_OPT_INT_AND_FLOAT_NATIVE, true); /* mysqlnd */ $q = mysqli_query($m, 'SELECT id FROM users LIMIT 10'); while($r = mysqli_fetch_row($q)) { var_dump($r[0]); } /* int(62) int(64) */
fetch()
operation will start a network data exchange. However, you can buffer sets with mysqli_stmt_store_result()
. $m = mysqli_connect(/* */); $ps = mysqli_prepare($m, 'SELECT id, name FROM Users LIMIT 1000'); mysqli_stmt_execute($ps); mysqli_stmt_bind_result($ps, $id, $name); /* */ mysqli_stmt_store_result($ps); while(mysqli_stmt_fetch($ps)) { /* do something with $id and $name */ } mysqli_stmt_close($ps);
mysqli_stmt_get_result()
is available to you, which turns the result set for the prepared expression into mysqli_result. This allows you to use the API for direct requests, but with prepared expressions: $m = mysqli_connect(/* params */); $ps = mysqli_prepare($m, 'SELECT id, name FROM Users LIMIT 1000'); mysqli_stmt_execute($ps); /* mysqli_result */ $r = mysqli_stmt_get_result($ps); /* mysqlnd */ while($result = mysqli_fetch_row($r)) { /* API */ /* - */ } mysqli_free_result($r); mysqli_stmt_close($ps);
$db = mysqli_connect(/* */); $result = mysqli_query($db, "SELECT very_huge_blob_column, lots, of, columns FROM foobar"); /* */ while($results[] = mysqli_fetch_row($result)) { } mysqli_free_result($result); /* , */ foreach($results as $foo) { /* - */ }
function memory_usage() { $pid = getmypid(); $r = explode(':',shell_exec("grep VmData /proc/$pid/status")); return '->'.trim($r[1])."\n"; } $db = mysqli_connect(/* */); echo "initial memory " . memory_usage(); $result = mysqli_query($db,"SELECT very_huge_blob_column FROM foobar"); echo "resultSet stored " . memory_usage(); while($results[] = mysqli_fetch_row($result)) { } echo "query result saved " . memory_usage(); mysqli_free_result($result); echo "resultSet freed " . memory_usage(); unset($results); echo "saved result freed " . memory_usage(); unset($db); echo "Db closed " . memory_usage();
> phplibmysql/bin/php poc_mysqli.php initial memory ->3348 kB resultSet stored ->72724 kB query result saved ->149012 kB resultSet freed ->81156 kB saved result freed ->25348 kB Db closed ->24260 kB
mysqli_query()
is mysqli_query()
, all result sets are sent to PHP memory. Its volume increases from 3 to 70 MB. This is normal for direct buffered queries. It is important to understand that the memory buffer was allocated by the library libmysql. And when you need to convert the results into a suitable form for PHP, all the data from the result set will be duplicated into memory, which leads to its swelling.memory_get_usage()
, and you will have to monitor your processes in order to notice it in time. 99.92% (257,473,815B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc. ->52.90% (136,314,880B) 0x69A01E: _zend_mm_alloc_int (zend_alloc.c:1908) | ->52.60% (135,528,448B) 0x69A1DE: _estrndup (zend_alloc.c:2503) | | ->52.60% (135,528,448B) 0x533BCE: php_mysqli_fetch_into_hash (mysqli.c:1191) | | ->52.60% (135,528,448B) 0x53F2E1: zif_mysqli_fetch_row (mysqli_nonapi.c:352) | | ->52.60% (135,528,448B) 0x70186A: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:320) | | ->52.60% (135,528,448B) 0x6D9D96: execute (zend_vm_execute.h:107) | | ->52.60% (135,528,448B) 0x6B4B98: zend_execute_scripts (zend.c:1236) | | ->52.60% (135,528,448B) 0x663D0C: php_execute_script (main.c:2308) | | ->52.60% (135,528,448B) 0x73BCDC: main (php_cli.c:1184) | | | ->00.31% (786,432B) in 1+ places, all below ms_print's threshold (01.00%) | ->45.85% (118,130,675B) 0x52DD010: my_malloc (my_malloc.c:37) | ->45.84% (118,112,344B) 0x52E0583: alloc_root (my_alloc.c:219) | | ->45.83% (118,096,024B) 0x5307A40: cli_read_rows (client.c:1418) | | | ->45.83% (118,096,024B) 0x5305955: mysql_store_result (client.c:2957) | | | ->45.83% (118,096,024B) 0x53EF09: zif_mysqli_query (mysqli_nonapi.c:540) | | | ->45.83% (118,096,024B) 0x70186A: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:320) | | | ->45.83% (118,096,024B) 0x6D9D96: execute (zend_vm_execute.h:107) | | | ->45.83% (118,096,024B) 0x6B4B98: zend_execute_scripts (zend.c:1236) | | | ->45.83% (118,096,024B) 0x663D0C: php_execute_script (main.c:2308) | | | ->45.83% (118,096,024B) 0x73BCDC: main (php_cli.c:1184)
mysqli_free_result()
. After that, the occupied memory will be reduced to 70 MB. And when we finally release the PHP array containing the copy of the set, the memory will return to its previous size. > phpmysqlnd/bin/php poc_mysqli.php initial memory ->3208 kB resultSet stored ->70452 kB query result saved ->71220 kB resultSet freed ->81148 kB saved result freed ->19196 kB Db closed ->19196 kB
memory_get_usage()
will show this memory, and you can even get access to memory_limit .$stmt->fetchAll()
transforms all result sets into a PHP variable. This allows more economical use of memory for searching in a set and obtaining actual data with the subsequent conversion of any string into a section of a PHP array. $db = mysqli_connect(/* */); $result = mysqli_query($db,"SELECT user_id, email FROM users LIMIT 5"); mysqli_data_seek($result, 5); $data = mysqli_fetch_row($result); do_something($data); mysqli_free_result($result); var_dump(mysqli_get_connection_stats($db)); /* only available under mysqlnd */ /* ["buffered_sets"]=> string(1) "1" ["rows_fetched_from_server_normal"]=> string(1) "5" ["rows_buffered_from_client_normal"]=> string(1) "5" ["rows_fetched_from_client_normal_buffered"]=> string(1) "1" ["connect_success"]=> string(1) "1" ["connect_failure"]=> string(1) "0" ["connection_reused"]=> string(1) "0" ["reconnect"]=> string(1) "0" ["active_connections"]=> string(1) "1" */
class JPMysqli extends Mysqli { public function __destruct() { $stats = $this->get_connection_stats(); $this->close(); if($diff = $stats["rows_fetched_from_server_normal"] - ($stats["rows_fetched_from_client_normal_unbuffered"] + $stats["rows_fetched_from_client_normal_buffered"])) { trigger_error(" *$diff* ", E_USER_NOTICE); } } } $db = new JPMysqli(/* */); $result = mysqli_query($db,"SELECT user_id, email FROM users LIMIT 5"); mysqli_data_seek($result, 5); $data = mysqli_fetch_row($result); do_something($data); exit(); /* Notice : " *4* " */
Source: https://habr.com/ru/post/234125/
All Articles