swoole / swoole-src

🚀 Coroutine-based concurrency library for PHP
https://www.swoole.com
Apache License 2.0
18.27k stars 3.16k forks source link

Coroutine PDO 内存缓慢增涨 #2486

Closed onanying closed 5 years ago

onanying commented 5 years ago

环境:

[root@localhost data]# php -v
PHP 7.2.9 (cli) (built: Aug 22 2018 08:02:49) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
[root@localhost data]# 
[root@localhost data]# php --ri swoole

swoole

Swoole => enabled
Author => Swoole Team <team@swoole.com>
Version => 4.3.1
Built => Apr  4 2019 02:38:44
coroutine => enabled
epoll => enabled
eventfd => enabled
signalfd => enabled
cpu_affinity => enabled
spinlock => enabled
rwlock => enabled
http2 => enabled
pcre => enabled
zlib => enabled
mutex_timedlock => enabled
pthread_barrier => enabled
futex => enabled
async_redis => enabled

Directive => Local Value => Master Value
swoole.enable_coroutine => On => On
swoole.display_errors => On => On
swoole.use_shortname => On => On
swoole.unixsock_buffer_size => 8388608 => 8388608

代码:

<?php

\Swoole\Runtime::enableCoroutine(true);

go(function () {

    for ($i = 0; $i < 50; $i++) {
        go(function () {

            while (true) {
                $pdo = new \PDO(
                    'mysql:host=192.168.1.150;port=3306;charset=utf8;dbname=test',
                    'root',
                    '123456',
                    [
                        \PDO::ATTR_EMULATE_PREPARES   => false,
                        \PDO::ATTR_ERRMODE            => \PDO::ERRMODE_EXCEPTION,
                        \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
                    ]
                );
                $statement = $pdo->prepare("select * from `test`");
                $statement->execute();
                $ret = $statement->fetchAll();
                var_dump(json_encode($ret));
            }

        });
    }

});

使用 docker stats 检测内存情况:

刚开始

CONTAINER           CPU %               MEM USAGE / LIMIT       MEM %               NET I/O             BLOCK I/O           PIDS
php72               98.17%              11.32 MiB / 976.5 MiB   1.16%               0 B / 0 B           347 MB / 8.58 MB    5

半小时后:

CONTAINER           CPU %               MEM USAGE / LIMIT       MEM %               NET I/O             BLOCK I/O           PIDS
php72               57.79%              150.5 MiB / 976.5 MiB   15.41%              0 B / 0 B           347 MB / 8.58 MB    5

代码2:重用 db 对象

<?php

\Swoole\Runtime::enableCoroutine(true);

go(function () {

    for ($i = 0; $i < 100; $i++) {
        go(function () {

            $pdo = new \PDO(
                'mysql:host=192.168.1.150;port=3306;charset=utf8;dbname=test',
                'root',
                '123456',
                [
                    \PDO::ATTR_EMULATE_PREPARES   => false,
                    \PDO::ATTR_ERRMODE            => \PDO::ERRMODE_EXCEPTION,
                    \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
                ]
            );

            while (true) {

                $statement = $pdo->prepare("select * from `test`");
                $statement->execute();
                $ret = $statement->fetchAll();
                var_dump(json_encode($ret));

            }

        });
    }

});

刚开始:

CONTAINER           CPU %               MEM USAGE / LIMIT       MEM %               NET I/O             BLOCK I/O           PIDS
php72               87.03%              16.17 MiB / 976.5 MiB   1.66%               0 B / 0 B           347 MB / 8.58 MB    5

10多分钟已经:

CONTAINER           CPU %               MEM USAGE / LIMIT      MEM %               NET I/O             BLOCK I/O           PIDS
php72               88.15%              50.3 MiB / 976.5 MiB   5.15%               0 B / 0 B           347 MB / 8.58 MB    5

又过了才3分钟,内存迅速增加:

CONTAINER           CPU %               MEM USAGE / LIMIT       MEM %               NET I/O             BLOCK I/O           PIDS
php72               97.83%              121.9 MiB / 976.5 MiB   12.48%              0 B / 0 B           347 MB / 8.58 MB    5

可见是否重用连接,都存在该问题。

onanying commented 5 years ago

可观察的现象是:每间隔一小段时间,立即增加2mb

onanying commented 5 years ago

该问题在 swoole v4.2.13 也有,但是表现的增涨情况为线性增加,不是每间隔一小段时间,立即增加2mb

twose commented 5 years ago

在代码尾部加一个Swoole\Event::wait(), 再观察

onanying commented 5 years ago
<?php

\Swoole\Runtime::enableCoroutine(true);

go(function () {

    for ($i = 0; $i < 100; $i++) {
        go(function () {

            $pdo = new \PDO(
                'mysql:host=192.168.1.150;port=3306;charset=utf8;dbname=test',
                'root',
                '123456',
                [
                    \PDO::ATTR_EMULATE_PREPARES   => false,
                    \PDO::ATTR_ERRMODE            => \PDO::ERRMODE_EXCEPTION,
                    \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
                ]
            );

            while (true) {

                $statement = $pdo->prepare("select * from `test`");
                $statement->execute();
                $ret = $statement->fetchAll();
                var_dump(json_encode($ret));

            }

        });
    }

});

Swoole\Event::wait();

问题依旧,10多分钟:

CONTAINER           CPU %               MEM USAGE / LIMIT       MEM %               NET I/O             BLOCK I/O           PIDS
php72               96.03%              154.9 MiB / 976.5 MiB   15.87%              0 B / 0 B           347 MB / 8.58 MB    5
twose commented 5 years ago

MacOS 下未复现, 内存稳定无波动, 我试一下Linux 你可以用宿主机观察docker容器进程的内存占用, docker stats好像有点不准

twose commented 5 years ago

Linux (Ubuntu)下: PHP总内存一段时间后无变化, 实际内存小范围波动 top观察虚拟内存和物理内存无变化 (因为PHP有内存池)

twose commented 5 years ago

你可以把var_dump数据打印去掉

onanying commented 5 years ago

宿主机 top VIRT RES,和 docker stats 的值非常接近,说明 docker stats 是准的,var_dump 去掉后,增长确实慢了非常多,我再长时间测试一下,看看

onanying commented 5 years ago
<?php

\Swoole\Runtime::enableCoroutine(true);

go(function () {

    for ($i = 0; $i < 100; $i++) {
        go(function () {

            $pdo = new \PDO(
                'mysql:host=192.168.1.150;port=3306;charset=utf8;dbname=test',
                'root',
                '123456',
                [
                    \PDO::ATTR_EMULATE_PREPARES   => false,
                    \PDO::ATTR_ERRMODE            => \PDO::ERRMODE_EXCEPTION,
                    \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
                ]
            );

            while (true) {

                $statement = $pdo->prepare("select * from `test`");
                $statement->execute();
                $ret = $statement->fetchAll();
                //var_dump(json_encode($ret));

            }

        });
    }

});

\Swoole\Event::wait();
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                     
 99544 root      20   0  340300  26828   8776 R  93.8  2.7   0:21.31 php   
onanying commented 5 years ago
CONTAINER           CPU %               MEM USAGE / LIMIT       MEM %               NET I/O             BLOCK I/O           PIDS
php72               99.79%              152.6 MiB / 976.5 MiB   15.62%              0 B / 0 B           347 MB / 8.58 MB    4
   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                     
 99544 root      20   0  340300 164732   8776 R 100.0 16.5   4:01.09 php      
onanying commented 5 years ago

恒定在 152.6 MiB 了,看来是没有问题,前期增长是没到那个点。

CONTAINER           CPU %               MEM USAGE / LIMIT       MEM %               NET I/O             BLOCK I/O           PIDS
php72               98.71%              152.6 MiB / 976.5 MiB   15.62%              0 B / 0 B           347 MB / 8.58 MB    4
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                     
 99544 root      20   0  340300 164732   8776 R  89.0 16.5   9:26.64 php    
8923052 commented 5 years ago

@twose 我也测试了一下,如果没有加以下代码

\Swoole\Event::wait();

内存确实会缓慢增长,直到超出ini限制进程退出,这个问题能否解决?可否不调用wait,稳定内存使用。 测试环境:

swoole

Swoole => enabled
Author => Swoole Team <team@swoole.com>
Version => 4.3.1
Built => Mar 14 2019 10:27:20
coroutine => enabled
epoll => enabled
eventfd => enabled
signalfd => enabled
spinlock => enabled
rwlock => enabled
sockets => enabled
openssl => LibreSSL 2.7.5
http2 => enabled
pcre => enabled
zlib => enabled
mutex_timedlock => enabled
pthread_barrier => enabled
futex => enabled
mysqlnd => enabled
jemalloc => enabled
async_redis => enabled

Directive => Local Value => Master Value
swoole.enable_coroutine => On => On
swoole.display_errors => On => On
swoole.use_shortname => On => On
swoole.unixsock_buffer_size => 8388608 => 8388608
onanying commented 5 years ago

@twose 对,刚刚我又测试了,不加:

\Swoole\Event::wait();

就会一直涨。

mabu233 commented 5 years ago

好像是之前php的bug,最新的swoole master已经兼容

twose commented 5 years ago

@onanying 此问题曾由 @mabu233 发现并已在master解决 Swoole下个版本(v4.3.2~master)会兼容此BUG, PHP将在7.2.17/7.3.4修复 https://github.com/php/php-src/commit/bd6eabd6591ae5a7c9ad75dfbe7cc575fa907eac

onanying commented 5 years ago

@twose \Swoole\Event::wait(); 只是把时间延长了,依然会有内存增涨问题。

<?php

\Swoole\Runtime::enableCoroutine(true);

go(function () {

    for ($i = 0; $i < 100; $i++) {
        go(function () {

            $pdo = new \PDO(
                'mysql:host=192.168.1.150;port=3306;charset=utf8;dbname=test',
                'root',
                '123456',
                [
                    \PDO::ATTR_EMULATE_PREPARES   => false,
                    \PDO::ATTR_ERRMODE            => \PDO::ERRMODE_EXCEPTION,
                    \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
                ]
            );

            while (true) {

                $statement = $pdo->prepare("select * from `test`");
                $statement->execute();
                $ret = $statement->fetchAll();
                //var_dump(json_encode($ret));

            }

        });
    }

});

\Swoole\Event::wait();

耐心等待,从12 分钟开始:

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                     
113685 root      20   0  340300  63332   8776 R  86.7  6.3  12:17.49 php      

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                     
113685 root      20   0  340300 189068   8776 R  86.7 18.9  13:23.31 php    

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                    
113685 root      20   0  340300 225572   8776 R  83.7 22.6  14:20.62 php     

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                    
113685 root      20   0  340300 225572   8776 R  82.4 22.6  19:38.12 php     

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                    
113685 root      20   0  340300 225572   8776 R  86.0 22.6  21:13.19 php