MySQL基础入门
创建用户
CREATE USER ‘username‘@’host’ IDENTIFIED BY ‘password’;
说明
- username:你将创建的用户名
- host:指定该用户在哪个主机上可以登陆,如果是本地用户可用localhost,如果想让该用户可以从任意远程主机登陆,可以使用通配符
%
- password:该用户的登陆密码,密码可以为空,如果为空则该用户可以不需要密码登陆服务器。
例如
1 | CREATE USER 'dog'@'localhost' IDENTIFIED BY '123456'; |
MySQL权限
最简单的MySQL权限
最简单也是最高效的,如果解决新手们删库跑路的问题其实也是很简单的,对于正式库只给一个增删改查的权限,或者只给一个查询权限(是不是就解决了删库的可能性?)
创建用户
语法
CREATE USER ‘username‘@’host’ IDENTIFIED BY ‘password’;
- username:你将创建的用户名
- host:指定该用户在哪个主机上可以登陆,如果是本地用户可用localhost,如果想让该用户可以从任意远程主机登陆,可以使用通配符
%
- password:该用户的登陆密码,密码可以为空,如果为空则该用户可以不需要密码登陆服务器。
例如
1 | CREATE USER 'dev'@'192.168.0.%' IDENTIFIED BY '123456'; |
一个sql语句就创建一个dev的用户,密码是123456并且只能192.168.0.*的IP才能访问
用户授权
使用root用户,执行
1 | grant SELECT on test.* TO 'dev'@'192.168.0.%' WITH GRANT OPTION; |
很简单的一句sq,仅仅运行在网段为192.168.0.*的网段进行查询操作。
再执行一条命令:
1 | show grants for 'dev'@'192.168.0.%'; |
深入研究下MySQL权限
用户标识是什么
上面一句简单的SQL堪称完美的解决了程序员新手的删库跑路的问题,但是如果想面试给面试管留下好映像,上面的知识好像还不够,有必要好好深入研究下MySql的权限了。这里有个小的知识点需要先具备,在mysql中的权限不是单纯的赋予给用户的,而是赋予给”用户+IP”的。
比如dev用户是否能登陆,用什么密码登陆,并且能访问什么数据库等都需要加上IP,这样才算一个完整的用户标识,换句话说 ‘dev‘@’192.168.0.168’ 、‘dev‘@’127.0.0.1’与‘dev‘@’localhost’ 这3个是完全不同的用户标识(哪怕你本机的ip就是192.168.0.168)。
用户权限所涉及的表
有了用户标识的概念接下来就可以看权限涉及的表了,这也是面试的时候加分项哦。有几张表需要谨记:
mysql.user,mysql.db,mysql.table_priv,mysql_column_priv
你可以熟悉其中的user表,甚至手动的改过里面的数据(不合规范哦!)
那这些表有什么用,和权限又有什么关系呢?
- User的一行记录代表一个用户标识
- db的一行记录代表对数据库的权限
- table_priv的一行记录代表对表的权限
- column_priv的一行记录代表对某一列的权限
测试
新建一个表account
1 |
|
mysql其实权限并不事特别low,权限的粒度甚至到了某一列上,举例来说,有个表account表。
对于前面创建的dev用户我不想让他访问balance列,但是id和name列是可以访问的,这样的需求在工作中不是没有。
1 | grant select(id,name) on test.account to 'dev'@'192.168.0.%'; |
在mysql维护工作中,做好权限管理是一个很重要的环节。下面对mysql权限操作进行梳理:
mysql的权限命令是grant,权限撤销的命令时revoke;
grant授权格式
grant 权限列表 on 库.表 to 用户名@’ip’ identified by “密码”;
revoke回收权限格式
revoke 权限列表 on 库.表 from 用户名@’ip’;
下面通过一些例子说明:
用户授权
grant 普通数据用户,查询、插入、更新、删除 数据库中所有表数据的权利
1 | grant all on *.* to dev@'192.168.0.1' identified by "123456"; |
查看权限
查看当前用户下所有的权限
1 | show grants; |
查看指定用户下的所有权限
USAGE是默认的初始状态,表示无任何权限!!
下面表示dev用户在192.168.0.0/254网段登陆本机mysql后,对test库下的所有表有insert, update, alter, delete, create, select的操作权限!
1 | show grants for dev@'192.168.0.%'; |
例如
1 | grant select(id,name) on test.account to 'dev'@'192.168.0.%'; |
这时候可以在分别看下table_priv,column_priv的数据
revoke撤销权限
revoke跟grant语法差不多,只需要把关键字 “to” 换成 “from” 即可,并且revoke语句中不需要跟密码设置。
注意:revoke可以回收所有权限,也可以回收部分权限。
1 | revoke all on *.* from dev@'192.168.0.1'; |
注意事项
- grant, revoke用户权限后,该用户只有重新连接MySQL数据库,权限才能生效。
- 如果想让授权的用户,也可以将这些权限grant给其他用户,那么授权时需添加选项 “grant option”!
如下设置后,那么这个wang用户连接mysql后也可以将这些权限授予其他用户。
mysql授权表一共涉及到5个表,分别是user、db、host、tables_priv和columns_priv。
这5张表的内容和用途如下:
- user表:user表列出可以连接服务器的用户及其口令,并且它指定他们有哪种全局(超级用户)权限。在user表启用的任何权限均是全局权限,并适用于所有数据库。例如,如果你启用了DELETE权限,在这里列出的用户可以从任何表中删除记录,所以在你这样做之前要认真考虑。
- db表:db表列出数据库,而用户有权限访问它们。在这里指定的权限适用于一个数据库中的所有表。
- host表:host表与db表结合使用在一个较好层次上控制特定主机对数据库的访问权限,这可能比单独使用db好些。这个表不受GRANT和REVOKE语句的影响,所以,你可能发觉你根本不是用它。
- tables_priv表:tables_priv表指定表级权限,在这里指定的一个权限适用于一个表的所有列。
- columns_priv表:columns_priv表指定列级权限。这里指定的权限适用于一个表的特定列。
1 | REVOKE SELECT on test.* from 'dev'@'192.168.3.%'; |
你使用dev登陆查询试试
你再要查询所有记录?不好意思不让查,而你查询id,name查询又是可以了。
MySQL的角色
准备工作
MySql基于”用户+IP”的这种授权模式其实还是挺好用的,但如果你使用Oracle、PostgreSQL、SqlServer你可能会发牢骚,这样对于每个用户都要赋权的方式是不是太麻烦了,如果我用户多呢?有没有角色或者用户组这样的功能呢?在mysql5.7开始才正式支持这个功能,而且连mysql官方把它叫做“Role Like”(不是角色,长得比较像而已)
那在5.7中怎么玩这个不像角色的角色呢?
show variables like “%proxy%”
你得先把check_proxy_users,mysql_native_password_proxy_users这两个变量设置成true才行
1 | set GLOBAL check_proxy_users =1; |
当然,你也可以把这两个配置设置到my.cnf中
创建一个角色
create USER ‘dev_role’;
可能被你发现了,我这里创建得是个user,为了稍微像角色一点点,我给这user取名叫dev_role,而且为了方便也没用使用密码了。
创建2个开发人员账号
1 | create USER 'zhangsan'; |
把两个用户加到组里面
这两个用户我也没设置密码,把两个用户加到组里面
1 | grant proxy on 'dev_role' to 'zhangsan'; |
查看权限
可以看下其中一个用户的权限
GRANT USAGE ON . TO ‘zhangsan‘@’%’
GRANT PROXY ON ‘dev_role‘@’%’ TO ‘zhangsan‘@’%’
注意:如果你是远程链接,你可能会收获一个大大的错误,你没有权限做这一步,这个时候你需要再服务器上执行一条
1 | GRANT PROXY ON ''@'' TO 'root'@'%' WITH GRANT OPTION; |
给角色dev_role应该有的权限
有了用户了,这用户也归属到了dev_role这角色下面,那接下来要做的就很简单了,根据业务需求给这角色设置权限就好了(需要在服务器上执行)
1 | grant select(id,name) on hankin.account to 'dev_role'; |
测试
现在使用zhangsan用户登陆系统试试
1 | select id ,name from hankin.account; |
你可能又会问,这种角色的权限是存哪的呢?
SELECT * FROM proxies_priv;
MySQL数据类型
Int类型
类型 | 字节 | 最小值 | 最大值 | |
---|---|---|---|---|
(带符号的/无符号的) | (带符号的/无符号的) | |||
TINYINT | 1 | -128 | 127 | |
0 | 255 | |||
SMALLINT | 2 | -32768 | 32767 | |
0 | 65535 | |||
MEDIUMINT | 3 | -8388608 | 8388607 | |
0 | 16777215 | |||
INT | 4 | -2147483648 | 2147483647 | |
0 | 4294967295 | |||
BIGINT | 8 | -9223372036854775808 | 9223372036854775807 | |
0 | 18446744073709551615 |
有无符号
在项目中使用BIGINT,而且是有符号的。
1 | create table test_unsigned(a int unsigned, b int unsigned); |
INT(N)是什么
1 | create table test_int_n(a int(4) zerofill); |
- int(N)中的 N 是显示宽度,不表示存储的数字的长度的上限。
- zerofill 表示当存储的数字长度< N时,用数字0填充左边,直至补满长度N。
- 当存储数字的长度超过N时,按照实际存储的数字显示。
自动增长的面试题
这列语法有错误吗?
1 | create table test_auto_increment(a int auto_increment); |
以下结果是什么
1 | insert into test_auto_increment values(NULL); |
总结:插入null索引会自动增加,插入0,索引也会增加但不是0,插入负数成功
字符类型
类型 | 说明 | N的含义 | 是否有字符集 | 最大长度 |
---|---|---|---|---|
CHAR(N) | 定长字符 | 字符 | 是 | 255 |
VARCHAR(N) | 变长字符 | 字符 | 是 | 16384 |
BINARY(N) | 定长二进制字节 | 字节 | 否 | 255 |
VARBINARY(N) | 变长二进制字节 | 字节 | 否 | 16384 |
TINYBLOB(N) | 二进制大对象 | 字节 | 否 | 256 |
BLOB(N) | 二进制大对象 | 字节 | 否 | 16K |
MEDIUMBLOB(N) | 二进制大对象 | 字节 | 否 | 16M |
LONGBLOB(N) | 二进制大对象 | 字节 | 否 | 4G |
TINYTEXT(N) | 大对象 | 字节 | 是 | 256 |
TEXT(N) | 大对象 | 字节 | 是 | 16K |
MEDIUMTEXT(N) | 大对象 | 字节 | 是 | 16M |
LONGTEXT(N) | 大对象 | 字节 | 是 | 4G |
排序规则
1 | select 'a' = 'A'; -- 查询结果:1 |
时间类型
日期类型 | 占用空间 | 表示范围 |
---|---|---|
DATETIME | 8 | 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 |
DATE | 3 | 1000-01-01 ~ 9999-12-31 |
TIMESTAMP | 4 | 1970-01-01 00:00:00UTC ~ 2038-01-19 03:14:07UTC |
YEAR | 1 | YEAR(2):1970-2070, YEAR(4):1901-2155 |
TIME | 3 | -838:59:59 ~ 838:59:59 |
datatime与timestamp区别
1 | create table test_time(a timestamp, b datetime); |
JSON类型
JSON入门
新建表
1 | create table json_user ( |
插入数据
1 | insert into json_user values (null, '{ "name":"lison","age":18,"address":"enjoy" }' ); |
查询结果
1 | 1 {"age": 18, "name": "张三", "address": "陕西西安"} |
JSON函数
json_extract抽取
1 | select json_extract('[10, 20, [30, 40]]', '$[1]'); |
1 | select json_extract(data, '$.name'), json_extract(data, '$.address') from test.json_user; |
JSON_OBJEC
将对象转为json
1 | select json_object("name", "隔壁老王", "email", "老王.sina.com", "age",35); |
1 | insert into test.json_user values ( null, json_object("name", "隔壁老王", "email", "老王.sina.com", "age",35) ); |
json_insert
插入数据 新增不存在的值
语法:JSON_INSERT(json_doc, path, val[, path, val] …)
json_doc 是JSON字符串
path
是要插入数据或更新值的元素的路径。val
是新的值.
1 | set @json = '{ "a": 1, "b": [2, 3]}'; |
我们发现 a 存在没有插入 c 不存在 插入了
1 | update test.json_user set data = json_insert(data, "$.address", "你家隔壁") where uid = 3; |
json_merge
合并数据并返回
1 | select json_merge('{"name": "enjoy"}', '{"id": 47}'); |
1 | select json_merge(json_extract(data, '$.name'), json_extract(data, '$.address')) from test.json_user where uid = 3; |
JSON索引
JSON 类型数据本身 无法直接 创建索引,需要将需要索引的 JSON数据 重新 生成虚拟列(Virtual Columns) 之后,对 该列 进行 索引
1 | create table test_inex_1( |
疑问:这条sql查询的结果是?
1 | select json_extract(data,"$.name") as username from test_inex_1 where gen_col="张三"; |
因为数据带有双引号 所以需要在外面包一个单引号
1 | select json_extract(data,"$.name") as username from test_inex_1 where gen_col='"张三"'; |
我们用执行计划看看有没有走索引
1 | explain select json_extract(data,"$.name") as username from test_inex_1 where gen_col='"张三"'; |
我们看到是走了索引
示例
经查阅官方文档,建立虚拟列,这个列查询的时候不需要加上“”符号
1 | create table test_index_2 ( |
查询下结果
1 | select json_extract(data,"$.name") as username from test_index_2 where gen_col="张三"; |
答案:通过使用JSON_UNQUOTE来解决这个问题
1 | SELECT json_extract(data,"$.name"),JSON_UNQUOTE(data->'$.name') from test_index_2 where gen_col="张三"; |