PHP程序應該減少brk調用,否則性能會受影響

字號+ 編輯: 国内TP粉 修訂: 秃顶萧峰 來源: 异步社区 2023-09-05 我要說兩句(0)

一個php底層方法, 解決一個性能問題。

昨天工作上遇到一個非常有意思的問題,特此分享給大家,也給大家提個醒,在 PHP 程序中盡量減少系統調用。

在我們系統中有一個 cron 腳本,完成的主要工作就是從 memcached 中獲取數據,然後同步到數據庫中。平時運行的好好的,但昨天卻遇到了問題,唯一的變化就是本次任務從 memcached 中獲取的數據非常多,總共有 100 萬條記錄。話不多少,先上偽代碼:

// 共100萬個memcached數據
$tnum = 1000000;
// 共1萬個key,每個100條memcached數據
$knum = ceil($tnum/100);
$mem->connect("localhost", "11211");
for ($i = 1; $i <= $knum; $i++) 
    $k[] = $mckey."_".$i;
# 一次性從 memcached 中獲取到數據
$emailmc = $mem->get($k);
$email = array();
foreach ($emailmc as $v) {
    $s     = unserialize($v);
    $s     = explode(",", $s);
    # 合並數組
    $email = array_merge($email, $s);
}
# 一次性導入到 mecached 中
importdb($email);

彪悍的 memcached

由於腳本本次運行對業務非常重要,我一直在監視,發現運行了半個小時也沒有結束,開始我思索是不是memcached一次性獲取太多了,導致memcached查詢遇到問題了?

使用 wireshark 和 strace 抓取了相關數據,發現獲取 memcached 非常快,幾秒鍾就返回了,贊一下 memcached 性能。

brk

接下去繼續分析,strace 出現了滿屏的 brk 系統調用,如下:

$ strace -p 27429 -T
brk(0x6d4c000)                          = 0x6d4c000 <0.000007>
brk(0x6d8c000)                          = 0x6d8c000 <0.000007>
brk(0x6dcc000)                          = 0x6dcc000 <0.000007>
brk(0x6e0c000)                          = 0x6e0c000 <0.000007>
brk(0x6e4c000)                          = 0x6e4c000 <0.000006>

雖然每次的 brk 調用響應並不慢,但次數太多了,那麽到底什麽是 brk?

brk, sbrk - change data segment size

也就是說 brk 在不斷的改變某個指針對象的内容,按照上面的偽代碼,$email 變量不斷的在調整内存大小,而且 $email 變量的内存越來越大,執行速度也越來越慢,而且執行到一定時間,php出現了内存不夠的錯誤,我做了相關調整:

ini_set('memory_limit', '500M');
$email = array();
foreach ($emailmc as $v) {
    $s     = unserialize($v);
    $s     = explode(",", $s);
    $email = array_merge($email, $s);
    echo memory_get_usage();
}

memory_limit 是限制 php 程序能夠使用的内存大小,通過 memory_get_usage 函數發現,内存使用越來越大,雖然最後代碼也能夠運行,但卻要花費至少半個小時。

call_user_func_array

對於 php 程序來說,應用代碼是涉及不到 brk 調用的,但如果能夠減少調用,程序執行時間肯定會提高很多,現在的目的就是減少 array_merge 操作,我先修改了部分代碼,分批次從 memcached 中獲取:

// 共100萬個memcached數據
$tnum = 1000000;
// 共1萬個key,每個100條memcached數據
$knum = ceil($tnum/100);
$mem->connect("localhost", "11211");
$j = 1;
for ($i = 1; $i <= $knum; $i++) {
    $k[] = $mckey."_".$i;
    if (count($k)>100) {
        $emailmc = $mem->get($k);
        foreach ($emailmc as $v) {
            $s     = unserialize($v);
            $s     = explode(",", $s);
            $emailarr[$j] = $s;
            $j++;
        }
        $k = array();
    }
}
# 要運行 100 次
for ($i=1;$i<=$j;$i++) {
    $email = array_merge($email,$emailarr[$j]);
}
importdb($email);

我分批次從 memcached 中獲取數據,然後保存到 $emailarr 數組變量中,如果再循環 array_merge,雖然速度快了一些,但仍然要100次,運行速度仍然非常慢。

我思索是不是在 php 内部能夠將 $emailarr 數組一次性合並呢?雖然有思路,但不知道具體如何操作,諮詢了 php 大牛,提出了 call_user_func_array 函數。

修改如下:

$email = call_user_func_array('array_merge', $email);
importdb($email);

代碼居然2秒就返回了,避免了由 php 應用代碼進行大量的 array_merge 合並,由 php 内部一次性完成了 array_merge。

可能有些同學說,爲啥你不能從 memcached 中獲取一部分數據就導入到數據庫中呢?主要原因是後面代碼太複雜,怕出現新的問題,所以本次的改造思路就是一次性獲取到 $email 變量對應的數據。

總結:php 應用代碼不會和系統調用直接産生聯繫,可系統調用非常昂貴,應該減少調用,所以在開發的時候,應該想象下php代碼的運行邏輯,從而提升性能。

閲完此文,您的感想如何?
  • 有用

    1

  • 沒用

    0

  • 開心

    0

  • 憤怒

    0

  • 可憐

    0

1.如文章侵犯了您的版權,請發郵件通知本站,該文章將在24小時内刪除;
2.本站標注原創的文章,轉發時煩請注明來源;
3.交流群: 2702237 13835667

相關課文
  • mac開發接入微信公衆號接口返回報錯 cURL error 56: SSLRead() return error -9806

  • PHP的換行符是什麽

  • pecl安裝程序時報錯Array and string offset access syntax with curly braces is no longer supported

  • 由於商家傳入的H5交易參數有誤,該筆交易暫時無法完成,請聯繫商家解決

我要說說
網上賓友點評