零、問題背景
??????? 最近想換房,為了方便自己對比感興趣的房子,因此決定將目標房源的基本信息放在表里,特別是要一目了然的看到眾多房子的各種圖紙和照片,因此決定要在Mysql8.0.34數據庫中以二進制形式保存圖片(拋開合理性和性能暫且不談,這里只說技術實現),許多帖子說使用load_file函數來實現,但我按照其他人的方法實操時一直返回NULL,還好歷經千辛萬苦終于解決了,現將過程與思路加以總結,存檔留念。
一、準備工作
1.1、服務器與數據庫版本
??????? 我是用 rpm 方式安裝的 MySQL 8.0.23 版本,服務器操作系統是Centos 7.9 虛擬機資源? 2C 4G 40G
1.2、表結構
??????? 下圖是我的表結構,其中有幾個字段是 mediumblob類型,用來存放大小約為200k以內的圖片。
1.3、圖片保存位置
??????? 為了便于后續業務擴展,也出于數據隔離的需要,我用服務器上自定義了 /data/pic/house 路徑作為當前業務存儲圖片的根目錄。并在其下面創建了 housepic 和 roomtype兩個路徑。
二、問題分步展示
2.1、問題展示
??????? 我將一張名為 h_1.jpg 的圖片放到? /data/pic/house/roomtype 下,然后使用如下語句嘗試查看圖片時沒能返回預期圖片內容,而是返回了NULL。
select load_file('/data/pic/house/roomtype/h_1.jpg');
2.2、原因及secure_file_priv介紹
???????? load_file 函數返回為 null 的原因,根源在于 MySQL 配置文件中的 “secure_file_priv” 屬性取值與load_file 函數使用的路徑不一。在具體解決問題之前,應該先了解這一屬性的作用和取值范圍是什么。
2.2.1、secure_file_priv介紹
??????? 該屬性有三類取值,分別為 "NULL"、空值、具體路徑字符串。不同取值的含義如下表所示
取值 | 含義 |
NULL | 不允許讀寫任何路徑下的文件 |
'' 或 沒有值 | 允許讀寫任意路徑下的文件 |
字符串(如 /tmp) | 允許讀寫當前路徑(如 /tmp)下的文件 |
2.2.2、secure_file_priv 查看
??????? 在MySQL任意一種客戶端中輸入如下命令查看。secure_file_priv的默認取值如下圖所示,為 “/var/lib/mysql-files/”。說明當前load_file 函數只能操作 “/var/lib/mysql-files/” 路徑下的文件。
show variables like '%secure%';
??????
2.2.3、secure_file_priv 修改
??????? 通過上面的分析已經知道了問題的原因,下面就是在MySQL的配置文件中修改這一參數的取值。因為我的MySQL是rpm安裝版,所以配置文件的名稱與路徑為: /etc/my.cnf (其他版本操作系統下的MySQL的配置文件位置與名稱請自行確認)。同時我希望 MySQL 不限制文件路徑,因此 secure_file_priv 取值及展示如下所示:
#關閉mysql的文件讀寫路徑限制,取值為空
secure-file-priv= ''
??????? 需要注意的一點就是,配置文件中應寫成 “secure-file-priv” 而非“secure_file_priv”的形式,切切!
2.3、效果與終極解決方法
??????? 如果逐位按照上面的方法作了修改,大概率是依然不成功。原因在于沒有關閉selinux 。我之前就在selinux 這個坑中掙扎了好久才爬出來。下面就簡單聊聊 selinux 是啥,以及為什么會對這個參數有這么大的影響。
2.3.1、selinux是什么、能做什么
??????? 它是“安全增強型 Linux(Security-Enhanced Linux)”的簡稱,它是 Linux 的一個安全子系統。
??????? 它能對linux系統中,包括root在內的所有用戶的行為做管制。它能通過一系列的配置實現 “允許 X 目標,對 Z 對象/資源,做 Y行為 ” (May <subject> do <action> to <object/source>)的訪問控制。其中,X多指進程;Y多指X訪問的資源,如端口、文件等;Z多指訪問或操作規則。
??????? 它有三種工作模式,如下表所示:
模式名稱 | 作用 |
enforcing | 強制模式。阻止并在日志記錄違反SELinux規則的行為 |
permissive | 寬容模式。僅在日志記錄違反SELinux規則的行為 |
disabled | 關閉模式。關閉SELinux |
??????? selinux 是一套相當復雜的子系統,里面有龐大的配置項需要了解,在此不做進一步的解釋,只需要知道它會對 MySQL 的關鍵行為做校驗與限制即可。除非是專門負責安全和服務器運維的工程師需要深入了解selinux各種規則之外,一般的開發人員在遇到linux 的一些校驗失敗的情況時,可以想到有這樣一個東西存在,并且知道通過關閉selinux可以解決大部分的安全校驗問題即可。
2.3.2、selinux 的關閉與開啟
??????? selinux 的關閉分為臨時關閉和永久關閉,下圖為臨時關閉方式(即設為寬容模式)
????????
??????? 下面為永久關閉方法
# 修改 /etc/selinux/config 文件中的 SELINUX=disabled
[root@MySQL01 ~]# vim /etc/selinux/config# 修改 /etc/sysconfig/selinux 文件中的 SELINUX=disabled
[root@MySQL01 ~]# vim /etc/sysconfig/selinux
2.4、最終效果
??????? 在MySQL 的 secure_file_priv參數設置為理想的值,并且關閉了 selinux 之后,發現可以正常讀取到圖片了,如下圖所示
2.5、補充說明
??????? 存放圖片的路徑,建議放到 mysql 屬組,且賦予 對應的權限。如下圖所示
????????
三、說在最后
9.1 如何查看centos 版本
cat /etc/redhat-release
9.2、如何查看MySQL版本
select VERSION();
9.3、load_file 函數的作用與效率
??????? 它在 MySQL 中的主要作用是讀取本地或遠程的文件。文件種類涵蓋文本、二進制、圖片、視頻等。因這樣的讀取方式不夠安全,因此建議在受信的環境中使用。
9.4、MySQL 幾種操作圖片的思路和對比
??????? 可以通過load_file 函數將圖片記錄到字段中,也可以將圖片所在的路徑記錄到字段中。前者存在安全隱患,后者無法在MySQL中直接看到圖片內容。因此對圖片的處理方式應根據業務需求等因素綜合考慮后選擇。