grant
# grant
grant用于给用户赋权,而在授权之前需要在MySQL创建一个账户,会进行两个动作:
- 磁盘user表中插入一行数据(ua,N,N,N,N,N),每一列权限字段的值初始化为N,表示还不具有任何权限。
- 内存acl_users用户数组插入一个用户对象,对象的access字段值为0.
# 1.用户权限
# 1.1全局权限
#赋予全局权限
grant all privileges on *.* to 'ua'@'%' with grant option;
#赋予权限并设置密码为pa
grant super on *.* to 'ua'@'%' identified by 'pa';
#回收以上权限
revoke all privileges on *.* from 'ua'@'%';
2
3
4
5
6
# 对磁盘内存影响
给ua用户赋一个最高权限可以执行如下SQL语句,会同时在磁盘和内存更新该用户的权限:
- 在磁盘user表中,将ua这一行记录的所有权限字段值都变为Y
- 内存acl_users数组里该用户对象的access值修改为二进制全1
# 对已存在连接影响
grant更新完后若有新的客户端连接,则从内存acl_users查到该对象的权限值,然后复制到该客户端线程对象中。此后在该连接中执行的语句,涉及到的权限判断都直接采用的线程对象内保存的权限位,与后续grant更新的权限无关。
因此当前已连接线程的全局权限不会受到后续grant和revoke权限修改的影响。
# 1.2db权限
grant all privileges on db1.* to 'ua'@'%' with grant option;
# 对磁盘内存影响
使用户拥有对整个库的所有权限,所有库权限记录在mysql.db表中,与上面类似,库权限的修改也同时在磁盘和内存生效:
- 在磁盘db表插入行记录,所有权限字段设置为Y
- 内存acl_dbs数组该对象access设置全1
# 对已存在连接影响
对于拥有库权限的连接而言,所有线程判断库权限都是通过全局数组acl_dbs,因此每次该用户的库权限被修改,连接的线程就能够立马感知到。也就是说每个线程并不会保存库权限位。
因此当前已连接线程的库权限会受到后续grant和revoke权限修改的影响。另外如果该线程已经use db进入到某个db库当中,那么这时候该连接的库权限也不会受到修改。
# 1.3表权限和列权限
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;
2
3
表权限和列权限对应的磁盘表分别为mysql.tables_priv和mysql.columns_priv,这两类权限会组合放在内存的hash结构column_priv_hash。
与库权限类似,这两个权限的已连接线程都会受到该账户最新的权限修改操作的影响。
# 2.flush privileges
问题
grant赋予权限之后到底需不需要重新flush privileges?
执行flush privileges命令后,系统会将磁盘表的权限数据重新刷到内存权限数组,保证内存的权限和数据库保持一致。而使用grant操作本身就会同时更新磁盘和内存的权限,因此一般情况下不需要使用flush privileges命令。
操作不规范的情况下,比如DML语句操作数据库表,而内存没有被修改,导致内存和数据库表权限不一致,此时才需要flush。
# flush privileges使用场景
- 直接操作系统表删除用户
delete from mysql.user where user="ua";
A线程执行以上SQL后,B线程此时依然能够使用ua账户进行连接。原因在于此时内存中还存在这个账户。
另外在这种情况下,接着创建ua账户会出现失败的情况,因为在内存判断时发现该ua账户还是存在的。
只有执行flush privileges后,才能完全删掉ua用户,并且重新创建用户。