细说flush privileges

07/13/2022

我们平时在给用户授权grant语句执行后,都习惯性的执行一个命令flush privileges;,但是我发现有时候不执行这个命令,授权语句执行之后,权限验证也是OK的,但是有时候发现不执行这个flush privileges;命令,权限认证还不能通过。

那么到底什么情况下需要执行flsh privileges;?什么情况下不需要执行这个命令呢?

Mysql的权限分为以下四种:
1.全局权限
eg:grant all privileges on . to ‘test’@’%’ with grant option;
revoke all privileges on . from ‘test’@’%’;
2.db权限
eg:grant all privileges on testdb.* to ‘test’@’%’ with grant option;
revoke all privileges on testdb.* from ‘test’@’%’;
3.表权限
eg:grant all privileges on testdb.test0701 to ‘test’@’%’ with grant option;
4.列权限
eg:grant select(id), insert(id,a) on testdb.test0702 to ‘test’@’%’ with grant option;

每种权限的信息都会在磁盘和内存中存储,具体的存储位置为:
1.全局权限
磁盘:表mysql.user
内存:数组acl_user
2.db权限
磁盘:表mysql.db
内存:数组acl_dbs
3.表权限
磁盘:表mysql.tables_priv
内存:和列权限组成的hash结构column_priv_hash
4.列权限
磁盘:表mysql.columns_priv
内存:和表权限组成的hash结构column_priv_hash

每种权限的修改策略和作用范围:
1.全局权限
策略:已存在的连接不生效,新建立连接立即生效
范围:当前线程
2.db权限
策略:所有连接立即生效
范围:全局
(注:这里面有个特殊情况,如果会话在持有某个db的权限时进入了该db,那么会话在执行use xxx时拿到的权限就会保存在会话变量中,即使之后别的会话revoke了权限,也不会影响到该会话,在切换出该db之前该会话会一直持有权限)
3.表权限
策略:所有连接立即生效
范围:全局
4.列权限
策略:所有连接立即生效
范围:全局

关于flush privileges操作:
对于全局权限,flush privileges操作会清空acl_user数组,然后从mysql.user表中读取数据重新构造一个acl_user数组,也就是以数据表中数据为准,将内存数组重新加载一遍
对于db权限、表权限和列权限,Mysql也是做了这样的处理。

所以说如果内存中的权限数据和磁盘表中的数据一致的话,flush privileges其实是可以不用做的。

而对于正常的grant/revoke/create user等操作,内存和磁盘中的数据都是同步更新的,所以正常的grant/revoke操作后是不需要flush privileges的。

flush privileges的使用场景:
当我们直接用DML语句修改系统权限表(mysql.user、mysql.db、mysql.tables_priv、mysql.columns_priv)时,内存中的权限数组是不会同步更新的,此时我们就需要flush privileges来更新内存权限数据了。

下面截取官方的几段说明
https://dev.mysql.com/doc/refman/5.7/en/privilege-changes.html

If the mysqld server is started without the -skip-grant-tables option, it reads all grant table contents into memory during its startup sequence. The in-memory tables become effective for access control at that point.

译文:如果mysqld服务未使用–skip-grant-tables选项启动,mysql会在启动过程中将权限表的所有内容读取到内存中。此时,内存中的表将对访问控制生效。

If you modify the grant tables indirectly using an account-management statement, the server notices these changes and loads the grant tables into memory again immediately. Examples include GRANTREVOKESET PASSWORD, and RENAME USER.

译文:如果使用帐户管理语句间接修改授权表,服务器会注意到这些更改,并立即将授权表重新加载到内存中。示例包括授予、撤销、设置账号管理语句包括GRANT, REVOKE, SET PASSWORD, RENAME USER四个。

If you modify the grant tables directly using statements such as INSERTUPDATE, or DELETE(which is not recommended), the changes have no effect on privilege checking until you either tell the server to reload the tables or restart it. Thus, if you change the grant tables directly but forget to reload them, the changes have no effect until you restart the server. This may leave you wondering why your changes seem to make no difference!

译文:如果你直接使用INSERT, UPDATE, DELETE(DELETE不推荐使用)修改权限表,除非你重新加载这个表,或者重启数据库,这些操作是不会影响权限检查的。因此,如果你修改了权限表但是忘记重新加载它们,直到你重启数据库,这个修改都是无效的。这可能会让你发现,为什么你命名修改了,但是没生效!

To tell the server to reload the grant tables, perform a flush-privileges operation. This can be done by issuing a FLUSH PRIVILEGES statement or by executing a mysqladmin flush-privileges or mysqladmin reload command.

译文:为了告诉服务器重新加载权限表,需要执行一个刷新权限的命令。我们可以在数据库中执行FLUSH PRIVILEGES语句,或者在命令行中执行mysqladmin flush privileges或者mysqladmin reload命令。

A grant table reload affects privileges for each existing client session as follows:

  • Table and column privilege changes take effect with the client’s next request.
  • Database privilege changes take effect the next time the client executes a USE *db_name* statement.
  • Global privileges and passwords are unaffected for a connected client. These changes take effect only in sessions for subsequent connections.

译文:每次权限表的加载都会对会话有以下影响:

  • 表级和行级的权限会在下次会话连接中改变
  • 数据库级的权限将会在下次USE DB_NAME后改变。注意:由于客户端存在缓存,如果不是真的切换数据库,此条改变可能不易被察觉。
  • 在同一个连接中,全局权限和密码不发生改变。只有开启新的会话时,才会生效。

If the server is started with the --skip-grant-tables option, it does not read the grant tables or implement any access control. Any user can connect and perform any operation, which is insecure. To cause a server thus started to read the tables and enable access checking, flush the privileges.

译文:如果会话以–skip-grant-tables选项启动,它不会读取权限表,也没有相关访问控制的权限。任何人可以通过–skip-grant-tables进行连接和操作,这是不安全的。如果要从服务器开始读取权限表并保证访问检查,请刷新权限。