違背常識,MySQL使用Grant授權后沒必要Flush Privilege
從我上大學時,數據庫概論老師就告訴我,MySQL使用grant對用戶授權之后,一定記得要用flush privilege命令刷新緩存,這樣才能使賦權命令生效。畢業工作以后,在很多的技術文檔上,仍然可以看到這種解釋。
但是,grant授權之后真的必須flush privilege嗎?如果不flush,授權真的就不生效嗎?
本篇文章也許會顛覆你的認知。
grant語句都做了哪些事
grant語句一般和創建用戶配合使用,比如創建一個用戶之后,給這個用戶授予一定的權限,當然,也可以對一個已存在的用戶授權。
我們以新建一個用戶testuser為例:
這條語句會往mysql.user表插入一行數據,同時會往內存中一個叫acl_users的數組中插入一個acl_user對象。
由于還沒有對這個用戶授權,所以這個用戶在user表中權限字段都是N,在acl_users數組中的對象的access字段都是0,表示還沒有任何權限。
而對于一個用戶的權限來說,其范圍是不同的,分別為全局權限、DB權限、表權限、列權限。
全局權限
當我們對一個用戶授予全局權限后,這個用戶就擁有了對整個數據庫實例的權限。
對testuser授予全局權限的寫法:
使用grant授權之后,mysql.user表中testuser這一行的權限字段的值就會全部變成Y,內存中access的值也會變成1。
這時,如果有新的數據庫鏈接接入,就可以從acl_users數組中查到這個用戶的權限,并且保存在當前的線程對象。
總結一下就是,grant命令同時更新了磁盤和內存的值,并且對于新鏈接會立刻生效,但是對于已經存在的老的鏈接,則不會產生影響。
大家可以思考一下,為什么對于老的鏈接,grant命令不會立刻生效。
DB權限
DB權限就是給一個用戶單獨指定某個庫的所有權。
DB權限與全局權限的授權命令的區別在于指定了DB庫:db1.*,而不是*.*。
執行grant命令之后,MySQL會往mysql.db表插入一條記錄,并且把權限字段的值置為Y,
另外增加一個對象到內存中的acl_dbs中,access的值設置為1。
當MySQL判斷一個用戶對某個數據庫的執行權限時,會遍歷這個acl_dbs數組,根據當前的user、host地址、DB名匹配符合的記錄,并判斷其中的權限位。
DB權限和全局權限的不同點在于,全局權限查詢后會設置到當前鏈接的線程對象中,每次判斷權限只需從線程對象中獲取判斷即可,而判斷DB權限需要每次遍歷acl_dbs數組。
由于每次判斷DB權限都需要去內存中遍歷acl_dbs數組,而這個數組又是一個全局對象,所以使用grant操作DB權限后,會立刻對所有鏈接生效。
表權限和列權限
除了全局權限和DB權限,MySQL還支持我們定義粒度更細的表權限和列權限。
grant表權限時,MySQL會更新mysql.tables_priv表,
grant列權限時,MySQL會更新mysql.columns_priv表,
同時,這兩個操作都會觸發MySQL更新內存中的hash表column_priv_hash。
?與DB權限一樣,對于表權限和列權限的修改,也會立刻影響到所有的鏈接。
那說了這么多,看起來grant命令都是立刻生效了,好像也不需要執行flush privileges了?
其實答案就是這樣的,grant命令授權后,并不需要再特意執行flush privileges了。
flush privileges的使用場景
既然MySQL提供了flush privileges,說明肯定有其適用的場景。
那么,flush privileges一般用在什么場景呢?
當使用flush privileges時,會清空內存中的acl_users、acl_dbs等數組,然后從表mysql.users、mysql.db等表中重新加載數據,
?換句話說,flush privileges主要用于使內存中的權限和數據庫中保持一致。
一般來說,內存中的數據和磁盤表中的數據都是一致的,但是當我們直接使用DML語句修改權限表中的值時,就會造成內存和磁盤的數據不一致。
這時,就需要使用flush privileges命令,刷新內存,使內存和磁盤的數據保持一致。
總結
使用grant命令之后,并不需要再隨手加上flush privileges,因為grant 語句會同時修改數據表和內存。
只有當我們不規范的直接使用DML語句修改表中權限字段時,才需要使用flush privileges刷新數據。