php中持久化儲存模組開發總結

2021-12-29 20:02:41 字數 2099 閱讀 6504

在專案中發現經常有這種需求,需要載入一些大的固定的格式化資料,比如對戰中的一些技能資料,物品等。這些資料都是唯讀資料,並且可能會比較大,目前來看大約有上萬條複雜資料,如果serialize的話,純文字有20m左右。嘗試過直接放乙個array在php檔案裡,結果發現require這個檔案很耗時,可能會花費幾十ms的時間,並且這個時候io很重,因為需要載入幾十m資料到記憶體;另外去調研了一下sqlite,這個東西還算比較靠譜,但問題在於,比如寫操作函式,使用起來很不爽;於是就產生了自己寫乙個擴充套件的想法。於是折騰之旅就此展開。

一開始想的是,直接在minit裡呼叫zend_execute_script方法來載入乙個php檔案,返回乙個zval來儲存到全域性變數裡。結果後來仔細一琢磨發現根本就是妄想。原因在於minit的時候php的vm還沒初始化完,不可能讓你呼叫zend_execute_script方法,並且這個方法也不會返回乙個zval,要想拿到zval必須從eg中去拿,很麻煩。

於是轉換思路,嘗試用unserialize/serialize,結果發現,php_var_unserialize在minit階段果然是可以呼叫的。於是開搞,呼叫這個方法得到乙個zval,然後存在全域性變數裡,在get方法裡返回這個zval。寫完之後,在測試的時候杯具的發現,只要呼叫就會core呀。於是查文件,自己思考,最終發現php_rshutdown_function函式裡會將所有非pealloc分配的的變數給清除。因此在minit階段還正常的資料,到了request階段已經被free了。

於是再查文件,發現php裡提供了pealloc這類函式來提供persistent的資料分配。於是再轉換思路,將全域性變數裡的hashtable用pealloc來分配,並且將hastable設定成persistent的(謝天謝地php的hashtable還要存**和vm,因此有這個功能)。但是杯具的是php_unserialize只會返回乙個zval,你根本無法控制它是否是persistent的。沒辦法,只能呼叫zend_hash_copy來做了。寫完之後再測試,發現還是core,這就不明白了,為啥呢?中午吃飯的時候,突然想到,可能是淺拷貝的問題,zend_hash_copy提供了乙個copy函式而我沒有設定它。再加上深拷貝函式之後再測試,發現果然可以用了,使用起來很清爽。 www.2cto.com

接下來進行測試發現,記憶體使用率不能忍,乙個20m的資料檔案載入到記憶體,需要大約100m左右的記憶體,如果有100個php-cgi程序,那就多要10g記憶體,這根本不能忍。於是設想,可以用共享記憶體來解決這個問題,反正這部分資料只要能讀就行了。php-cgi的主程序負責minit操作,子程序只要讀這部分資料就行了。但是很麻煩的是,php裡沒提供任何讓使用者維護記憶體的介面,於是只能乙個函式乙個函式的扒了。

仔細看了一下php的hashtable實現,發現比較複雜,而且關鍵用到了realloc函式,這個太讓人無語了,總不能我也寫乙個記憶體管理吧。目前只用共享記憶體實現了乙個簡單的執行緒分配記憶體的函式,從共享記憶體上依次往後分配空間。但是還好,這部分功能是resize功能根本不需要。因為我的目標是將php_var_unserialize裡得到的zval拷貝到共享記憶體而已,而大小我明顯已經知道了。並且也不需要updatea功能,因為是全新的copy。最終弄完之後,發現可以使用了,記憶體使用率果然降了。

接下來進行壓力測試,突然發現又開始core了,這根本不能忍呀,為啥呀?根據core檔案,發現是裡面的hashtable的refcount降到0了。於是各種測試,發現單執行緒情況下是ok的,只有多執行緒大壓力情況下會掛。於是想到refcount是會被修改的,而且多執行緒修改的話,必須可能被改亂。那怎麼辦呢?總不能加鎖吧。

後來仔細想了一下,突然想到只要我每次在返回這個zval裡將頂層的zval的refcount修改為大於php-cgi程序數的值,那即使會被改亂也沒啥問題,因為根本不會改到0。於是修改了之後再測試,發現果然靠譜了。

到此,整個問題基本解決。但是還有另外乙個問題,在重啟php-cgi時還是會core,原因是,當時把正在使用的一些變數給強制寫成0了。其實共享記憶體的正確用法是,乙個程序來寫,另外的程序來讀,但是我這個應用裡將共享記憶體當做絕對位址在使用,因此不可能在乙個地方寫,在其他地方讀,除非shmat裡的第二個引數修改為固定值,但是這個就需要對程序的位址分配有充分的了解,知道哪些記憶體根本不可能被使用。不過這個應該還好,因為php-cgi程序有記憶體上限,所以應該可以找到一塊內存在php-cgi執行過程中無法被用到的。不過具體的情況得接下來具體研究一下。

作者 無心雲

php中持久化儲存模組開發總結

在專案中發現經常有這種需求,需要載入一些大的固定的格式化資料,比如對戰中的一些技能資料,物品等。這些資料都是唯讀資料,並且可能會比較大,目前來看大約有上萬條複雜資料,如果serialize的話,純文字有20m左右。嘗試過直接放乙個array在php檔案裡,結果發現require這個檔案很耗時,可能會...

ios開發之資料的持久化儲存機制

ios中資料的持久化儲存這塊內容,類似於android中檔案的幾種常見的儲存方式。對於資料的持久化儲存,ios中一般提供了4種不同的機制。1.屬性列表 2.物件歸檔 3.資料庫儲存 sqlite3 4.蘋果公司提供的永續性工具core data。其實儲存的形式無非就這麼幾種,而我們還必須要關心的是,...

Servlet 關於http中的持久化儲存

http 協議是無狀態協議 即是每一次的請求是不會知道上一次的訪問結果的,不會相關的痕跡 session 在伺服器端產生,客戶端是沒有session,但是jsid儲存在cookie 臨時會話 中 cookie 客戶端和伺服器端都是會存在的 cookie的使用 1.建立cookie 物件 直接通過ne...