MySQL45講讀書筆記 42講grant之後要跟着flushprivileges嗎

一 序

  本文屬於極客時間MySQL45講讀書筆記系列。授權操作是開發同學瞭解,應該還是dba日常操作。

 在MySQL裏面,grant語句是用來給用戶賦權的。grant之後真的需要執行flush privileges嗎?如果沒有執行這個flush命令的話,賦權語句真的不能生效嗎?

接下來,我就先和你介紹一下grant語句和flush privileges語句分別做了什麼事情,然後再一起來分析這個問題。

爲了便於說明,我先創建一個用戶:

create user 'ua'@'%' identified by 'pa';

這條語句的邏輯是創建一個用戶’ua’@’%’,密碼是pa。注意,在MySQL裏面,用戶名(user)+地址(host)才表示一個用戶,因此 ua@ip1 和 ua@ip2代表的是兩個不同的用戶。

這條命令做了兩個動作:

  1. 磁盤上,往mysql.user表裏插入一行,由於沒有指定權限,所以這行數據上所有表示權限的字段的值都是N;

  2. 內存裏,往數組acl_users裏插入一個acl_user對象,這個對象的access字段值爲0。

圖1就是這個時刻用戶ua在user表中的狀態。

圖1 mysql.user 數據行

在MySQL中,用戶權限是有不同的範圍的。接下來,我就按照用戶權限範圍從大到小的順序依次和你說明。

全局權限

全局權限,作用於整個MySQL實例,這些權限信息保存在mysql庫的user表裏。如果我要給用戶ua賦一個最高權限的話,語句是這麼寫的:

grant all privileges on *.* to 'ua'@'%' with grant option;

這個grant命令做了兩個動作:

  1. 磁盤上,將mysql.user表裏,用戶’ua’@’%'這一行的所有表示權限的字段的值都修改爲‘Y’;

  2. 內存裏,從數組acl_users中找到這個用戶對應的對象,將access值(權限位)修改爲二進制的“全1”。

在這個grant命令執行完成後,如果有新的客戶端使用用戶名ua登錄成功,MySQL會爲新連接維護一個線程對象,然後從acl_users數組裏查到這個用戶的權限,並將權限值拷貝到這個線程對象中。之後在這個連接中執行的語句,所有關於全局權限的判斷,都直接使用線程對象內部保存的權限位。

基於上面的分析我們可以知道:

  1. grant 命令對於全局權限,同時更新了磁盤和內存。命令完成後即時生效,接下來新創建的連接會使用新的權限。

  2. 對於一個已經存在的連接,它的全局權限不受grant命令的影響。

需要說明的是,一般在生產環境上要合理控制用戶權限的範圍。我們上面用到的這個grant語句就是一個典型的錯誤示範。如果一個用戶有所有權限,一般就不應該設置爲所有IP地址都可以訪問。

如果要回收上面的grant語句賦予的權限,你可以使用下面這條命令:

revoke all privileges on *.* from 'ua'@'%';

這條revoke命令的用法與grant類似,做了如下兩個動作:

  1. 磁盤上,將mysql.user表裏,用戶’ua’@’%'這一行的所有表示權限的字段的值都修改爲“N”;

  2. 內存裏,從數組acl_users中找到這個用戶對應的對象,將access的值修改爲0。

db權限

除了全局權限,MySQL也支持庫級別的權限定義。如果要讓用戶ua擁有庫db1的所有權限,可以執行下面這條命令:

grant all privileges on db1.* to 'ua'@'%' with grant option;

基於庫的權限記錄保存在mysql.db表中,在內存裏則保存在數組acl_dbs中。這條grant命令做了如下兩個動作:

  1. 磁盤上,往mysql.db表中插入了一行記錄,所有權限位字段設置爲“Y”;

  2. 內存裏,增加一個對象到數組acl_dbs中,這個對象的權限位爲“全1”。

圖2就是這個時刻用戶ua在db表中的狀態。

圖2 mysql.db 數據行

每次需要判斷一個用戶對一個數據庫讀寫權限的時候,都需要遍歷一次acl_dbs數組,根據user、host和db找到匹配的對象,然後根據對象的權限位來判斷。

也就是說,grant修改db權限的時候,是同時對磁盤和內存生效的。

grant操作對於已經存在的連接的影響,在全局權限和基於db的權限效果是不同的。接下來,我們做一個對照試驗來分別看一下。

圖3 權限操作效果

需要說明的是,圖中set global sync_binlog這個操作是需要super權限的。

可以看到,雖然用戶ua的super權限在T3時刻已經通過revoke語句回收了,但是在T4時刻執行set global的時候,權限驗證還是通過了。這是因爲super是全局權限,這個權限信息在線程對象中,而revoke操作影響不到這個線程對象。

而在T5時刻去掉ua對db1庫的所有權限後,在T6時刻session B再操作db1庫的表,就會報錯“權限不足”。這是因爲acl_dbs是一個全局數組,所有線程判斷db權限都用這個數組,這樣revoke操作馬上就會影響到session B。

這裏在代碼實現上有一個特別的邏輯,如果當前會話已經處於某一個db裏面,之前use這個庫的時候拿到的庫權限會保存在會話變量中。

你可以看到在T6時刻,session C和session B對錶t的操作邏輯是一樣的。但是session B報錯,而session C可以執行成功。這是因爲session C在T2 時刻執行的use db1,拿到了這個庫的權限,在切換出db1庫之前,session C對這個庫就一直有權限。

表權限和列權限

除了db級別的權限外,MySQL支持更細粒度的表權限和列權限。其中,表權限定義存放在表mysql.tables_priv中,列權限定義存放在表mysql.columns_priv中。這兩類權限,組合起來存放在內存的hash結構column_priv_hash中。

這兩類權限的賦權命令如下:

create table db1.t1(id int, a int);

grant all privileges on db1.t1 to 'ua'@'%' with grant option;
GRANT SELECT(id), INSERT (id,a) ON mydb.mytbl TO 'ua'@'%' with grant option;

跟db權限類似,這兩個權限每次grant的時候都會修改數據表,也會同步修改內存中的hash結構。因此,對這兩類權限的操作,也會馬上影響到已經存在的連接。

看到這裏,你一定會問,看來grant語句都是即時生效的,那這麼看應該就不需要執行flush privileges語句了呀。

答案也確實是這樣的。

flush privileges命令會清空acl_users數組,然後從mysql.user表中讀取數據重新加載,重新構造一個acl_users數組。也就是說,以數據表中的數據爲準,會將全局權限內存數組重新加載一遍。

同樣地,對於db權限、表權限和列權限,MySQL也做了這樣的處理。

也就是說,如果內存的權限數據和磁盤數據表相同的話,不需要執行flush privileges。而如果我們都是用grant/revoke語句來執行的話,內存和數據表本來就是保持同步更新的。

因此,正常情況下,grant命令之後,沒有必要跟着執行flush privileges命令。

flush privileges使用場景

那麼,flush privileges是在什麼時候使用呢?顯然,當數據表中的權限數據跟內存中的權限數據不一致的時候,flush privileges語句可以用來重建內存數據,達到一致狀態。

這種不一致往往是由不規範的操作導致的,比如直接用DML語句操作系統權限表。我們來看一下下面這個場景:

圖4 使用flush privileges

可以看到,T3時刻雖然已經用delete語句刪除了用戶ua,但是在T4時刻,仍然可以用ua連接成功。原因就是,這時候內存中acl_users數組中還有這個用戶,因此係統判斷時認爲用戶還正常存在。

在T5時刻執行過flush命令後,內存更新,T6時刻再要用ua來登錄的話,就會報錯“無法訪問”了。

直接操作系統表是不規範的操作,這個不一致狀態也會導致一些更“詭異”的現象發生。比如,前面這個通過delete語句刪除用戶的例子,就會出現下面的情況:

圖5 不規範權限操作導致的異常

可以看到,由於在T3時刻直接刪除了數據表的記錄,而內存的數據還存在。這就導致了:

  1. T4時刻給用戶ua賦權限失敗,因爲mysql.user表中找不到這行記錄;

  2. 而T5時刻要重新創建這個用戶也不行,因爲在做內存判斷的時候,會認爲這個用戶還存在。

小結

今天這篇文章,我和你介紹了MySQL用戶權限在數據表和內存中的存在形式,以及grant和revoke命令的執行邏輯。

grant語句會同時修改數據表和內存,判斷權限的時候使用的是內存數據。因此,規範地使用grant和revoke語句,是不需要隨後加上flush privileges語句的。

flush privileges語句本身會用數據表的數據重建一份內存權限數據,所以在權限數據可能存在不一致的情況下再使用。而這種不一致往往是由於直接用DML語句操作系統權限表導致的,所以我們儘量不要使用這類語句。

******************

開發同學接觸的很少,瞭解下就好。大的公司也會有腳本平臺的自動處理。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章