iT邦幫忙

0

PHP/Go 讀取大檔案 50M,不關閉會怎樣嗎?

  • 分享至 

  • xImage

請問一下,我在寫 Go 操作 GeoIP2 的資料庫檔案時,作法是把檔案打開,只開一次後就不關了,請求打來就直接搜 IP 城市位置,此 API 用的很頻繁,用滿久都沒碰到狀況。
但我看滿多範例是要用時打開、用完就關閉,但這樣一直開關檔案不會影響速度嗎?
特別是用 HDD 或透過網路掛載檔案時
我測 PHP 寫的是開關開關方式,大量請求打進來就非常慢,網路掛載話慢 3 倍
想問下檔案開著不關會怎麼樣嗎?
這兩種程式語言運作方式大不同,PHP 不太熟能否做到只載入一次檔案就不關閉,持續使用?
以下 PHP 範例是 ChatGPT 產的,大概是像這樣方式。
Go 為了效能我是 init 時載完放到變數就沒關了

補充,我誤會了 GeoIp2 City.mmdb 不是單純"讀取檔案"內容操作,而是作為資料庫操作。這樣我的問題已經不是上面描述了,應該是檔案操作觀念,能否只做一次載入到記憶體後,之後讀取同一份記憶體內容,還是檔案操作同資料庫連線操作?

<?php

// 開啟 GeoLite2-City.mmdb 檔案
$database = new \GeoIp2\Database\Reader('/path/to/GeoLite2-City.mmdb');

// 設定要查詢的 IP
$ip = '123.45.67.89';

try {
    // 查詢 IP 國家
    $record = $database->city($ip);
    
    // 取得國家資訊
    $country = $record->country->name;
    $isoCode = $record->country->isoCode;
    
    // 輸出結果
    echo "IP: $ip\n";
    echo "國家: $country\n";
    echo "國家代碼: $isoCode\n";
} catch (\GeoIp2\Exception\AddressNotFoundException $e) {
    // IP 查詢失敗
    echo "IP 地址未找到";
} catch (\GeoIp2\Exception\GeoIp2Exception $e) {
    // 其他錯誤
    echo "查詢時發生錯誤: " . $e->getMessage();
}

// 關閉檔案
$database->close();

?>
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
1
賽門
iT邦超人 1 級 ‧ 2023-07-06 12:48:49
最佳解答

我們寫程式,要記得只要有執行就會:

  1. 佔CPU。
  2. 佔記憶體。
    但凡Session在那裏就會有Process。
    所以程式開多了CPU和記憶體資源就不斷佔用,直到資源耗盡。
    好一些的作業系統還會想辦法用硬碟上的空間來置換那些暫停中的Session、Process,但如果置換的過程中出了差錯,就是系統Crush。
    用到硬碟空間來置換,嚴重就是效能低落,直到作業系統也撐不住就給你當機。
    所以,在寫資料庫應用的程式時,一定要記得開了什麼不用了就要關起來,免得耗用資源。
    資料庫的觀點來看,你的程式開啟了一個Session,沒有關閉的動作,資料庫就會一直保留那個Session,如果你的程式開了20個Session,然後直接關閉程式,資料還是會留著這20個Session,直到一定的時間到了,都沒有任何交易發生,資料庫管理程式會自己刪掉這些靜止中的Session。
    但問題在,如果同時有200個執行你的程式的用戶存取資料庫,就出現了4,000個靜止的Session在資料庫中,這對資料庫來說是很嚴重的效能問題。沒有一個DBA能夠忍受這種應用程式的存在的。
    如果,這些Session涉及的SQL語法又寫的很爛的時候,資料庫就直接當機給你看。
    Go語言,有說不用關閉嗎?不知道你有沒有誤解了呢?總之一個原則,開了不關就一定佔用資源,資源佔用多了,效能一定很糟。所以開了就要記得關,沒用到的一定要關,節省資源才是王道。
看更多先前的回應...收起先前的回應...
froce iT邦大師 1 級 ‧ 2023-07-06 13:25:11 檢舉

我覺得他是把資料庫和檔案的讀取方式搞混了,看GeoIp2的資料庫應該是類似sqlite的file base database。
反正不close在單人使用應該不會有什麼事,多人使用一定掛掉給你看。

vicentli iT邦研究生 4 級 ‧ 2023-07-06 18:31:53 檢舉

謝謝兩位前輩回覆,學習 Go 時,看過檔案操作範例也是要關檔案的,只是【以為那是因為每次程式運行到這,都要重複執行開檔案所以得接著關,不然開太多記憶體會爆】。運行方式不同意思是想說 Go 有啟動時執行初始動作機制,只會做一次,所以為了效能,我才寫只做一次開檔讀到記憶體,但 PHP 我不太熟,不知道有沒 init Run,才發問檔案操作性能及初始化問題。froce 說的沒錯,我搞混了,資料庫操作機制要做連線池及釋放連線,目前是有這麼做的,我以為 GeoLite2-City.mmdb 是像檔案操作,沒聯想到 sqlite,一直想著 City.mmdb 是檔案,我只是把它整個載到記憶體去讀內容
/images/emoticon/emoticon04.gif

vicentli iT邦研究生 4 級 ‧ 2023-07-06 19:02:23 檢舉

回錯地方

vicentli iT邦研究生 4 級 ‧ 2023-07-06 20:31:19 檢舉

想請問以單純檔案操作觀點話,可以把檔案內容讀到記憶體,之後做多次的查看內容而不從記憶體釋放掉嗎?ip mmdb 這個我大概了解它是資料庫操作概念,優化方向知道了
/images/emoticon/emoticon06.gif

賽門 iT邦超人 1 級 ‧ 2023-07-06 22:26:14 檢舉

單純的檔案操作,你必需要瞭解作業系統的保安機制。
讀進來的檔案,對電腦來說純粹就只是記憶體的位址。同一位址同時只能有一個Process來讀取,如果有不同的Process讀取同一位址,就會引發很多問題,這是駭客才會做的事。
例如,你執行了A Process,打開X檔案,放在變數VA裏,然後再執行B Process,也想讀取已開啟的X檔案,你的操作應該是讀取變數VA所在的記憶體內容,這是違反作業系統運作的行為。
由於是在作業系統控管的層級,不論你用何種程式語言都是相同的情況。
除非你想鑽作業系統安全漏洞,否則,Session內的事就在Session裏面搞定,別妄想多個Session讀同一變數內容的事了。
可是,網頁開發人員會說,我可以用Cookie來做到Session間交換變數內容,這是不同領域的事了,不要混在一起談。

froce iT邦大師 1 級 ‧ 2023-07-07 08:05:43 檢舉

想請問以單純檔案操作觀點話,可以把檔案內容讀到記憶體,之後做多次的查看內容而不從記憶體釋放掉嗎?

正規的做法是建cache,如下面提到的redis。
如果你確定跟你磁碟IO速度有關,可以試試建個RAM disk,啟動前放進去,但如果要多人使用請照正規作法。

整個資料讀出來放到RAM裡面是完全不建議的,資料庫檔案大小50M,讀出來之後應該不會只有50M。

vicentli iT邦研究生 4 級 ‧ 2023-07-07 09:22:42 檢舉

謝謝 froce 大,以讀檔來說,確實讀到 RAM 會不只有 50M 而是更腫大,所以確實不該用此作法,有糾正到觀念了。
賽門大說的我越來越懵了,以 go 為例,在 service 啟動時讀 env 檔或服務參數檔,把一些變數宣告好再 close 檔案,程式接著執行監聽 HTTP Port,之後請求進來讀同一個變數不是很常見程式設計邏輯嗎 @@ 這些變數不會一再去讀檔再被重新宣告,也避免有人進去改 env 或參數檔造成運行中正常的程式突然給錯誤的結果
這應該是同一個 Process?只是多請求時會以極快速度依序去讀取?

賽門 iT邦超人 1 級 ‧ 2023-07-07 10:29:22 檢舉

如force說的,利用Cache、redis,然後做成Service監聽HTTP Port,再做些API,然後大家共用同一Service,這是OK的,因為Service結束了,作業系統會自動把Service佔用的資源全部釋放,不用顧慮關不關的問題。
至於我說的就不用管了,那只是另一個情境的想法而己。

froce iT邦大師 1 級 ‧ 2023-07-07 11:04:01 檢舉

賽門大說的我越來越懵了

我是覺得賽門大也被你問到懵了。
問題的癥結其實只是你把file base database和一般的檔案搞混而已。
說真的我也沒想到你第一個問題問完,我回你那是file base database,你還會繼續問要整個讀到記憶體。
啊底層運作就不同了,你整個把db解成鍵值對,要搜尋的時候你是要怎麼搜尋啦...

vicentli iT邦研究生 4 級 ‧ 2023-07-07 11:22:28 檢舉

我真的是搞混了,後來我是也想確認一下讀一般檔案,非 file base database,例如 txt 之類的檔案觀念,一起確認下

感謝兩位前輩,經過這一輪討論下來,我也再去看了一下 Process 及記憶體文章,充實了知識

0
DennisLu
iT邦好手 1 級 ‧ 2023-07-06 13:42:15

較高頻率查詢,
大概就是要作到 資料庫與該資料庫的連線池技術去搭配程式。

[前] -----> [後+DB連線池]---->DB

連線池通常就需求量不高的時候 就跟資料庫保持最低連線,
假設設定10條常態連線,平常資料庫上會看到10條連線,
當前端打了需求到後端,後端馬上查詢,因為連線池的設計,
省略每次查詢的開關,增加反應速度,會分配使用其中一條連線去做查詢。

當需求拉高的時候10條不夠用,才會提高連線量,
這樣資料庫少了很多開開關關的處理。

依照上限設計,假設是50,當需求壓力來了就會增加連線直到50,
還不夠就是排隊到得到工作或逾時失敗。
依照觀察就是往上調整了。

消化連線池請求速度小於需求增加請求速度的時候。
會造成持續累慢性加到崩潰的狀況時,連線池上限設到不能再大DBA罵人了,
那到時候會吵一件事,SQL與schema設計是不是太爛,效能太低來不及消化。
可能有人甩鍋是硬體太差,或者DB參數調校不當。

那PostgreSQL的Table欄位屬性支援網路IP Address與子網路的表示來省資料量
以及IP相關的對應SQL語法,
國家IP資料導入要查詢一個IP是來自哪國的SQL輕鬆簡單。
也有連線池的應用(要架另外一個服務),是可以考慮。
(不是在推廣PostgreSQL,只是剛好有用過)

vicentli iT邦研究生 4 級 ‧ 2023-07-06 19:35:17 檢舉

還沒摸過 PostgreSQL,謝謝你的回覆,又多了解一項知識

0
fuzzylee1688
iT邦研究生 3 級 ‧ 2023-07-06 13:42:32

善用3-Tier architecture, 持續連結會有明顯效果的.

vicentli iT邦研究生 4 級 ‧ 2023-07-06 21:05:16 檢舉

我一開始就誤會了,用單純操作檔案的概念來看從 mmdb 檔案內抓資料。
3-Tier architecture 是把 mmdb 內資料放到資料庫中,然後做 keep alive 連線池概念嗎

fuzzylee1688 iT邦研究生 3 級 ‧ 2023-07-07 10:27:38 檢舉

3-Tier architecture 也能針對非結構化DB處理, 最主要AP SERVER與 Data SERVER的分工, 不見得要用DB.

0
MatthewWangUS
iT邦新手 3 級 ‧ 2023-07-06 15:07:59

把資料想辦法塞進redis . 用 redis 當資料庫負責查詢。會比用好又快

vicentli iT邦研究生 4 級 ‧ 2023-07-06 19:19:08 檢舉

爬了一下,有提供 csv 格式,那就可以做到塞到 redis,謝謝🙏

0
whitefloor
iT邦研究生 2 級 ‧ 2023-07-06 16:25:53

你應該是想一下用其他手段增進performamce
local memory能少用也是盡量少用
不然connection/body reader之類的不關
哪天你就會看到程式三不五時就panic給你看
在code review的時候還會被罵

vicentli iT邦研究生 4 級 ‧ 2023-07-06 19:31:58 檢舉

對喔,主要是想看怎麼增進效能,看完這篇討論也找了一下 geoip 有提供 csv 格式可以塞到資料庫

0
rehan
iT邦見習生 ‧ 2023-07-06 18:08:49

你應該是想一下用其他手段增進performamce
local memory能少用也是盡量少用
不然connection/body reader之類的不關
哪天你就會看到程式三不五時就panic給你看
在code review https://fontsbunch.com

我要發表回答

立即登入回答