Home | 簡體中文 | 繁體中文 | 雜文 | 知乎專欄 | Github | OSChina 博客 | 雲社區 | 雲棲社區 | Facebook | Linkedin | 視頻教程 | 打賞(Donations) | About
知乎專欄多維度架構 微信號 netkiller-ebook | QQ群:128659835 請註明“讀者”

16.3. 資料庫與外界檔案

你是是不是在開發中常常遇到,刪除了資料庫記錄後,發現該記錄對應的圖片沒有刪除,或者刪除了圖片,資料庫中仍有數據存在,你的網站臟數據(圖片)成幾何數增長,閲讀下文這裡為你提供了一個完美決方案。

16.3.1. 背景

我以電商網站為例,一般的網站產品數據存放在資料庫中,商品圖片是上傳到檔案伺服器,然後通過http伺服器瀏覽商品圖片。這是最基本的也是最常見做法。

稍複雜的方案是,如果圖片數量龐大,會使用分散式檔案系統方案。但是這些方案都不能保證數據的完整性,極易產生臟數據(垃圾數據)。臟數據是指當你刪除了資料庫表中的記錄後,圖片仍然存在,或者手工刪除了圖片,而資料庫中的記錄仍然存在。

將圖片放入資料庫中存放在BLOB的方法可以解決臟數據問題,典型的案例是公安的身份證系統。但這種方案的前提是,圖片不能太大,數量不多,訪問量不大。 這顯然不適合電商網站。

2009年我在走秀網工作,商品圖片與縮圖檔案900GB到2012離職已經有10TB,每天有成百上千的商品上架下架,很多商品下架後永遠不會再上架,這些批量下架的商品數據不會刪除,僅僅標記為刪除,總是期望以後能繼續使用,實際上再也不會有人過問,另一方面隨着品類經理頻繁更換,員工離職,這些商品會石沉大海,再也無人問均。這些商品所對應的圖片也就臟數據主要來源。新的品類經理上任後,會重新拍照,上傳新圖片。

總之,刪除資料庫中的數據不能將圖片刪除就會產生臟數據。很多採用刪除數據的時候去檢查圖片如果存在先刪除圖片,再刪除數據的方法。這種方案也非完美解決方案,存在這圖片先被刪除,程序出錯SQL沒有運行,或者反之。

16.3.2. 解決思路

如果刪除圖片能夠成為事物處理中的一個環節,所有問題都能迎刃而解,可徹底解決臟數據的煩惱。

16.3.3. 解決方案

mysql plugin 開發 udf。我寫幾個function

UDF

image_check(filename)

檢查圖片是否存在.

image_remove(filename)

刪除圖片.

image_rename(oldfile,newfile)

更改圖片檔案名.

image_md5sum(filename)

md5sum 主要用戶圖片是否被更改過.

image_move(filename,filename)

移動圖片的位置

有了上面的function後你就可以在begin,commit,rollback 直接穿插使用,實現在事物處理期間做你愛做的事。

16.3.4. plugin 的開發與使用

編譯UDF你需要安裝下面的軟件包

sudo apt-get install pkg-config
sudo apt-get install libmysqlclient-dev

sudo apt-get install gcc gcc-c++ make automake autoconf
		

https://github.com/netkiller/mysql-image-plugin

編譯udf,最後將so檔案複製到 /usr/lib/mysql/plugin/

git clone https://github.com/netkiller/mysql-image-plugin.git
cd mysql-image-plugin/src

gcc -I/usr/include/mysql -I./ -fPIC -shared -o image.so image.c
sudo mv image.so /usr/lib/mysql/plugin/
		

裝載

create function image_check returns boolean soname 'images.so';
create function image_remove returns boolean soname 'images.so';
create function image_rename returns boolean soname 'images.so';
create function image_md5sum returns string soname 'images.so';
create function image_move returns string soname 'images.so';
		

卸載

drop function image_check;
drop function image_remove;
drop function image_rename;
drop function image_md5sum;
drop function image_move;
		

16.3.5. 在事務中使用該插件

插入圖片流程,上傳圖片後,通過插件檢查圖片是否正確上傳,然後插入記錄

begin;
IF image_check('/path/to/images.jpg') THEN
	insert into images(product_id,thumbnail,original) values(1000,'thumbnail/path/to/images.jpg','original/path/to/images.jpg');
	commit;
ELSE
	image_remove('/path/to/images.jpg');
END IF
rollback;
		

刪除商品採用image_move 方案,當出現異常rollback後還可以還原被刪除的圖片

begin;
IF image_check('/path/to/images.jpg') THEN
	select thumbnail,original into @thumbnail,@original from images where id='1000' for delete;
	delete from images where id='1000';
	select image_move(@thumbnail,'recycle/path/to/');
	select image_move(@original,'recycle/path/to/');
	commit;
END IF

rollback;
select image_move('recycle/path/to/images.jpg','path/to/images.jpg');
		

我們可以使用EVENT定時刪除資源回收筒內的圖片

image_remove('recycle/path/to/images.jpg');
		

16.3.6. 通過觸發器調用圖片處理函數

通過觸發器更能保證數據完整性

1. insert 觸發器的任務: 插入記錄的時候通過image_check檢查圖片是否正常上傳,如果非沒有上傳,數據插入失敗。
2. delete 觸發器的任務: 檢查刪除記錄的時候,首先去刪除圖片,刪除成功再刪除該記錄。
		

觸發器進一步優化

1. insert 觸發器的任務: 插入記錄的時候通過image_check檢查圖片是否正常上傳,如果非沒有上傳,數據插入失敗。如果上傳成功再做image_md5sum 進行校驗100% 正確後插入記錄
2. delete 觸發器的任務: 檢查刪除記錄的時候,首先去改圖片檔案名,然後刪除該記錄,最後刪除圖片,刪除成功。如果中間環境失敗 記錄會rollback,圖片會在次修改檔案名改回來。100% 保險