42 | grant之后要跟着flush privileges吗?


MySQL45讲

实践篇

42 | grant之后要跟着flush privileges吗?

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

这条语句的逻辑是创建一个用户 ’ua’@’%’,密码是 pa。注意,在 MySQL 里面,用户名 (user) + 地址 (host,% 表示所有IP) 才表示一个用户,因此 ua@ip1 和 ua@ip2 代表的是两个不同的用户。

这条命令做了两个动作:

  • 磁盘上,往 mysql.user 表里插入一行,由于没有指定权限,所以这行数据上所有表示权限的字段的值都是 N;
  • 内存里,往数组 acl_users 里插入一个 acl_user 对象,这个对象的 access 字段值为 0。

在这里插入图片描述

全局权限

全局权限,作用于整个 MySQL 实例,这些权限信息保存在 mysql 库的 user 表里。

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

with grant option 表示该用户可以将自己拥有的权限授与其他用户。

这个 grant 命令做了两个动作:

  • 磁盘上,将 mysql.user 表里,用户’ua’@’%'这一行的所有表示权限的字段的值都修改为 ‘Y’;
  • 内存里,从数组 acl_users 中找到这个用户对应的对象,将 access 值(权限位)修改为二进制的“全 1”。

在这个 grant 命令执行完成后,如果有新的客户端使用用户名 ua 登录成功,MySQL 会为新连接维护一个线程对象,然后从 acl_users 数组里查到这个用户的权限,并将权限值拷贝到这个线程对象中。之后在这个连接中执行的语句,所有关于全局权限的判断,都直接使用线程对象内部保存的权限位。

一般在生产环境上要合理控制用户权限的范围。如果一个用户有所有权限,一般就不应该设置为所有 IP 地址都可以访问。

回收上面的 grant 语句赋予的权限,使用以下命令:

revoke all privileges on *.* from 'ua'@'%';
  • revoke 命令的用法与 grant 类似,做了如下两个动作: 磁盘上,将 mysql.user 表里,用户 ’ua’@’%'这一行的所有表示权限的字段的值都修改为“N”;
  • 内存里,从数组 acl_users 中找到这个用户对应的对象,将 access 的值修改为 0。
db 权限

让用户 ua 拥有库 db1 的所有权限,可以执行以下命令:

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

这条 grant 命令做了如下两个动作:

  • 磁盘上,往 mysql.db 表中插入了一行记录,所有权限位字段设置为“Y”;
  • 内存里,增加一个对象到数组 acl_dbs 中,这个对象的权限位为“全 1”。

在这里插入图片描述

每次需要判断一个用户对一个数据库读写权限的时候,都需要遍历一次 acl_dbs 数组,根据 user、host 和 db 找到匹配的对象,然后根据对象的权限位来判断。也就是说,grant 修改 db 权限的时候,是同时对磁盘和内存生效的。

grant 操作对于已经存在的连接的影响,在全局权限和基于 db 的权限效果不同。

在这里插入图片描述

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。

在 T6 时刻,session C 和 session B 对表 t 的操作逻辑一样。但是 session B 报错,而 session C 可以执行成功。这是因为 session C 在 T2 时刻执行的 use db1,拿到了这个库的权限,在切换出 db1 库之前,session C 对这个库就一直有权限。

表权限和列权限

表权限定义存放在表 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 结构。因此,对这两类权限的操作,也会马上影响到已经存在的连接。

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

如果内存的权限数据和磁盘数据表相同,不需要执行 flush privileges。而如果都是用 grant/revoke 语句来执行,内存和数据表本来就是保持同步更新的。

因此,正常情况下,grant 命令之后,没有必要跟着执行 flush privileges 命令。

flush privileges 使用场景

当数据表中的权限数据跟内存中的权限数据不一致的时候,flush privileges 语句可以用来重建内存数据,达到一致状态。

这种不一致往往是由不规范的操作导致,比如直接用 DML 语句操作系统权限表。

在这里插入图片描述

T3 时刻虽然已经用 delete 语句删除了用户 ua,但是在 T4 时刻,仍然可以用 ua 连接成功。原因就是,这时候内存中 acl_users 数组中还有这个用户,因此系统判断时认为用户还正常存在。

在 T5 时刻执行过 flush 命令后,内存更新,T6 时刻再要用 ua 来登录,就会报错“无法访问”。

直接操作系统表是不规范的操作,这个不一致状态也会导致一些更“诡异”的现象发生。

在这里插入图片描述

由于在 T3 时刻直接删除了数据表的记录,而内存的数据还存在。这就会导致:

  • T4 时刻给用户 ua 赋权限失败,因为 mysql.user 表中找不到这行记录;
  • T5 时刻要重新创建这个用户也不行,因为在做内存判断的时候,会认为这个用户还存在。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

久违の欢喜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值