iT邦幫忙

0

php CPU使用率50%

php

大家好^^
我寫了一小段程式
要從電腦192.168.0.20資料庫rich88
中的table "person"
複製到
電腦192.168.1.30資料庫rich99
中的table "tperson"
最後寫入一個檔案"patient"
程式可以執行
可是我發現
cpu使用率高達50%
我弄不懂?.?
請大家幫幫忙
謝謝大家^^

<?php
   $STORE_ID='888';
   $conn=mysql_connect('192.168.0.20:3388','root','');
   if(!$conn)
   {
    die('Could not connect: '.mysql_error());
   }
   mysql_select_db('rich88');
   $query="SELECT * FROM person";
   $result=mysql_query($query);
   $new=mysql_numrows($result);
   if(!$result)
   {
    die('Query failed: ' . mysql_error());
   }
   $conn2=mysql_connect('192.168.1.30:3330','root','');
   if(!$conn2)
    {
     die('Could not connect: '.mysql_error());
    }
   mysql_select_db('rich99');
   $i=0;
   while($i<$new)
   {
    $ID=mysql_result($result,$i,"ID");
    $NAME=mysql_result($result,$i,"NAME");
    $BDAY=mysql_result($result,$i,"BDAY");
    $BNO=mysql_result($result,$i,"BNO");
    $TEL=mysql_result($result,$i,"TEL1");
    //寫入table tperson
    $bobo="REPLACE INTO tperson(STORE_ID,ID,NAME,BDAY,BNO,TEL1)
                          VALUES('$STORE_ID','$ID','$NAME','$BDAY','$BNO','$TEL')";
    if(!mysql_query($bobo))
       {
       die('Query failed: ' . mysql_error());
       }
    $ID2=STR_PAD(mysql_result($result,$i,"ID"),10,' ',STR_PAD_RIGHT);
    $NAME2=STR_PAD(mysql_result($result,$i,"NAME"),20,' ',STR_PAD_RIGHT);
    $BDAY2=STR_PAD(mysql_result($result,$i,"BDAY"),9,' ',STR_PAD_RIGHT);
    $BNO2=STR_PAD(mysql_result($result,$i,"BNO"),10,' ',STR_PAD_RIGHT);
    $TEL2=STR_PAD(mysql_result($result,$i,"TEL1"),16,' ',STR_PAD_RIGHT);
    //合併字串
    $aa="$STORE_ID"."        "."$ID2"."$NAME2"."$BDAY2"."$BNO2"."$TEL2";
      if(!$f=fopen("D:\patient","a+"))
      {
      echo "Cannot open file D:\patient";
      exit;
      }
    //寫入檔案
    fwrite($f,$aa);
    fclose($f);
    $i++;
   }
   mysql_free_result($result);
   mysql_close($conn);
   mysql_close($conn2);
?>
另外是否有這樣的可能?
透過 mysql 與 mysql 之間,
來做table的複製。
再用內部的功能或指令來新增欄位及內容,
再replace into到所要的表格。
fillano iT邦超人 1 級 ‧ 2010-04-09 11:41:31 檢舉
據說已經這樣做了:
http://ithelp.ithome.com.tw/question/10040698

只是最後用php做的事情...這些如果用trigger可以做出來,也許會比較好...

另外就是架構設計的問題...因為不知道實際需求,所以很難置喙。他有在study area問同樣的問題,但是好像沒人回...
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
4
fillano
iT邦超人 1 級 ‧ 2010-04-07 10:31:45
最佳解答

在迴圈中的檔案I/O,例如你用的fwrite()通常會是效能瓶頸,建議你把寫入檔案的內容先放進字串,最後才一次寫入檔案。

看更多先前的回應...收起先前的回應...
tom0705 iT邦新手 5 級 ‧ 2010-04-07 11:44:40 檢舉

謝謝您^^
我之所以一筆一筆寫入
是因為為了達到"及時"的要求^^|||
因為一整天下來有數千筆資料要新增與修改更新
如果分成2~6次寫入
資料會有時間差
這部分我沒說清楚
真抱歉^^
謝謝您

就如fillano所言,先拿掉寫入 patient 的動作看看,
是否就減少了 loading?
如果有減輕的話,就是要改這邊的寫入法。
如果沒減輕的話,就要看其他的部份。

fillano iT邦超人 1 級 ‧ 2010-04-08 09:58:34 檢舉

我並沒有叫你不要做資料庫的動作阿?我只是叫你把fwrite移出while迴圈試試看...Orz

這跟「一整天有數千筆資料」及「分2~6次寫入」怎麼會有關係?這我就不了解了...

另外一點我有點好奇,就是你這支php程式是怎麼觸發的?

fillano iT邦超人 1 級 ‧ 2010-04-08 15:31:13 檢舉

我不知道你寫入的這個patient會怎麼用,有即時性的需求嗎?還是只是當作log?

針對檔案I/O,你的程式可以做兩個階段的調整:

第一是把fopen以及fclose移到while迴圈外面。既然你已經用"a+"來開啟檔案,每次寫入的東西一定都在檔案結尾,那完全沒必要在迴圈內做fopen/fclose。

第二是把file I/O全部移到while迴圈外面做,迴圈裡面用一個字串變數來紀錄要寫入檔案的內容。不過這樣做需要考慮到記憶體使用的限制以及檔案每次寫入大小的限制。

我知道你這個問題是接續上一次的問題來的,你資料的細節我不是很確定,不過每次都用"SELECT * FROM person"來開始,等到你資料累積到百萬、千萬筆,這個程式的效率就...也許你可以考慮只針對差異的部份進行處理比較好。(不知道詳細的資料結構與需求,我也不確定是否做的到,但是最好盡量朝這個方向努力)

tom0705 iT邦新手 5 級 ‧ 2010-04-11 16:40:03 檢舉

您好
fwrite和fclose已經改到迴圈外
不過cpu loading還是很重
所以我改變了select
把範圍縮小
loading就好多了
我是利用排程觸發這支程式地^^
謝謝您

6
逮丸逮丸
iT邦大師 1 級 ‧ 2010-04-07 10:19:38

前提
如果 192.168.1.30 的 tperson 是不會有異動的話,
可以用這種覆蓋的方式進行;
如果 tperson 也是一直有異動情形的話,
就不適此方法。
*nix平台適用,windows平台不適用。

基本做法
我目前也有需要複製整個table到另台主機的需要,
是以簡單的指令方式完成,而不用php;
主要的方式是:
在A主機以 root 身份複製該資料表的檔案到 web 的某目錄。
在B主機也以 root 身份抓 A 目錄的資料表檔案,
蓋掉 B 的資料表,再重啟動 mysql。

執行方式
以下模擬您要複製的程序:
在 192.168.0.20 上以 root 身份來跑 cron,
假設每天 13,19,23 點複製:

<pre class="c" name="code">0 13,19,23 * * * /home/myscript/getfile.sh > /dev/null 2>&1

getfile.sh 的內容

<pre class="c" name="code">
#!/bin/sh
/bin/cp /var/lib/mysql/rich88/person.* /WEB/htdocs/DIR/
/bin/chown apache:apache /WEB/htdocs/DIR/*

在 192.168.1.30 也以root跑以下的 cron,
多個二分鐘後執行:

<pre class="c" name="code">2 13,19,23 * * * /home/somehome/updatedb.sh > /dev/null 2>&1

updatedb.sh 的內容:

<pre class="c" name="code">
#!/bin/sh
wget -q http://192.168.1.30/DIR/person.MYD -O /var/lib/mysql/rich99/tperson.MYD
wget -q http://192.168.1.30/DIR/person.MYI -O /var/lib/mysql/rich99/tperson.MYI
wget -q http://192.168.1.30/DIR/person.frm -O /var/lib/mysql/rich99/tperson.frm
/etc/init.d/mysqld restart > /dev/null 2>&1

這樣就達到table的複製目的。

類似可行方案
當然,所放在網頁的目錄要保護好。
或者同理用 scp 或 rsync 等方式來取代 wget 的傳檔方式。

tom0705 iT邦新手 5 級 ‧ 2010-04-07 11:49:46 檢舉

謝謝您^^
192.168.1.30的tperson是有異動的
(前面加了store_id這個欄位,目的要區分從哪裡來的資料)
另外
我之所以一筆一筆寫入
是因為為了達到"及時"的要求^^|||
因為一整天下來有數千筆資料要新增與修改更新
如果分成2~6次寫入
資料會有時間差
這部分我沒說清楚
真抱歉^^
我的問題是
如何降低cpu的loading呢?
謝謝您^^

仍提供一個不用 php 的方式,
類似上述的複製檔案的程序,
把 複製、取代 的動作,
改為 mysqldump、mysql < dump.sql 的動作。

就依照您需增一欄位、replace into的方式:
在 192.168.0.20 上執行:

&lt;pre class="c" name="code">mysqldump -u DBUSER -pMYPASS rich88 person | sed -e '1,/Dumping data for/d;; s/INSERT/REPLACE/g; s/(/(888,/g; s/`person`/`tperson`/g' |  > dump.sql

看看這樣子的 dump.sql 符合不符合您所要的改變內容?
如果合的話,就可以在 192.168.1.30 執行

&lt;pre class="c" name="code">mysql -u USER -pPASS rich99 &lt; dump.sql

應該就與您 php 要做的動作結果是相同的。

如果上面用dump的方式符合您需要,
到時再討論怎麼寫到 patient 的方式,
及怎麼處理STORE_ID、即時一次執行完畢等問題。

基本上,這種從作業系統處理的方式,
一定比php一筆一筆處理快。

fillano iT邦超人 1 級 ‧ 2010-04-09 17:05:48 檢舉

嗯嗯,這個方式也不錯。不過他好像是用windows作業系統,但是應該有類似的處理方法。

2
godstamp
iT邦新手 3 級 ‧ 2010-04-11 15:27:15

也許可以換一種做法
192.168.1.30 也建立一個 rich88 資料庫
然後在兩台機器設定資料庫同步,讓192.168.0.20和192.168.1.30的資料庫 rich88 同步
這樣子雙方的 rich88 就能更新維持相同的內容
此時如果需要在192.168.1.30定時取得 rich88 的資料重新編排處理並記錄到 rich99 中
可能不需要使用到php,直接排程執行SQL語法即可,這樣即使資料量很大也會非常快速完成工作
若有其他需求也可寫php直接對同一台機器撈取需要的資料出來處理,避免耗掉不必要的資源

以上意見,希望對你會有幫助^^

我要發表回答

立即登入回答