简体   繁体   English

使用远程数据库时简单Eloquent查询太慢

[英]Simple Eloquent query too slow when using remote database

Question Edit问题编辑

Server服务器

32 GB RAM
innodb_buffer_pool_size=1400M

Database数据库

Fresh empty table:新鲜空桌:

fields(id, name, type, optional)

SHOW TABLE STATUS LIKE "fields" result SHOW TABLE STATUS LIKE "fields" 结果

Name:fields|Engine:InnoDB|Row_format:Dynamic|Rows:0|Av_row_length:0|Data_length:16384|Max_data_length:0|Data_free:0|Auto_increment:null|Create_time:2022-08-01 12:52:42|Update_time:NULL|Check_time:NULL|Collation:utf8_general_ci|Checksum:NULL|Create_options:|Comment:|Max_index_length:0|Temporary:N

Test code测试代码

Route::get('/', function () {
    $gtc = microtime(true);
    DB::connection()->getPdo();
    $total = round((microtime(true) - $gtc) * 1000);
    echo "Laravel Connection time: $total ms<br>";

    $gtc = microtime(true);
    DB::select('SELECT * FROM fields');
    $total = round((microtime(true) - $gtc) * 1000);
    echo "Laravel Get rows time: $total ms<br>";

    $gtc = microtime(true);
    DB::select('SELECT 1');
    $total = round((microtime(true) - $gtc) * 1000);
    echo "Laravel SELECT time: $total ms<br>";

    $gtc = microtime(true);
    $pdo = new MySQLModel(env('DB_HOST'), env('DB_DATABASE'), env('DB_USERNAME'), env('DB_PASSWORD'));
    $connection = $pdo->getConnection();
    $total = round((microtime(true) - $gtc) * 1000);
    echo "PDO Connection time: $total ms<br>";

    $gtc = microtime(true);
    $ps = $connection->prepare('SELECT* FROM fields');
    $ps->execute();
    $total = round((microtime(true) - $gtc) * 1000);
    echo "PDO Get rows time: $total ms<br>";

    $gtc = microtime(true);
    $ps = $connection->prepare('SELECT 1');
    $ps->execute();
    $total = round((microtime(true) - $gtc) * 1000);
    echo "PDO SELECT time: $total ms<br>";

    for($i = 0; $i<5; $i++) {
        print_r(DB::select('SELECT NOW(3)'));
        echo '<hr>';
    }
});

Results结果

Laravel Connection time: 549 ms
Laravel Get rows time: 136 ms
Laravel SELECT time: 137 ms
PDO Connection time: 282 ms
PDO Get rows time: 68 ms
PDO SELECT time: 68 ms
Array ( [0] => stdClass Object ( [NOW(3)] => 2022-08-01 13:24:10.784 ) ) 
Array ( [0] => stdClass Object ( [NOW(3)] => 2022-08-01 13:24:10.921 ) ) 
Array ( [0] => stdClass Object ( [NOW(3)] => 2022-08-01 13:24:11.057 ) ) 
Array ( [0] => stdClass Object ( [NOW(3)] => 2022-08-01 13:24:11.193 ) ) 
Array ( [0] => stdClass Object ( [NOW(3)] => 2022-08-01 13:24:11.333 ) ) 

This does not happen if the database is located in the same server but when connecting with the remote one.如果数据库位于同一台服务器但与远程服务器连接时,则不会发生这种情况。

It all points it is a connection issue related with Eloquent and I haven't found any information about configuring something to prevent such a huge increase in query execution (tends to doubles it in all test I have made).这一切都表明这是一个与 Eloquent 相关的连接问题,我还没有找到任何有关配置某些东西以防止查询执行量如此巨大增加的信息(在我所做的所有测试中往往会加倍)。

Inconsistent timing:时间不一致:

Don't turn on the stopwatch.不要打开秒表。 The first fetch is slower because of fetching data from disl;因为从disl取数据,第一次取比较慢; the rest run faster. rest 运行速度更快。 due to caching.由于缓存。

Tuning:调音:

What is the value of innodb_buffer_pool_size ? innodb_buffer_pool_size的值是多少? How much RAM does the server have?服务器有多少内存?

Connecting:连接:

The program should connect only once, and do it before starting any timing tests.该程序应该只连接一次,并在开始任何计时测试之前完成。 Time the connection separately.单独计时连接。 (Yes, 400ms to connect is absurd.) (是的,400 毫秒的连接是荒谬的。)

Data_length:数据长度:

16K is the 'minimum' size of a non-empty table. 16K 是非空表的“最小”大小。 That is 1 block.那是1块。 Even on the least powerful local machine, the SELECT * FROM t should take under 10ms.即使在最不强大的本地机器上, SELECT * FROM t也应该在 10ms 以下。

Next steps:下一步:

If you can instrument Eloquent this way, we can focus on the connection , not be distracted by the SELECTs:如果您可以通过这种方式检测 Eloquent,我们可以专注于连接,而不会被 SELECT 分心:

Print the time just and just after before making the connection.在建立连接之前打印刚刚和之后的时间。

If that is not practical, then change the query to simply SELECT 1 .如果这不切实际,则将查询更改为简单的SELECT 1

Another approach is to perform and display SELECT NOW(3) in a loop 5 times.另一种方法是循环执行并显示SELECT NOW(3) 5 次。 This should demonstrate whether Eloquent is reconnecting!这应该证明 Eloquent 是否正在重新连接!

It is Laravel default config fault , it will query the server multiple times to setup the * * database instead of creating a single SQL Query to send to MySQL.这是 Laravel 默认配置错误,它将多次查询服务器以设置 * * 数据库,而不是创建单个 SQL 查询以发送到 MySQL。

Set charset to null in config/database.php在 config/database.php 中将字符集设置为 null

'charset' => null,

It will prevent Laravel sending a它将阻止 Laravel 发送

"set names '{$config['charset']}'".$this->getCollation($config)

query to the server.向服务器查询。

Next set strict to NULL in config/app.php in the mysql key接下来在 mysql 密钥中的 config/app.php 中将严格设置为 NULL

'strict' => null,

It will prevent Laravel from它将防止 Laravel

$connection->prepare($this->strictMode($connection, $config))->execute();

or或者

$connection->prepare("set session sql_mode='NO_ENGINE_SUBSTITUTION'")->execute();

This will prevent server communication delay which results the connection request to the server be a lot faster.这将防止服务器通信延迟,从而导致对服务器的连接请求更快。

Laravel Connection time: 268 ms

For the SELECT queries taking double time it is due对于需要双倍时间的 SELECT 查询,到期

$statement = $this->prepared(
                $this->getPdoForSelect($useReadPdo)->prepare($query)
            );//+68 ms more!

which communicates with the server for PDO prepare.它与 PDO 准备的服务器通信。

$statement->execute();//+68 ms more!

Set PDO::ATTR_EMULATE_PREPARES => true in the options key within mysql array set to enable emulated prepares and increase performance.在 mysql 数组集中的选项键中设置PDO::ATTR_EMULATE_PREPARES => true以启用模拟准备并提高性能。

The performance should be almost equal to PDO now.现在的性能应该和PDO差不多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM