MySQL常用运维操作(二):grant赋权语句

创建一个用户:

sql 复制代码
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。

下图就是这个时刻用户ua在user表中的状态:

在MySQL中, 用户权限是有不同的范围的。 接下来, 我就按照用户权限范围从大到小的顺序依次和你说明。

全局权限


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

如果我要给用户ua赋一个最高权限的话, 语句是这么写的:

sql 复制代码
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数组里查到这个用户的权限, 并将权限值拷贝到这个线程对象中(也就是说,只要新连接建立,其对应线程对象就会获取该用户的super权限)。之后在这个连接中执行的语句, 所有关于全局权限的判断, 都直接使用线程对象内部保存的权限位。

基于上面的分析可知:

  1. grant 命令对于全局权限, 同时更新了磁盘和内存。 命令完成后即时生效, 接下来新创建的连接会使用新的权限。

  2. 对于一个已经存在的连接, 它的全局权限不受grant命令的影响。

问:grant语句赋予的权限,如何回收?

答:可执行如下命令进行回收:revoke all privileges on *.* from 'ua'@'%';

revoke命令的用法和grant命令类似,做了如下两个动作:

  1. 磁盘上, 将mysql.user表里, 用户'ua'@'%'这一行的所有表示权限的字段的值都修改为"N"。

  2. 内存里, 从数组acl_users中找到这个用户对应的对象, 将access的值修改为0。

注:revoke命令只能回收当前线程对象的权限。

DB权限


MySQL也支持库级别的权限定义。

如果要让用户ua拥有库db1的所有权限, 可以执行下面这条命令:

sql 复制代码
grant all privileges on db1.* to 'ua'@'%' with grant option;

基于库的权限记录保存在mysql.db表中, 在内存里则保存在数组acl_dbs中。

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

  1. 磁盘上, 往mysql.db表中插入了一行记录, 所有权限位字段设置为"Y"。

  2. 内存里, 增加一个对象到数组acl_dbs中, 这个对象的权限位为"全1"。

用户ua在db表中的状态:

重点:每次需要判断一个用户对一个数据库读写权限的时候, 都需要遍历一次acl_dbs数组, 根据user、 host和db找到匹配的对象, 然后根据对象的权限位来判断。(而super权限,只在创建连接时,获取一次)

也就是说,grant修改db权限的时候, 是同时对磁盘和内存生效的。

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

示例:假设有如下序列:

注:图中set global sync_binlog这个操作是需要super权限的。

上图说明:

  1. 在T3时刻,使用revoke语句回收用户ua的super权限。

  2. 在T4时刻,执行set global时,权限验证通过。这是因为super是全局权限,这个权限信息在线程对象中,而revoke操作影响不到session B、session C线程对象。(super权限,只在创建连接时,其线程对象获取一次)

  3. 在T5时刻,使用revoke语句回收用户ua的db1库权限。

  4. 在T6时刻,session B操作db1库,此时会报错:"权限不足"。这是因为acl_dbs是一个全局数组,所有线程判断db权限都用这个数组,这样revoke命令就会马上影响到session B。(db权限,每次需要判断一个用户对一个数据库是否有读写权限时,都需要获取该权限)

  5. 在T6时刻,session C和session B对表t的操作逻辑是一样的。但是session B报错,而session C可以执行成功。这是因为session C在T2时刻执行的use db1,拿到了这个库的权限,在切换db1库之前,session C对这个库就一直有权限。(DB权限在代码实现上有一个特别的逻辑,如果当前会话已经处于某一个db里面,之前use这个库时拿到的权限会保存在会话变量中)

表权限和列权限


MySQL支持更细粒度的表权限和列权限。

表权限定义存放在表mysql.tables_priv中, 列权限定义存放在表mysql.columns_priv中。 这两类权限, 组合起来存放在内存的hash结构column_priv_hash中。

两类权限的赋权命令:

sql 复制代码
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结构。

因此,对这两类权限的操作,也会马上影响到已经存在的连接。

问1:flush privileges命令的作用是什么?

flush privileges命令会清空acl_users数组, 然后从mysql.user表中读取数据重新加载, 重新构造一个acl_users数组。 也就是说, 以数据表中的数据为准, 会将全局权限内存数组重新加载一遍。

问2:既然grant语句都是即时生效的,那是不是就不需要执行flush privileges了呢?

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

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

flush privileges使用场景


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

问:什么情况下数据表中的权限数据和内存中的权限数据不一致呢?

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

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

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

直接操作系统表是不规范的操作,这个不一致状态也会导致一些更"诡异"的现象发生。比如上面的delete语句删除用户的例子,就会出现如下情况:

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

  1. T4时刻给用户ua赋权限失败, 因为mysql.user表中找不到这行记录。

  2. 而T5时刻要重新创建这个用户也不行, 因为在做内存判断的时候, 会认为这个用户还存在。

小结:思考题


总结规范:
1)grant语句会同时修改数据表和内存, 判断权限的时候使用的是内存数据。 因此, 规范地使用grant和revoke语句, 是不需要随后加上flush privileges语句的。

2)flush privileges语句本身会用数据表的数据重建一份内存权限数据, 所以在权限数据可能存在不一致的情况下再使用。 而这种不一致往往是由于直接用DML语句操作系统权限表导致的, 所以我们尽量不要使用这类语句。

3)grant赋权语句另一种写法如下,这条命令加了identified by'密码', 语句的逻辑里面除了赋权外, 还包含了:

sql 复制代码
grant super on *.* to 'ua'@'%' identified by 'pa';
  • 如果用户'ua'@'%'不存在, 就创建这个用户, 密码是pa。
  • 如果用户ua已经存在, 就将密码修改成pa。

注:这种写法不建议,因为其很容易就会不慎把密码修改了。

相关推荐
XIAOHEZIcode5 小时前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户03284722207021 小时前
如何搭建本地yum源(上)
运维
大树884 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠4 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质4 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
Inhand陈工4 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智4 天前
ARP代理--工作原理
运维·网络·arp·arp代理
shushangyun_4 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
施努卡机器视觉4 天前
SNK施努卡侧滑门锁上滑轮总成自动化装配线,从零件到组件,全流程精密制造方案
运维·自动化·制造
AC赳赳老秦4 天前
用 OpenClaw 搭建服务器故障应急响应系统,自动处理 80% 常见运维故障
android·运维·服务器·python·rxjava·deepseek·openclaw