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。

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

相关推荐
chenbin52033 分钟前
Jenkins 自动构建Job
运维·jenkins
java 凯34 分钟前
Jenkins插件管理切换国内源地址
运维·jenkins
AI服务老曹38 分钟前
运用先进的智能算法和优化模型,进行科学合理调度的智慧园区开源了
运维·人工智能·安全·开源·音视频
sszdzq2 小时前
Docker
运维·docker·容器
book01212 小时前
MySql数据库运维学习笔记
运维·数据库·mysql
bugtraq20213 小时前
XiaoMi Mi5(gemini) 刷入Ubuntu Touch 16.04——安卓手机刷入Linux
linux·运维·ubuntu
xmweisi3 小时前
【华为】报文统计的技术NetStream
运维·服务器·网络·华为认证
VVVVWeiYee3 小时前
BGP配置华为——路径优选验证
运维·网络·华为·信息与通信
陆鳐LuLu3 小时前
日志管理利器:基于 ELK 的日志收集、存储与可视化实战
运维·elk·jenkins
DC_BLOG4 小时前
Linux-GlusterFS进阶分布式卷
linux·运维·服务器·分布式