請問一下,我在寫 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();
?>
我們寫程式,要記得只要有執行就會:
我覺得他是把資料庫和檔案的讀取方式搞混了,看GeoIp2的資料庫應該是類似sqlite的file base database。
反正不close在單人使用應該不會有什麼事,多人使用一定掛掉給你看。
謝謝兩位前輩回覆,學習 Go 時,看過檔案操作範例也是要關檔案的,只是【以為那是因為每次程式運行到這,都要重複執行開檔案所以得接著關,不然開太多記憶體會爆】。運行方式不同意思是想說 Go 有啟動時執行初始動作機制,只會做一次,所以為了效能,我才寫只做一次開檔讀到記憶體,但 PHP 我不太熟,不知道有沒 init Run,才發問檔案操作性能及初始化問題。froce 說的沒錯,我搞混了,資料庫操作機制要做連線池及釋放連線,目前是有這麼做的,我以為 GeoLite2-City.mmdb 是像檔案操作,沒聯想到 sqlite,一直想著 City.mmdb 是檔案,我只是把它整個載到記憶體去讀內容
回錯地方
想請問以單純檔案操作觀點話,可以把檔案內容讀到記憶體,之後做多次的查看內容而不從記憶體釋放掉嗎?ip mmdb 這個我大概了解它是資料庫操作概念,優化方向知道了
單純的檔案操作,你必需要瞭解作業系統的保安機制。
讀進來的檔案,對電腦來說純粹就只是記憶體的位址。同一位址同時只能有一個Process來讀取,如果有不同的Process讀取同一位址,就會引發很多問題,這是駭客才會做的事。
例如,你執行了A Process,打開X檔案,放在變數VA裏,然後再執行B Process,也想讀取已開啟的X檔案,你的操作應該是讀取變數VA所在的記憶體內容,這是違反作業系統運作的行為。
由於是在作業系統控管的層級,不論你用何種程式語言都是相同的情況。
除非你想鑽作業系統安全漏洞,否則,Session內的事就在Session裏面搞定,別妄想多個Session讀同一變數內容的事了。
可是,網頁開發人員會說,我可以用Cookie來做到Session間交換變數內容,這是不同領域的事了,不要混在一起談。
想請問以單純檔案操作觀點話,可以把檔案內容讀到記憶體,之後做多次的查看內容而不從記憶體釋放掉嗎?
正規的做法是建cache,如下面提到的redis。
如果你確定跟你磁碟IO速度有關,可以試試建個RAM disk,啟動前放進去,但如果要多人使用請照正規作法。
整個資料讀出來放到RAM裡面是完全不建議的,資料庫檔案大小50M,讀出來之後應該不會只有50M。
謝謝 froce 大,以讀檔來說,確實讀到 RAM 會不只有 50M 而是更腫大,所以確實不該用此作法,有糾正到觀念了。
賽門大說的我越來越懵了,以 go 為例,在 service 啟動時讀 env 檔或服務參數檔,把一些變數宣告好再 close 檔案,程式接著執行監聽 HTTP Port,之後請求進來讀同一個變數不是很常見程式設計邏輯嗎 @@ 這些變數不會一再去讀檔再被重新宣告,也避免有人進去改 env 或參數檔造成運行中正常的程式突然給錯誤的結果
這應該是同一個 Process?只是多請求時會以極快速度依序去讀取?
如force說的,利用Cache、redis,然後做成Service監聽HTTP Port,再做些API,然後大家共用同一Service,這是OK的,因為Service結束了,作業系統會自動把Service佔用的資源全部釋放,不用顧慮關不關的問題。
至於我說的就不用管了,那只是另一個情境的想法而己。
賽門大說的我越來越懵了
我是覺得賽門大也被你問到懵了。
問題的癥結其實只是你把file base database和一般的檔案搞混而已。
說真的我也沒想到你第一個問題問完,我回你那是file base database,你還會繼續問要整個讀到記憶體。
啊底層運作就不同了,你整個把db解成鍵值對,要搜尋的時候你是要怎麼搜尋啦...
我真的是搞混了,後來我是也想確認一下讀一般檔案,非 file base database,例如 txt 之類的檔案觀念,一起確認下
感謝兩位前輩,經過這一輪討論下來,我也再去看了一下 Process 及記憶體文章,充實了知識
較高頻率查詢,
大概就是要作到 資料庫與該資料庫的連線池技術去搭配程式。
[前] -----> [後+DB連線池]---->DB
連線池通常就需求量不高的時候 就跟資料庫保持最低連線,
假設設定10條常態連線,平常資料庫上會看到10條連線,
當前端打了需求到後端,後端馬上查詢,因為連線池的設計,
省略每次查詢的開關,增加反應速度,會分配使用其中一條連線去做查詢。
當需求拉高的時候10條不夠用,才會提高連線量,
這樣資料庫少了很多開開關關的處理。
依照上限設計,假設是50,當需求壓力來了就會增加連線直到50,
還不夠就是排隊到得到工作或逾時失敗。
依照觀察就是往上調整了。
消化連線池請求速度小於需求增加請求速度的時候。
會造成持續累慢性加到崩潰的狀況時,連線池上限設到不能再大DBA罵人了,
那到時候會吵一件事,SQL與schema設計是不是太爛,效能太低來不及消化。
可能有人甩鍋是硬體太差,或者DB參數調校不當。
那PostgreSQL的Table欄位屬性支援網路IP Address與子網路的表示來省資料量
以及IP相關的對應SQL語法,
國家IP資料導入要查詢一個IP是來自哪國的SQL輕鬆簡單。
也有連線池的應用(要架另外一個服務),是可以考慮。
(不是在推廣PostgreSQL,只是剛好有用過)
善用3-Tier architecture, 持續連結會有明顯效果的.
把資料想辦法塞進redis . 用 redis 當資料庫負責查詢。會比用好又快
你應該是想一下用其他手段增進performamce
local memory能少用也是盡量少用
不然connection/body reader之類的不關
哪天你就會看到程式三不五時就panic給你看
在code review的時候還會被罵
你應該是想一下用其他手段增進performamce
local memory能少用也是盡量少用
不然connection/body reader之類的不關
哪天你就會看到程式三不五時就panic給你看
在code review https://fontsbunch.com