零、
help、\h、? 调出帮助
mysql> \hFor information about MySQL products and services, visit:http://www.mysql.com/
For developer information, including the MySQL Reference Manual, visit:http://dev.mysql.com/
To buy MySQL Enterprise support, training, or other products, visit:https://shop.mysql.com/List of all MySQL commands:
Note that all text commands must be first on line and end with ';'
? (\?) Synonym for `help'.
clear (\c) Clear the current input statement.
connect (\r) Reconnect to the server. Optional arguments are db and host.
delimiter (\d) Set statement delimiter. 设置结束符
edit (\e) Edit command with $EDITOR.
ego (\G) Send command to mysql server, display result vertically.
exit (\q) Exit mysql. Same as quit.
go (\g) Send command to mysql server.
help (\h) Display this help.
nopager (\n) Disable pager, print to stdout.
notee (\t) Don't write into outfile.
pager (\P) Set PAGER [to_pager]. Print the query results via PAGER.
print (\p) Print current command.
prompt (\R) Change your mysql prompt.
quit (\q) Quit mysql.
rehash (\#) Rebuild completion hash.
source (\.) Execute an SQL script file. Takes a file name as an argument.
status (\s) Get status information from the server.
system (\!) Execute a system shell command.
tee (\T) Set outfile [to_outfile]. Append everything into given outfile.
use (\u) Use another database. Takes database name as argument.
charset (\C) Switch to another charset. Might be needed for processing binlog with multi-byte charsets.
warnings (\W) Show warnings after every statement.
nowarning (\w) Don't show warnings after every statement.
resetconnection(\x) Clean session context.For server side help, type 'help contents'
\c或ctrl+c 清除当前输入
登陆mysql:
mysql [-u用户名] [-h主机名(或ip)] [-p密码]
不写-u用户名表示用root登陆
不写-h主机名(ip)表示主机为localhost
不写-p密码表示密码为空
show variables like ‘%character%’; like相当于linux中的grep
清空屏幕:CTRL+L
一、Mysql格式化输出结果(Linux命令行)
1.使用G按行垂直显示结果
如果一行很长,需要这行显示的话,在linux命令行中看起结果来就非常的难受。在SQL语句或者命令后使用G而不是分号结尾,可以将每一行的值垂直输出。这个可能也是大家对于MySQL最熟悉的区别于其他数据库工具的一个特性了。
mysql> select * from db_archivelog\G
*************************** 1. row ***************************
id: 1
check_day: 2008-06-26
db_name: TBDB1
arc_size: 137
arc_num: 166
per_second: 1.6
avg_time: 8.7
2.使用pager设置显示方式
如果select出来的结果集超过几个屏幕,那么前面的结果一晃而过无法看到。使用pager可以设置调用os的more或者less等显示查询结果,和在os中使用more或者less查看大文件的效果一样。
使用more
mysql> pager more
PAGER set to ‘more’
使用less
mysql> pager less
PAGER set to ‘less’
还原成stdout
mysql> nopager
PAGER set to stdout
3.使用tee保存运行结果到文件
可以将命令行中的所有结果保存到外部文件(不指定文件路径的话默认在运行mysql命令的路径中)中。如果指定已经存在的文件,则结果会附加到文件中。
mysql> tee output.txt
Logging to file ‘output.txt’
关闭:
mysql> notee
Outfile disabled.
4.执行Linux系统命令
mysql> system uname
Linux
或
mysql> \! uname
Linux
5.执行SQL文件
不指定文件路径的话默认在运行mysql命令的路径中寻找文件
mysql> source test.sql
+—————-+
| current_date() |
+—————-+
| 2008-06-28 |
+—————-+
1 row in set (0.00 sec)
6.以html格式输出结果
使用mysql客户端的参数--html
,则所有SQL的查询结果会自动生成为html的table代码
$ mysql -uroot --html
Welcome to the MySQL monitor. Commands end with ; or \g.
YourMySQL connection id is 15
Server version: 5.7.42 MySQL Community Server (GPL)
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
7.以xml格式输出结果
跟上面差不多,使用–xml选项,可以将结果输出为xml格式
$ mysql -uroot --xml
Welcome to the MySQL monitor. Commands end with ; or \g.
YourMySQL connection id is 15
Server version: 5.7.42 MySQL Community Server (GPL)
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
8.修改命令提示符
prompt
命令可以在mysql提示符中显示当前用户、数据库、时间等信息
通过prompt设置了当前时间显示,这样也便于在日志文件中查看每次操作的时间。
使用mysql的--prompt=" "
选项,或者进入mysql命令行环境后使用prompt
命令,或者修改配置文件都可以修改提示符
登录的时候设置
mysql -uroot -p --prompt="\u@\h:[\d] \r:\m:\s>"
mysql命令行中设置
prompt \u@\h:[\d] \r:\m:\s>
配置文件中设置
/etc/my.cnf
配置文件中添加[mysql]
部分
在配置文件里最好使用双斜杠
[mysql]
prompt=\\u@\\h:[\\d] \\r:\\m:\\s>
\u:当前连接的用户
\h:当前连接的主机
\d:当前连接的数据库
\r:\m:\s:显示当前时间
更多参数请使用man mysql
源博客
https://blog.csdn.net/qq_36411874/article/details/63251141
二、配置文件
[client]代表客户端默认设置内容;
[mysql]代表我们使用mysql命令登录mysql数据库时的默认设置;
[mysqld]代表数据库自身的默认设置;
三、数据库mysql详解
MySQL数据库安装完成后,自带了一下四个数据库,具体作用如下:
MySQL 在安装时会自动创建一个名为 mysql 的数据库,mysql 数据库中存储的都是用户权限表。用户登录以后,MySQL 会根据这些权限表的内容为每个用户赋予相应的权限。
mysql> show tables from mysql;
+---------------------------+
| Tables_in_mysql |
+---------------------------+
| columns_priv |
| component |
| db |
| default_roles |
| engine_cost |
| func |
| general_log |
| global_grants |
| gtid_executed |
| help_category |
| help_keyword |
| help_relation |
| help_topic |
| innodb_index_stats |
| innodb_table_stats |
| password_history |
| plugin |
| procs_priv |
| proxies_priv |
| role_edges |
| server_cost |
| servers |
| slave_master_info |
| slave_relay_log_info |
| slave_worker_info |
| slow_log |
| tables_priv |
| time_zone |
| time_zone_leap_second |
| time_zone_name |
| time_zone_transition |
| time_zone_transition_type |
| user |
+---------------------------+
33 rows in set (0.02 sec)
0. 系统默认用户
Mysql版本:8.0.11
select user,host,account_locked,authentication_string from mysql.user;
用户说明
- root是MySQL的特权账号,该用户拥有所有权限,可执行任何操作。
- user1是我们自己建的用户。
- mysql.sys用户是用于sys schema中对象的定义。使用 mysql.sys 用户可避免DBA重命名或者删除root用户时发生的问题。该用户已被锁定,客户端无法连接。
- mysql.session用户是插件内部使用来访问服务器。该用户已被锁定,客户端无法连接。
- mysql.infoschema是mysql 8版本新增的系统用户,mysql 5版本没有。该用户是MySQL数据库的系统用户,用来管理和访问系统自带实例information_schema示例的。该用户已被锁定,客户端无法连接。
1. user(全局级别权限)
user 表是 MySQL 中最重要的一个权限表,用来记录允许连接到服务器的账号信息。需要注意的是,在 user 表里启用的所有权限都是全局级的,适用于所有数据库。
user 表中的字段大致可以分为 4 类,分别是用户列、权限列、安全列和资源控制列,下面主要介绍这些字段的含义。
用户列
用户列存储了用户连接 MySQL 数据库时需要输入的信息。需要注意的是 MySQL 5.7 版本不再使用 Password 来作为密码的字段,而改成了 authentication_string。
MySQL 5.7 版本的用户列如表所示:
字段名 | 字段类型 | 是否为空 | 默认值 | 说明 |
---|---|---|---|---|
Host | char(60) | NO | 无 | 主机名 |
User | char(32) | NO | 无 | 用户名 |
authentication_string | text | YES | 无 | 密码 |
用户登录时,如果这 3 个字段同时匹配,MySQL 数据库系统才会允许其登录。创建新用户时,也是设置这 3 个字段的值。修改用户密码时,实际就是修改 user 表的 authentication_string 字段的值。因此,这 3 个字段决定了用户能否登录。
[注]:
-
Host列指定了允许用户登录所使用的IP,比如user=root Host=192.168.1.1。这里的意思就是说root用户只能通过192.168.1.1的客户端去访问。
-
%是个通配符,如果Host=192.168.1.%,那么就表示只要是IP地址前缀为“192.168.1.”的客户端都可以连接。如果Host=%,表示所有IP都有连接权限。
-
mysql 5.7和mysql 8.0版本中,%包含localhost。
权限列
权限列的字段决定了用户的权限,用来描述在全局范围内允许对数据和数据库进行的操作。
权限大致分为两大类,分别是高级管理权限和普通权限:
- 高级管理权限主要对数据库进行管理,例如关闭服务的权限、超级权限和加载用户等;
- 普通权限主要操作数据库,例如查询权限、修改权限等。
user 表的权限列包括 Select_priv、Insert_ priv 等以 priv 结尾的字段,这些字段值的数据类型为 ENUM,可取的值只有 Y 和 N:Y 表示该用户有对应的权限,N 表示该用户没有对应的权限。从安全角度考虑,这些字段的默认值都为 N。
字段名 | 字段类型 | 是否为空 | 默认值 | 说明 |
---|---|---|---|---|
Select_priv | enum(‘N’,‘Y’) | NO | N | 是否可以通过SELECT 命令查询数据 |
Insert_priv | enum(‘N’,‘Y’) | NO | N | 是否可以通过 INSERT 命令插入数据 |
Update_priv | enum(‘N’,‘Y’) | NO | N | 是否可以通过UPDATE 命令修改现有数据 |
Delete_priv | enum(‘N’,‘Y’) | NO | N | 是否可以通过DELETE 命令删除现有数据 |
Create_priv | enum(‘N’,‘Y’) | NO | N | 是否可以创建新的数据库和表 |
Drop_priv | enum(‘N’,‘Y’) | NO | N | 是否可以删除现有数据库和表 |
Reload_priv | enum(‘N’,‘Y’) | NO | N | 是否可以执行刷新和重新加载MySQL所用的各种内部缓存的特定命令,包括日志、权限、主机、查询和表 |
Shutdown_priv | enum(‘N’,‘Y’) | NO | N | 是否可以关闭MySQL服务器。将此权限提供给root账户之外的任何用户时,都应当非常谨慎 |
Process_priv | enum(‘N’,‘Y’) | NO | N | 是否可以通过SHOW PROCESSLIST命令查看其他用户的进程 |
File_priv | enum(‘N’,‘Y’) | NO | N | 是否可以执行SELECT INTO OUTFILE和LOAD DATA INFILE命令 |
Grant_priv | enum(‘N’,‘Y’) | NO | N | 是否可以将自己的权限再授予其他用户 |
References_priv | enum(‘N’,‘Y’) | NO | N | 是否可以创建外键约束 |
Index_priv | enum(‘N’,‘Y’) | NO | N | 是否可以对索引进行增删查 |
Alter_priv | enum(‘N’,‘Y’) | NO | N | 是否可以重命名和修改表结构 |
Show_db_priv | enum(‘N’,‘Y’) | NO | N | 是否可以查看服务器上所有数据库的名字,包括用户拥有足够访问权限的数据库 |
Super_priv | enum(‘N’,‘Y’) | NO | N | 是否可以执行某些强大的管理功能,例如通过KILL命令删除用户进程;使用SET GLOBAL命令修改全局MySQL变量,执行关于复制和日志的各种命令。(超级权限) |
Create_tmp_table_priv | enum(‘N’,‘Y’) | NO | N | 是否可以创建临时表 |
Lock_tables_priv | enum(‘N’,‘Y’) | NO | N | 是否可以使用LOCK TABLES命令阻止对表的访问/修改 |
Execute_priv | enum(‘N’,‘Y’) | NO | N | 是否可以执行存储过程 |
Repl_slave_priv | enum(‘N’,‘Y’) | NO | N | 是否可以读取用于维护复制数据库环境的二进制日志文件 |
Repl_client_priv | enum(‘N’,‘Y’) | NO | N | 是否可以确定复制从服务器和主服务器的位置 |
Create_view_priv | enum(‘N’,‘Y’) | NO | N | 是否可以创建视图 |
Show_view_priv | enum(‘N’,‘Y’) | NO | N | 是否可以查看视图 |
Create_routine_priv | enum(‘N’,‘Y’) | NO | N | 是否可以更改或放弃存储过程和函数 |
Alter_routine_priv | enum(‘N’,‘Y’) | NO | N | 是否可以修改或删除存储函数及函数 |
Create_user_priv | enum(‘N’,‘Y’) | NO | N | 是否可以执行CREATE USER命令,这个命令用于创建新的MySQL账户 |
Event_priv | enum(‘N’,‘Y’) | NO | N | 是否可以创建、修改和删除事件 |
Trigger_priv | enum(‘N’,‘Y’) | NO | N | 是否可以创建和删除触发器 |
Create_tablespace_priv | enum(‘N’,‘Y’) | NO | N | 是否可以创建表空间 |
如果要修改权限,可以使用 GRANT
语句为用户赋予一些权限,也可以通过 UPDATE
语句更新 user 表的方式来设置权限。
安全列
安全列主要用来判断用户是否能够登录成功,user 表中的安全列如表所示:
字段名 | 字段类 | 是否为空 | 默认值 | 说明 |
---|---|---|---|---|
ssl_type | enum( ‘ANY’,‘X509’,‘SPECIFIED’) | NO | 支持ssl标准加密安全字段 | |
ssl_cipher | blob | NO | 支持ssl标准加密安全字段 | |
x509_issuer | blob | NO | 支持x509标准字段 | |
x509_subject | blob | NO | 支持x509标准字段 | |
plugin | char(64) | NO | mysql_native_password | 引入plugins以进行用户连接时的密码验证,plugin创建外部/代理用户 |
password_expired | enum(‘N’,‘Y’) | NO | N | 密码是否过期 (N 未过期,y 已过期) |
password_last_changed | timestamp | YES | 记录密码最近修改的时间 | |
password_lifetime | smallint(5) unsigned | YES | 设置密码的有效时间,单位为天数 | |
account_locked | enum(‘N’,‘Y’) | NO | N | 用户是否被锁定(Y 锁定,N 未锁定) |
[注]:
- 即使 password_expired 为“Y”,用户也可以使用密码登录 MySQL,但是不允许做任何操作。
- 通常标准的发行版不支持 ssl,读者可以使用 SHOW VARIABLES LIKE “have_openssl” 语句来查看是否具有 ssl 功能。如果 have_openssl 的值为 DISABLED,那么则不支持 ssl 加密功能。
资源控制列
资源控制列的字段用来限制用户使用的资源,user 表中的资源控制列如表所示。
字段名 | 字段类型 | 是否为空 | 默认值 | 说明 |
---|---|---|---|---|
max_questions | int(11) unsigned | NO | 0 | 规定每小时允许执行查询的操作次数 |
max_updates | int(11) unsigned | NO | 0 | 规定每小时允许执行更新的操作次数 |
max_connections | int(11) unsigned | NO | 0 | 规定每小时允许执行的连接操作次数 |
max_user_connections | int(11) unsigned | NO | 0 | 规定允许同时建立的连接次数 |
以上字段的默认值为 0,表示没有限制。一个小时内用户查询或者连接数量超过资源控制限制,用户将被锁定,直到下一个小时才可以在此执行对应的操作。可以使用 GRANT 语句更新这些字段的值。
2. db(数据库级别权限)
db 表比较常用,是 MySQL 数据库中非常重要的权限表,表中存储了用户对某个数据库的操作权限。表中的字段大致可以分为两类,分别是用户列和权限列。
用户列
db 表用户列有 3 个字段,分别是 Host、User、Db,标识从某个主机连接某个用户对某个数据库的操作权限,这 3 个字段的组合构成了 db 表的主键。
db 表的用户列如下表所示:
字段名 | 字段类型 | 是否为空 | 默认值 | 说明 |
---|---|---|---|---|
Host | char(60) | NO | 无 | 主机名 |
Db | char(64) | NO | 无 | 数据库名 |
User | char(32) | NO | 无 | 用户名 |
权限列
db 表中的权限列和 user 表中的权限列大致相同,只是user 表中的权限是针对所有数据库的,而 db 表中的权限只针对指定的数据库。如果希望用户只对某个数据库有操作权限,可以先将 user 表中对应的权限设置为 N,然后在 db 表中设置对应数据库的操作权限。
3. tables_priv(表级别权限)
tables_priv 表用来对单个表进行权限设置。
tables_priv 表结构如下表所示:
字段名 | 字段类型 | 是否为空 | 默认值 | 说明 |
---|---|---|---|---|
Host | char(60) | NO | 无 | 主机 |
Db | char(64) | NO | 无 | 数据库名 |
User | char(32) | NO | 无 | 用户名 |
Table_name | char(64) | NO | 无 | 表名 |
Grantor | char(93) | NO | 无 | 修改该记录的用户 |
Timestamp | timestamp | NO | CURRENT_TIMESTAMP | 修改该记录的时间 |
Table_priv | set(‘Select’,‘Insert’,‘Update’,‘Delete’,’ Create’,‘Drop’,‘Grant’,‘References’, ‘Index’,‘Alter’,‘Create View’,‘Show view’,‘Trigger’) | NO | 无 | 表示对表的操作权限,包括 Select、Insert、Update、Delete、Create、Drop、Grant、References、Index 和 Alter 等 |
Column_priv | set(‘Select’,‘Insert’,‘Update’, ‘References’) | NO | 无 | 表示对表中的列的操作权限,包括 Select、Insert、Update 和 References |
4. columns_priv(列级别的权限)
columns_priv 表用来对单个数据列进行权限设置
字段名 | 字段类型 | 是否为空 | 默认值 | 说明 |
---|---|---|---|---|
Host | char(60) | NO | 无 | 主机 |
Db | char(64) | NO | 无 | 数据库名 |
User | char(32) | NO | 无 | 用户名 |
Table_name | char(64) | NO | 无 | 表名 |
Column_name | char(64) | NO | 无 | 数据列名称,用来指定对哪些数据列具有操作权限 |
Timestamp | timestamp | NO | CURRENT_TIMESTAMP | 修改该记录的时间 |
Column_priv | set(‘Select’,’ Insert’,‘Update’,‘References’) | NO | 无 | 表示对表中的列的操作权限,包括 Select、Insert、Update 和 References |
5. procs_priv (存储过程和函数的权限)
procs_priv 表可以对存储过程和存储函数进行权限设置,procs_priv 的表结构如表所示:
字段名 | 字段类型 | 是否为空 | 默认值 | 说明 |
---|---|---|---|---|
Host | char(60) | NO | 无 | 主机名 |
Db | char(64) | NO | 无 | 数据库名 |
User | char(32) | NO | 无 | 用户名 |
Routine_name | char(64) | NO | 无 | 表示存储过程或函数的名称 |
Routine_type | enum(‘FUNCTION’, ‘PROCEDURE’) | NO | 无 | 表示存储过程或函数的类型,Routine_type 字段有两个值,分别是 FUNCTION 和 PROCEDURE。FUNCTION 表示这是一个函数;PROCEDURE 表示这是一个 存储过程。 |
Grantor | char(93) | NO | 无 | 插入或修改该记录的用户 |
Proc_priv | set(‘Execute’,‘Alter Routine’,‘Grant’) | NO | 无 | 表示拥有的权限,包括 Execute、Alter Routine、Grant 3种 |
Timestamp | timestamp | NO | CURRENT_TIMESTAMP | 表示记录更新时间 |
源博客
http://c.biancheng.net/view/7249.html
http://c.biancheng.net/view/7253.html
四、sys数据库
https://blog.csdn.net/chenshm/article/details/120519044
1. session
在MySQL 5.6以前,我们通过show processlist\G
命令查看系统中正在运行的所有进程:
mysql> show processlist;
+----+------+-----------+-------+---------+------+----------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+-------+---------+------+----------+------------------+
| 6 | root | localhost | mysql | Query | 0 | starting | show processlist |
+----+------+-----------+-------+---------+------+----------+------------------+
1 row in set (0.00 sec)
从5.7开始,我们又可以通过select * from sys.session
来查看系统正在运行的所有进程,而且该表中的记录相processlist
比较完善:
mysql> select * from sys.session\G
*************************** 1. row ***************************thd_id: 31conn_id: 6user: NULLdb: mysqlcommand: Querystate: Sending datatime: 0current_statement: select * from sys.sessionstatement_latency: 1.79 msprogress: NULLlock_latency: 0 psrows_examined: 0rows_sent: 0rows_affected: 0tmp_tables: 4tmp_disk_tables: 1full_scan: YESlast_statement: NULL
last_statement_latency: NULLcurrent_memory: 0 byteslast_wait: NULLlast_wait_latency: NULLsource: NULLtrx_latency: NULLtrx_state: NULLtrx_autocommit: NULLpid: 4612program_name: mysql
1 row in set (0.06 sec)
源博客
https://www.jianshu.com/p/427cac0d8763
五、DCL(Data Control Language)
数据控制语言,用来创建数据库用户,控制数据库的访问权限
0. 系统状态查看
查看当前登录用户的状态status
mysql> status
--------------
mysql Ver 8.0.11 for macos10.13 on x86_64 (MySQL Community Server - GPL)Connection id: 790
Current database: myweb_db
Current user: root@localhost
SSL: Not in use
Current pager: less
Using outfile: ''
Using delimiter: ;
Server version: 8.0.11 MySQL Community Server - GPL
Protocol version: 10
Connection: Localhost via UNIX socket
Server characterset: utf8mb4
Db characterset: utf8mb4
Client characterset: utf8mb4
Conn. characterset: utf8mb4
UNIX socket: /tmp/mysql.sock
Uptime: 60 days 53 min 36 secThreads: 25 Questions: 10957 Slow queries: 0 Opens: 578 Flush tables: 2 Open tables: 525 Queries per second avg: 0.002
--------------
[注]:
-
使用命令
mysql -u用户名 -hlocalhost -p
登录时,连接方式是Connection: Localhost via UNIX socket
-
使用命令
mysql -u用户名 -h ip -p
登录时连接方式是Connection: xxx.xxx.xxx.xxx via TCP/IP
1. 管理用户
查询用户
select * from mysql.user;
创建用户
#create user '用户名'@'主机名' identified by '密码';mysql> create user 'user1'@'localhost' identified by '123456';
Query OK, 0 rows affected (0.08 sec)
[注]:
- 主机名可以使用%通配,Host=%,表示所有IP都有连接此数据库。mysql 5.7和mysql 8.0版本中,%包含localhost。
修改用户密码
方法1:alter命令
#alter user '用户名'@'主机名' identified with mysql_native_password by '新密码';mysql> alter user 'user1'@'localhost' identified with mysql_native_password by '1234abcd';
Query OK, 0 rows affected (0.03 sec)~ ᐅ mysql -uuser1 -p123456 #登录失败
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1045 (28000): Access denied for user 'user1'@'localhost' (using password: YES)~ ᐅ mysql -uuser1 -p1234abcd #登录成功
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 776
Server version: 8.0.11 MySQL Community Server - GPLCopyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
方法2:set password命令
set password for '用户名'@'主机名'='新密码';set password for 'root'@'localhost'='123';
方法3:update直接编辑mysql.user表
update mysql.user set authentication_string=password('新密码') where user='用户名' and Host ='主机名';
flush privileges;update mysql.user set authentication_string=password('12344321') where user='root' and Host ='localhost';
flush privileges;
方法4:无需登录mysql,直接使用mysqladmin命令修改密码
mysqladmin -u用户名 -p旧密码 password 新密码
#-u用户名 -p旧密码是整体,不要写成-u 用户名 -p 旧密码,-u 和 root 间可以加空格,但是会有警告出现,所以就不要加空格了。
删除用户
#drop user '用户名'@'主机名';mysql> drop user 'user1'@'localhost';
Query OK, 0 rows affected (0.06 sec)
2. 权限控制
常用的权限
权限 | 说明 |
---|---|
all,all privileges | 所有权限 |
select | 查询数据 |
insert | 插入数据 |
update | 修改数据 |
delete | 删除数据 |
alter | 修改表 |
drop | 删除数据库/表/视图 |
create | 创建数据库/表 |
查询权限
#show grants for '用户名'@'主机名';mysql> show grants for 'user1'@'localhost';
+-------------------------------------------+
| Grants for user1@localhost |
+-------------------------------------------+
| GRANT USAGE ON *.* TO `user1`@`localhost` |
+-------------------------------------------+
1 row in set (0.00 sec)
授予权限
#grant 权限列表 on 数据库名.表名 to '用户名'@'主机名';#赋予'user1'@'localhost'对所有数据库所有表的创建和查询权限
mysql> grant create,select on *.* to 'user1'@'localhost';
Query OK, 0 rows affected (0.10 sec)mysql> FLUSH PRIVILEGES; #刷新权限
Query OK, 0 rows affected (0.01 sec)mysql> show grants for 'user1'@'localhost'; #查询权限
+----------------------------------------------------+
| Grants for user1@localhost |
+----------------------------------------------------+
| GRANT SELECT, CREATE ON *.* TO `user1`@`localhost` |
+----------------------------------------------------+
1 row in set (0.00 sec)
刷新权限
MySQL权限刷新是指在MySQL中重新加载授权表,使修改后的用户权限生效。FLUSH命令可以刷新MySQL的各种缓存,包括授权表缓存。
FLUSH PRIVILEGES;
[注]:
MySQL权限刷新注意事项:
- 刷新权限需要以root用户登录MySQL数据库。
- 刷新权限可能会影响MySQL的性能,建议在低峰期进行操作。
- 刷新权限后,所有修改的用户权限都会生效,包括新增用户和修改已有用户的权限。
撤销权限
#revoke 权限列表 on 数据库名.表名 from '用户名'@'主机名';mysql> revoke create on *.* from 'user1'@'localhost';
Query OK, 0 rows affected (0.01 sec)mysql> show grants for 'user1'@'localhost';
+--------------------------------------------+
| Grants for user1@localhost |
+--------------------------------------------+
| GRANT SELECT ON *.* TO `user1`@`localhost` |
+--------------------------------------------+
1 row in set (0.00 sec)
[注]:
- 多个权限之间,使用逗号分割
- 授权时,数据库名和表名可以使用 * 进行通配,代表所有
六、DDL(Data Definition Language)
数据定义语言,用来定义数据库对象(数据库,表,字段)
0. show命令
show tables或show tables from database_name; — 显示当前数据库中所有表的名称。show databases; — 显示mysql中所有数据库的名称。show columns from table_name from database_name; 或show columns from database_name.table_name; — 显示表中列名称。show grants for user_name; — 显示一个用户的权限,显示结果类似于grant 命令。show index from table_name; — 显示表的索引。show status; — 显示一些系统特定资源的信息,例如,正在运行的线程数量。show variables; — 显示系统变量的名称和值。show processlist; — 显示系统中正在运行的所有进程,也就是当前正在执行的查询。大多数用户可以查看他们自己的进程,但是如果他们拥有process权限,就可以查看所有人的进程,包括密码。show table status; — 显示当前使用或者指定的database中的每个表的信息。信息包括表类型和表的最新更新时间。show privileges; — 显示服务器所支持的不同权限。show create database database_name; — 显示create database 语句是否能够创建指定的数据库。show create table table_name; — 显示create database 语句是否能够创建指定的数据库。show engies; — 显示安装以后可用的存储引擎和默认引擎。show innodb status; — 显示innoDB存储引擎的状态。show logs; — 显示BDB存储引擎的日志。show warnings; — 显示最后一个执行的语句所产生的错误、警告和通知。show errors; — 只显示最后一个执行语句所产生的错误。show [storage] engines; –显示安装后的可用存储引擎和默认引擎。
1. 查询
查询mysql版本
select version();mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.11 |
+-----------+
1 row in set (0.00 sec)
查询所有数据库
show databases;
查询当前正在使用的数据库
select database();
查询当前数据库中的表
show tables;
查询指定数据库中的表
show tables from 数据库名;
mysql> show tables from myweb_db;
+--------------------+
| Tables_in_myweb_db |
+--------------------+
| emp |
| user |
+--------------------+
2 rows in set (0.00 sec)
查询指定表中的所有字段
show columns from 表名 from 数据库名;
或
show columns from 数据库名.表名;
mysql> show columns from myweb_db.user;
查看指定数据库的字符集
show create database 数据库名;mysql> show create database myweb_db;
+----------+-------------------------------------------------------------------------------------------------+
| Database | Create Database |
+----------+-------------------------------------------------------------------------------------------------+
| myweb_db | CREATE DATABASE `myweb_db` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */ |
+----------+-------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
查询表的字符集
方法1
show create table 表名;mysql> show create table user;
| Table | Create Table | user | CREATE TABLE `user` (`id` bigint(255) unsigned NOT NULL AUTO_INCREMENT,`username` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`password` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`age` tinyint(3) unsigned DEFAULT NULL,`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,`nickname` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`is_delete` tinyint(1) unsigned DEFAULT '0' COMMENT '逻辑删除,0表示不删除,1表示删除,默认值为0\n',`is_enable` tinyint(1) unsigned DEFAULT '1' COMMENT '该用户是否可用,1表示可用,0表示不可用。默认值为1',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
方法2
show table status from myweb_db like '表名';mysql> show table status from myweb_db like 'user'\G
*************************** 1. row ***************************Name: userEngine: InnoDBVersion: 10Row_format: DynamicRows: 5Avg_row_length: 3276Data_length: 16384
Max_data_length: 0Index_length: 0Data_free: 0Auto_increment: 9Create_time: 2023-04-30 14:53:02Update_time: 2023-05-05 22:41:41Check_time: NULLCollation: utf8mb4_unicode_ciChecksum: NULLCreate_options:Comment:
1 row in set (0.00 sec)
查询表中列的属性
show full columns from 表名;
2. 创建
创建数据库
create database [if not exists] 数据库名 [default charset 字符集][collate 排序规则];
[注]:
- 中括号里的可加可不加,具体情况而定
- 第一个是如果不存在相同名称的数据库则创建
- 第二个是设置字符的字符集和排序规则
创建表
create table 表名 (字段1 字段1类型[comment 字段1注释],字段2 字段2类型[comment 字段2注释],字段3 字段3类型[comment 字段3注释],......字段n 字段n类型[comment 字段n注释]
)[comment 表注释];
create table if not exists t1 (id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'id',name VARCHAR(10) COMMENT '名字',age TINYINT UNSIGNED COMMENT '性别',create_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',update_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日期',is_delete TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否逻辑删除,0表示未删除,1表示已删除',PRIMARY KEY(id)
)comment '测试用表' ENGINE INNODB DEFAULT CHARSET=utf8;
[注]:
- DEFAULT CURRENT_TIMESTAMP表示当新增记录的时候,MySQL自动将系统的当前时间 set 到create_date和更新时间这两个字段中。
- 设置联合主键
primary key(列1,列2,…)
- 设置联合主键的原因是列1和列2都有可能存在重复数据,而列1,列2,…构成的联合主键必须是唯一的,例如在成绩表中设置学生id和课程id为联合主键,因为一个学生可以选择多门课,一门课也可以被多个学生选,所以学生id和课程id均有可能重复,但是学生id+课程id是唯一的,所以设置学生id和课程id为联合主键。
3. 使用
使用数据库
use 数据库名;
使用表的语句是select
4. 删除
删除数据库
drop database [if exists] 数据库名;
删除表
drop table [if exists] 表名;
格式化表
删除指定表,并重新创建该表(俗称格式化),即清空表中的数据,保留列
truncate table 表名;
5. 修改
修改数据库字符集和排序规则
alter database 数据库名 [character set '字符集'] [collate '排序规则'];
[注]:
- 修改完数据库字符集,需要重启mysql数据库。
修改表字符集和排序规则
ALTER TABLE 表名 [DEFAULT CHARACTER SET '字符集'] [COLLATE '排序规则'];
修改表指定列的字符集
ALTER TABLE 表名 MODIFY COLUMN 列名 数据类型 [CHARACTER SET '字符集'] [COLLATE '排序规则'];#实例
ALTER TABLE t1 MODIFY COLUMN name VARCHAR(10) CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_0900_ai_ci';
[注]:
- 在执行命令时,如果表格中已存在数据,修改字符集可能会导致某些数据无法正确存储。因此,在修改字符集之前,先备份好数据,以便于后续恢复数据。
修改表名
alter table 表名 rename to 新表名;
表添加字段
alter table 表名 add 字段名 类型(长度) [comment 注释][约束];
修改表字段的数据类型
alter table 表名 modify 字段名 新数据类型(长度);
修改表字段名和字段类型
alter table 表名 change 旧字段名 新字段名 类型(长度)[comment 注释][约束];
删除表的字段
alter table 表名 drop 字段名;
源博客
https://blog.csdn.net/weixin_32006655/article/details/113942961
七、DML(Data Manipulation Language)
数据操作语言,用来对数据库表中的数据进行增删改
0. 创建表
create table worktable(
id int comment '编号',
worknum int comment '工号',
name varchar(20) comment '姓名',
sex char(1) comment '性别',
age int comment '年龄',
idcard int comment '身份证号',
entrydate date comment '入职日期'
)comment '员工信息表';
1. 插入数据
给指定列名数据
insert into 表名(字段名1,字段名2,.....) values(值1,值2,......);
给全部列添加数据
insert into 表名 values(值1,值2,.....);
批量添加数据
#指定列名
insert into
表名(字段名1,字段名2,.....)
values
(值1,值2,......),
(值1,值2,......),
......
(值1,值2,......);#全部列
insert into
表名
values
(值1,值2,......),
(值1,值2,......),
......
(值1,值2,......);
[注]:
- 插入数据时,指定的字段顺序需要与值的顺序是一一对应的
- 字符串和日期型数据应该包含在引号中
- 插入的数据大小,应该在字段的规定范围内
2. 修改数据
update 表名 set 列名1=值1,列名2=值2,....[where 条件];
[注]:
- 修改语句的条件可以有,也可以没有,如果没有条件,则会修改整张表的所有数据
3. 删除数据
delete from 表名 [where 条件];
[注]:
-
delete语句的条件可以有,也可以没有,如果没有条件,则会删除整张表的所有数据
-
delete语句不能删除某一个字段的值(可以使用update来将某字段置空)
八、DQL(Data Query Language)
数据查询语言,用来查询数据库中表的记录
整体语法概览聚合函数(count,max,min,avg,sum):
- 基本查询(select 列名1,列名2,… from 表名)
- 条件查询(where)
- 聚合函数(count,max,min,avg,sum)
- 分组查询(group by)
- 排序查询(order by)
- 分页查询(limit)
0. 创建表
create table emp(
id int comment '编号',
worknum varchar(10) comment '工号',
name varchar(10) comment '姓名',
gender char(1) comment '性别',
age tinyint unsigned comment '年龄',
idcard char(18) comment '身份证号',
workaddress varchar(50) comment '工作地址',
entrydate date comment '入职时间'
)comment '员工表';insert into emp (id,worknum,name,gender,age,idcard,workaddress,entrydate)
values (1,'1','柳岩','女',20,'123456789012345678','北京','2000-01-01'),(2,'2','张无忌','男',18,'123456789012345670','北京','2005-09-01'),(3,'3','韦一笑','男',38,'123456789712345670','上海','2005-08-01'),(4,'4','赵敏','女',18,'123456757123845670','北京','2009-12-01'),(5,'5','小昭','女',16,'123456769012345678','上海','2007-07-01'),(6,'6','杨逍','男',28,'12345678931234567X','北京','2006-01-01'),(7,'7','范瑶','男',40,'123456789212345670','北京','2005-05-01'),(8,'8','黛绮丝','女',38,'123456157123645670','天津','2015-05-01'),(9,'9','范凉凉','女',45,'123156789012345678','北京','2010-04-01'),(10,'10','陈友谅','男',53,'123456789012345670','上海','2011-01-01'),(11,'11','张士诚','男',55,'123567897123465670','江苏','2015-05-01'),(12,'12','常遇春','男',32,'123446757152345670','北京','2004-02-01'),(13,'13','张三丰','男',88,'123656789012345678','江苏','2020-11-01'),(14,'14','灭绝','女',65,'123456719012345670','西安','2019-05-01'),(15,'15','胡青牛','男',70,'12345674971234567X','西安','2018-04-01'),(16,'16','周芷若','女',18,null,'北京','2012-06-01');
1. 基本查询
查询多个字段
select 字段1,字段2,字段3.....from 表名; #查询指定字段
select * from 表名; #查询全部字段
设置别名
select 字段1 [as 别名1],字段2 [as 别名2] .... from 表名 [as 表别名]; #as可省略
去除重复记录
select distinct 字段列表 from 表名;
select distinct workaddress from emp;
2. 条件查询
select 字段列表 from 表名 where 条件列表;
常见条件
比较运算符 | 功能 | 逻辑运算符 | 功能 |
---|---|---|---|
> | 大于 | and 或 && | 并且(多个条件同时成立) |
>= | 大于等于 | or 或 || | 或者(多个条件任意一个成立) |
< | 小于 | not 或 ! | 非,不是 |
<= | 小于等于 | ||
= | 等于 | ||
<> 或 != | 不等于 | ||
between…and… | 在某个范围内(含最小,最大值) | ||
in(…) | 在in之后的列表中的值,多选一 | ||
like 占位符 | 模糊匹配(_匹配单个字符,%匹配任意个字符) | ||
is null | 是null |
3. 聚合函数
常见聚合函数
函数 | 功能 |
---|---|
count | 统计数量 |
max | 最大值 |
min | 最小值 |
avg | 平均值 |
sum | 求和 |
select 聚合函数(字段列表) from 表名;
[注]:
- null值不参与所有聚合函数运算
- count(*)、count(1)将返回表格中所有存在的行的总数(包括值为null的行)
- count(列名) 会统计该字段在表中出现的次数,忽略字段为null 的情况。即不统计字段为null 的记录(这里的空不是指空字符串或者0)。
- count(1), count(*), count(列名)详解:https://blog.csdn.net/weixin_44363315/article/details/125994997
4. 分组查询
select 字段列表 from 表名 [where 条件] group by 分组字段名 [having 分组过滤条件];
[注]:where 与 having 区别
1.执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;
having是分组之后对结果进行过滤。
2.判断条件不同:where不能对聚合函数进行判断,而having可以。
5. 排序查询
select 字段列表 from 表名 order by 字段1 排序方式1 , 字段2 排序方式2;
[注]:
- 排序方式: asc:升序(默认值)、desc:降序
- 如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序。
6. 分页查询
select 字段列表 from 表名 limit 起始索引,查询记录数;
[注]:
- 起始索引从0开始,起始索引 = (当前页码 - 1) * 每页显示记录数
- 分页查询是数据库的方言,不同的数据库有不同的实现,MySQL中是limit
- 如果查询的是第一页数据,起始索引可以省略,直接简写为limit 10
7. 整体语法顺序
九、常见函数
函数是指一段可以直接被另一段程序调用的程序或代码。
1. 字符串函数
由于业务需求变更,企业员工的工号,统一为5位数,目前不是5位数的全部在员工的工号前面补0,比如:1号员工的工号应该为00001.
sql:update emp set workno=lpad(workno, 5, '0')
执行前:
执行后:
substring_index()
参数说明:
- 第一个参数为需要被截取的字符串列
- 第二个参数为按指定的什么字符截取,例如按 逗号 或 分号
- 第三个参数为从指定下标索引处开始截取,下标从1开始,指定的下标值需要在分隔后的数量范围内,返回对应下标范围内的全部数据
案例:
selectid,remark,substring_index(remark, ',', 1) as subStr
from t_table_one
where instr(remark, 'cc');
charset(s1)
返回字符串s1的字符集。
length(s1)
返回字符串s1的长度,结果的单位是字节。
replace(s1,s2,s3)
s1是想要改变的字符串,s2是要改变什么,s3是要将字符串中的s2改变成什么。
instr(s1,s2)
返回该串(第二个参数)在字符串(第一个参数)中出现的位置,如果没有出现就返回0。
strcmp(s1,s2)
字符串长度比较,如果相等返回0,如果前面的大返回1,如果后面的大返回-1。
left、right
从左/右边开始取字符,字符从1开始
ltrim、rtrim
去除左/右空格
2. 数学函数
3. 日期函数
https://blog.csdn.net/qq_60750110/article/details/131012270
4. 流程函数
ifnull(val1, val2)
如果val1为null,返回val2,否则返回val1的值。类似于三目运算符
case when then else end
案例1:
案例2:
统计班级各个学员的成绩,展示的规则如下:
大于等于 85,展示优秀;
大于等于60,展示及格;
否则,展示不及格。
准备数据:
sql:
显示结果:
5. 其他函数
json_extract
json_extract
函数是MySQL数据库中的一个内置函数,Mysql5.7+之后提供,用于从JSON字符串中提取数据。它的语法相对简单,功能却非常强大。
用法:
select json_extract(json_doc, path[, path] ...)json_doc:这是你要提取数据的JSON字符串。
path:这是你要提取的数据的路径,可以是一个或多个。
若json对象时,可以通过json_extract(‘db字段’, ‘$.json串key’)来表示查询对应的value
实例:假设我们有一个名为users
的表,其中有一个名为data
的列,存储着JSON格式的数据,如下所示:
{ "name": "张三", "age": 30, "address": { "city": "北京", "street": "长安街" }
}
如果我们想要提取name
字段的值,可以这样写:
SELECT json_extract(data, '$.name');
这样相应的就可以获取到: 张三。
如果我们想要获取到city字段的值,涉及到了嵌套。那么可以这么处理:
SELECT json_extract(data, '$.address.city') ;
#使用.来拼接,获取内部字段内容
如果查询的key不在json串中,返回的是null,而不是抛异常。
如果是json数组时,使用json_extract(‘db字段’, ‘$数组下标.json串key’)
除了在查询结果中使用json_extract之外,也可以在查询条件中使用它
group_concat()
https://blog.csdn.net/hkl_Forever/article/details/116704472
函数 group_concat(arg) 可以合并多行的某列(或多列)数据为一行,默认以逗号分隔。以及分组函数和统计函数的组合使用
案例:
-
单列合并,默认以逗号分隔
selectgroup_concat(ttop.user_name) as testStr from t_table_one_parent ttop;
输出:张三1,张三2,张三3,张三1,张三2,张三3,张三4
-
单列合并,指定冒号分隔符
selectgroup_concat(ttop.user_name separator ';') as testStr from t_table_one_parent ttop;
输出:张三1;张三2;张三3;张三1;张三2;张三3;张三4
-
单列合并,并去重
selectgroup_concat(distinct ttop.user_name separator ';') as testStr from t_table_one_parent ttop;
输出:张三1;张三2;张三3;张三4
-
多列拼接合并
selectgroup_concat(distinct ttop.user_name, ttop.company_code separator ';') as testStr from t_table_one_parent ttop;
输出:张三1123456;张三21234567;张三312345678;张三4123456789
-
多列拼接合并,列和列之间指定分隔符
selectgroup_concat(distinct ttop.user_name, ',', ttop.company_code separator ';') as testStr from t_table_one_parent ttop;
输出:张三1,123456;张三2,1234567;张三3,12345678;张三4,123456789
小结:
-
group_concat() 函数默认合并后以逗号分隔,也可以自定义分隔符
-
group_concat() 函数可以多列合并,列和列之间可以自定义分隔符
-
group_concat() 函数可以使用 distinct 进行去重合并
count() 配合 case when then 的使用
https://blog.csdn.net/hkl_Forever/article/details/116704472
user()
查询当前用户。
md5
对一个字符串进行md5摘要,摘要后得到一个32位字符串。
password
MySQL使用该函数对其加密,MySQL有自己独特的加密方式,本质上与md5形成摘要是一样的,但是密码强度更强。
十、存储引擎
- 连接层
最上层是一些客户端和链接服务,主要完成一些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
- 服务层
第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如过程、函数等。
- 引擎层
存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。
- 存储层
主要是将数据存储在文件系统之上,并完成与存储引擎的交互。
1. 存储引擎介绍
存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的,同一个数据库下的不同表可以选择不同的存储引擎,所以存储引擎也可被称为表类型。
-
查看数据库所支持的存储引擎
show engines;
-
创建表时指定存储引擎
create table my_memory(id int,name varchar(10) ) engine = Memory;
2. Innodb
- 介绍
InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,在 MySQL 5.5之后,InnoDB是默认的 MySQL 存储引擎。
- 特点
1、 DML操作遵循ACID模型,支持事务;
2 、行级锁,提高并发访问性能;
3、 支持外键 FOREIGN KEY约束,保证数据的完整性和正确性;
- 文件
xxx.ibd:xxx代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文件,存储该表的表结构(frm、sdi)、数据和索引。
- Innodb逻辑存储结构
3. MyISAM
- 介绍
MyISAM是MySQL早期的默认存储引擎。
- 特点
1、 不支持事务,不支持外键
2、 支持表锁,不支持行锁
3、 访问速度快
- 文件
1、 xxx.sdi:存储表结构信息
2、 xxx.MYD:存储数据
3、 xxx.MYI:存储索引
4. Memory
- 介绍
Memory引擎的表数据时存储在内存中的,由于受到硬件问题、或断电问题的影响,只能将这些表作为临时表或缓存使用。
- 特点
1、 内存存放
2、 hash索引(默认)
- 文件
xxx.sdi:存储表结构信息
- 存储引擎对比
5. 存储引擎选择
十一、索引
1. 索引概述
- 介绍
索引(index)是帮助MySQL,高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
- 优缺点
- 索引结构
MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含以下几种:
- 不同存储引擎的索引结构支持情况
- 为什么使用B+树索引结构?
二叉树的缺点:顺序插入时,会形成一个链表,查询性能大大降低。
为了解决这个问题,出现了红黑树:
红黑树缺点:和二叉树一样,只有两个叶子结点,大数据量情况下,层级较深,检索速度慢。
为了解决这个问题,出现了多叉树,多叉树的优点就是极大的减少了树的深度,B树(多路平衡查找树)和B+树就是多叉树。3层深度的B树大约能存储100万的数据。深度减少能极大的减少磁盘读取次数,虽然每个节点比较次数不少,但是如果把每个节点的数据读到内存中比较,那这个比较时间就可以忽略不计了。所以用B树来存储对MySQL来说是非常合适的。
B树:
演示B树的构建过程:https://www.cs.usfca.edu/~galles/visualization/BTree.html
依次插入如下数据:100 65 169 368 900 556 780 35 215 1200 234 888 158 90 1000 88 120 268 250
B树有以下特点:
1.所有键值分布在整颗树中(索引值和具体data都在每个节点里);
2.任何一个关键字出现且只出现在一个结点中;
3.搜索有可能在非叶子结点结束(最好情况O(1)就能找到数据);
4.在关键字全集内做一次查找,性能逼近二分查找;
5.所有叶子节点位于同一层。
B+树:
和B树的区别:①所有的数据都会出现在叶子节点;②叶子节点形成一个单向链表。
B+树相对于B树有以下特点:
1.所有关键字都会在在叶子节点出现,
2.内部节点(非叶子节点)不存储数据,数据只在叶子节点存储
3.所有叶子结点构成了一个链指针,而且所有的叶子节点按照顺序排列。
那B+树比B树有什么优势呢?
- 每一层更宽,更胖,存储的数据更多。因为B+树只存储关键字,而不存储多余data.所以B+树的每一层相比B树能存储更多节点。
- 所有的节点都会在叶子节点上出现,查询效率更稳定。因为B+树节点上没有数据,所以要查询数据就必须到叶子节点上,所以查询效率比B树稳定。而在 B 树中,非叶子节点也会存储数据,这样就会造成查询效率不稳定的情况,有时候访问到了非叶子节点就可以找到关键字,而有时需要访问到叶子节点才能找到关键字。
- 查询效率比B树高。因为B+树更矮,更胖,所以和磁盘交互的次数比B树更少,而且B+树通过底部的链表也可以完成遍历,但是B树需要找到每个节点才能遍历,所以B+树效率更高。
总体来说,B+树因为更矮更胖能存储更多数据、效率稳定,读写磁盘次数少,比B-树效率更高,据统计,一棵深度为3的B+树能存储2000万以上条数据。所以最终选择了B+树。
MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能。
- Hash索引
哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。
- Hash索引特点
1、 Hash索引只能用于对等比较(=, in),不支持范围查询 (between, > ,<,…)
2、 无法利用索引完成排序操作
3、 查询效率高,通常只需要一次检索就可以了,效率通常要高于B+tree索引
在Mysql中,支特hash索引的是Memory存储引擎,而Innodb中具有自适应hash功能,hash索引是存储引擎根据B+Tree索引在指定条件下自动构建的。
2. 索引分类
主键索引是唯一索引的特殊类型。
在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:
聚集索引选取规则:
- 如果存在主键,主键索引就是聚集索引。
- 如果不存在主键,将使用第一个唯一(UNIQUE) 索引作为聚集索引。
- 如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引。
执行select * from user where name='Arm';
时如何走索引?
查询条件是name,所以只能走二级索引,Arm在比Lee小(A小于L),所以走左子树,Arm小于Geek,继续走左子树,找到了name=Arm对应的主键为10。因为查询的是数据的所有字段(select *),所以需要拿着主键10去聚集索引查找,最终找到了id=10的数据的所有字段。先走二级索引找到主键,再拿着主键走聚集索引找数据的过程称为回表查询。
问:InnoDB主键索引的B+tree高度3时,能存储多少数据呢?
答:
高度为3时可以存储两千多万条数据,如果超过两千多万条数据,则需要考虑分库分表了。
3. 索引语法
- 创建索引
CREATE [UNIQUE | FULLTEXT] INDEX index_name ON table_name (index_col_name,...);
- 查看索引
SHOW INDEX FROM table_name;
- 删除索引
DROP INDEX index_name ON table_name;
- 案例
1、 name字段为姓名字段,该字段的值可能会重复,为该字段创建索引。
create index idx_user_name on tb_user (name);
2、 phone手机号字段的值,是非空,且唯一的,为该字段创建唯一索引。
create unique index idx_user_phone on tb_user (phone);
3、 为profession、 age、 status创建联合索引。
create index idx_user_pro_age_sta on tb_user (profession, age, status);
4、 为email建立合适的索引来提升查询效率。
create index idx_user_email on tb_user (email);
4. SQL性能分析
- SQL执行频率
MySQL 客户端连接成功后,通过show [session | global] status
命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的INSERT、 UPDATE、 DELETE、 SELECT的访问频次:
SHOW GLOBAL STATUS LIKE 'Com_______';
- 慢查询日志
慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志。
查询是否开启慢查询日志(1为开启,0为关闭):
show variables 1ike 'slow_query_1og';
MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件 (/etc/my.cnf)中配置如下信息:
配置完毕之后,重新启动MySQL服务器进行测试,查看慢日志文件中记录的信息 /var/lib/mysql/localhost-slow.log。
- profile详情
show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。通过have_profiling参数,能够看到当前MySQL是否支持profile操作(yes表示支持):
SELECT @@have_profiling;
默认profiling是关闭的,查询是否开启profiling(0为关闭,1为开启):
select @@profiling;
可以通过set语句在session/global级别开启profiling:
SET profiling=1;
查看每一条sql的耗时基本情况:
show profiles;
查看指定query id的SQL语句各个阶段的耗时情况:
show profile for query query_id;
查看指定query_id的SQL语句CPU的使用情况:
show profile cpu for query query_id;
- explain执行计划
EXPLAIN 或者 DESC命令获取 MySQL 如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序。
# 直接在select语句之前加上关键字 explain / desc
EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件;
EXPLAIN 执行计划各字段含义:
id:
select查询的序列号,表示查询中执行select子句或者是操作表的顺序(id相同,执行顺序从上到下;id不同,值越大,越先执行)。
select_type:
表示 SELECT 的类型,常见的取值有 SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION (UNION 中的第二个或者后面的查询语句)、SUBQUERY (SELECT/WHERE之后包含了子查询)等。
type(重点):
表示连接类型,性能由好到差的连接类型为NULL、 system、 const、eq_ref、 ref、 range、 index、 all。
只有不访问任何表时才会为NULL:
访问系统表时会出现system;
根据主键或唯一索引访问时一般会出现const:
根据非唯一性索引查询时会出现ref:
index表示使用了索引,但是扫描遍历了整个索引树;
all表示进行了全表扫描。
possible_key(重点):
显示可能应用在这张表上的索引,一个或多个。
key(重点):
实际使用的索引,如果为NULL,则没有使用索引。
key_len(重点):
表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好。
rows:
MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估计值,可能并不总是准确的。
filtered:
表示返回结果的行数占需读取行数的百分比,filtered 的值越大越好。
Extra(重点):
Extra展示其他字段没有展示的内容。
5. 索引使用
- 验证索引效率
- 索引使用规则-最左前缀法则(联合索引)
如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将部分失效(后面的字段索引失效)。
查看tb_user的索引:
验证最左匹配原则:
- 索引使用规则-范围查询(联合索引)
联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效
- 索引使用规则-索引列操作
不要在索引列上进行运算操作,索引将失效。
- 索引使用规则-字符串不加引号
字符串类型字段使用时,不加引号,会发生隐式类型转换,会导致索引失效。
- 索引使用规则-模糊匹配
如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。
- 索引使用规则-or连接的条件
用or分割开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。
- 索引使用规则-数据分布影响
如果MySQL评估使用索引比全表更慢,则不使用索引。
- 索引使用规则-SQL提示
profession列建立了两个索引,如果不指定使用哪个索引,则mysql会自动选择一个索引。
SQL提示是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。
(1)使用use index(索引名字)
,建议mysql使用这个索引,mysql可能不接受
(2)使用ignore index(索引名字)
忽略这个索引
(3)使用force index(索引名字)
,强制mysql使用这个索引
- 索引使用规则-覆盖索引
尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到),减少select *
using index condition:查找使用了索引,但是需要回表查询数据
using where; using index:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据
思考:一张表,有四个字段(id, username, password, status), 由于数据量大,需要对以下SQL语句进行优化,该如何进行才是最优方案:
select id,username,password from tb_user where username = 'itcast';
答:针对username和password两个字段建立联合索引。
- 索引使用规则-建立前缀索引
当字段类型为字符串 (varchar, text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引/效率。
语法:
create index idx_xxxx on table_name(column(n));
前缀长度:可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。
公式求选择性的值:
select count(distinct substring(email,1,5))/count(*) from tb_user;
前缀索引的数据结构:
从前缀索引中找到第一个匹配的叶子结点lvbu6的主键1,然后回表查询聚集索引,找到id=1的数据,然后用这条数据的email列和lvbu666@163.com对比,如果相等则返回,然后继续查找辅助索引中lvbu6后面的数据…
- 索引使用规则-单列索引和联合索引如何选择
单列索引:即一个索引只包含单个列。
联合索引:即一个索引包含了多个列。
单列索引使用情况:
phone和name都是单列索引,但是查询条件是phone和name时,mysql却只使用了phone这一列的索引。
联合索引的数据结构:
在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引。
6. 索引设计原则
- 针对于数据量较大,且查询比较频繁的表建立索引,一般当数据超过百万条时,可以考虑建立索引。
- 针对于常作为查询条件(where)、排序(order by)、分组(group by) 操作的字段建立索引。
- 尽量选择区分度高的列作为索引,如手机号,身份证号等,尽量建立唯一索引,区分度越高,使用索引的效率越高。
- 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
- 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
- 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率,而且索引表会占用磁盘空间。
- 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。
7. SQL优化
- 插入数据优化
(1)批量插入
批量插入可以减少磁盘IO,减少网络传输,减少与数据库的连接次数。一次性批量插入数据的条数在500-1000条最佳,如果需要插入大于1000条数据,可以将插入语句分成多条批量插入sql,每条sql插入的数据控制在500-1000条。
insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
(2)统一手动事务提交
start transaction;
insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
insert into tb_test values(4,'Tom'),(5,'Cat'),(6,'Jerry');
insert into tb_test values(7,'Tom'),(8,'Cat'),(9,'Jerry');
commit;
(3)主键顺序插入
(4)大批量插入数据时使用load命令
如果一次性需要插入大批量数据,使用insert语句插入性能较低,此时可以使用MySQL数据库提供的load指令进行插入。操作如下:
语法:
- 主键优化
在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(index organized table IOT)
页分裂:
页可以为空,也可以填充一半,也可以填充100%。每个页包含了2-N行数据(如果一行数据太大,会行溢出),根据主键排列。
页合并:
当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记(flaged)为删除并且它的空间变得允许被其他记录声明使用。当页中删除的记录达到 MERGE_THRESHOLD(默认为页的50%),InnoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用。MERGE_THRESHOLD:合并页的阈值可以自己设置,在创建表或者创建索引时指定。
主键设计原则:
(1)满足业务需求的情况下,尽量降低主键的长度。
(2)插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键。
(3)尽量不要使用UUID做主键或者是其他自然主键,如身份证号。
(4)业务操作时,避免对主键的修改。
- order by优化
使用order by时,Extra会出现两种情况:
① Using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。
② Using index:通过有序索引顺序扫描直接返回有序数据,这种情况即为using index,不需要额外排序,操作效率高。
(1)根据排序字段建立合适的索引,多字段排序时,尽量使用覆盖索引,order by也遵循最左前缀法则。
age列没有建立索引,order by age
时会出现Using flesort
为age列和phone列建立联合索引,order by age,phone
时,会出现Using index
因为创建联合索引idx_user_age_phone时,age是第一个字段,phone是第二个字段,所以使用order by phone,age
时会出现phone使用Using index
,age使用Using filesort
的情况(违背了最左前缀法则)
(2)多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则 (ASC/DESC)
建立联合索引idx_user_age_phone时,默认age和phone都是按照升序排序的,所以使用order by age desc,phone desc
时会出现反向扫描索引Backward index scan;
,虽然是反向扫描索引,但是还是使用的Using index
因为联合索引idx_user_age_phone的age和phone都是按照升序排序的,使用order by age asc,phone desc
时,只有age使用Using index
,phone使用的是Using filesort
解决方法:
创建索引,age升序,phone降序
查看联合索引的结构:
查看执行计划,发现并没有出现Using filesort
(3)如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小 sort_buffer_size(默认256k)。
(4)使用order by时,同样需要尽量避免使用select *
- group by优化
(1) 在分组操作时,可以通过索引来提高效率。
profession列没有索引,使用group by profession
为profession,age, status建立联合索引
(2)分组操作时,索引的使用也需要满足最左前缀法则。
不满足最左前缀原则
满足最左前缀原则
where 联合索引的第一个列 group by 联合索引的第二个列,同样满足最左前缀原则
- limit优化
一个常见又非常头疼的问题就是 limit 2000000,10,此时需要MySQL排序前2000010 记录,仅仅返回2000000 - 2000010的记录,其他记录丢弃,查询排序的代价非常大。
优化思路:一般分页查询时,通过创建覆盖索引能够比较好地提高性能,可以通过覆盖索引加子查询形式进行优化。
select *
from tb_sku where id in (select id from tb_sku order by id limit 2000000,10);
或
explain select * from tb_sku t (select id from tb_sku order by id limit 2000000,10) a where t.id = a.id;
- count优化
MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行 count(*) 的时候会直接返回这个数,效率很高,前提是查询语句后面不加where条件。
InnoDB 引擎就麻烦了,它执行 count(*)的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数。
count的几种用法:
(1)count(*)
InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加。
(2)count(主键)
InnoDB 引擎会遍历整张表,把每一行的 主键id 值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加(主键不可能为null)。
(3)count(字段)
没有not null 约束:InnoDB 引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加。
有not null约束:InnoDB 引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,直接按行进行累加。
(4)count(1)
InnoDB 引擎遍历整张表,但不取值。服务层对于返回的每一行,放一个数字"1"进去,直接按行进行累加。
按照效率排序的话,count(字段)<count(主键id)<count(1) ≈ count(*),所以尽量使用 count(*)。
- update优化
InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁,所以更新操作时,要根据索引列进行更新,即update操作时where后面一定要是索引列。
(1)where后面的条件是索引列
会话1
会话2
(2)where后面的条件是非索引列
会话1
会话2
十二、视图
视图(view)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表(称为基表),并且是在使用视图时动态生成的。
通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。
1. 创建
创建视图后出现views目录,views目录下存储创建的视图
2. 查询
3. 修改
4. 删除
5. 视图的作用
-
简单
视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作
每次指定全部的条件。 -
安全
数据库可以授权,但不能授权到数据库特定行和特定的列上。通过视图用户只能查询和修改他们所能见到的数据
-
数据独立
视图可帮助用户屏蔽真实表结构变化带来的影响。
6. 案例
案例1:为了保证数据库表的安全性,开发人员在操作tb_user表时,只能看到的用户的基本字段,屏蔽手机号和邮箱两个字段。
create view tb_user_view as select id,name, profession, age, gender,status, createtime from tb_user;
案例2:查询每个学生所选修的课程(三张表联查),这个功能在很多的业务中都有使用到,为了简化操作,定义一个视图。
十三、存储过程
特点:1. 封装、复用; 2. 可以接收参数,也可以返回数据;3. 减少网络交互,效率提升
1. 创建与调用
- 创建
创建之后会出现routines目录
- 调用
2. 查看存储过程
3. 删除存储过程
4. 变量
- 系统变量
系统变量是MySQL服务器提供,不是用户定义的,属于服务器层面。分为全局变量(GLOBAL)、会话变量 (SESSION)。
全局变量对所有会话都有效,但是mysql服务器重启后,所设置的全局参数会失效,如果想不失效,可以在/etc/my.cnf中配置。
会话变量仅对当前会话有效。
查看系统变量
查看所有系统变量,不写SESSION或GLOBAL时,默认为SESSION
SHOW [SESSION | GLOBAL] VARIABLES;
可以通过LKE模糊匹配方式查找变量
SHOW [SESSION | GLOBAL] VARIABLES LIKE '%......%'
查看指定变量的值
SELECT @@[SESSION | GLOBAL].系统变量名;
设置系统变量
SET [SESSION | GLOBAL〕 系统变量名=值;
SET @@[SESSION | GLOBAL]系统变量名=值:
- 用户定义变量
用户定义变量是用户根据需要自己定义的变量,用户变量不用提前声明,在用的时候直接用"@变量名"使用就可以。其作用域为当前连接(当前会话)。用户定义的变量无需对其进行声明或初始化,只不过获取到的值为NULL。
赋值
SET @var_name = expr [,@var_name = expr]...;
SET @var_name := expr [,@var_name := expr]...;
SELECT @var_name := expr [,@var_name := expr]...;
SELECT 字段名 INTO @var_name FROM 表名;
使用
SELECT @var_name
实例
- 局部变量
局部变量是根据需要定义的在局部生效的变量,访问之前,需要DECLARE声明。可用作存储过程内的局部变量和输入参数,局部变量的范围是在其内声明的BEGIN…END块。
声明
DECLARE 变量名 变量类型 [DEFAULT...];
变量类型就是数据库字段类型:INT、BIGINT、CHAR、 VARCHAR、 DATE、 TIME等。
赋值
SET 变量名=值:
SET 变量名:=值
SELECT 字段名 INTO 变量名 FROM 表名…;
实例
5. if
IF 条件1 THEN
...
ELSEIF 条件2 THEN -- 可选
...
ELSE -- 可选
...
END IF;
实例
6. 参数
用法
CREATE PROCEDURE 存储过程名称([IN/OUT/INOUT 参数名 参数类型])
BEGIN
SQL语句
END
实例
定义存储过程:
调用存储过程:
定义一个用户定义变量result,使用result来接收存储过程p4返回的值
输出:
7. case
语法1
CASE case_valueWHEN when_valuel THEN statement_listl[WHEN when_value2 THEN statement_list 2]...[ELSE statement_list]
END CASE;
语法2
CASEWHEN search_conditionl THEN statement_listl[WHEN search_condition2 THEN statement_list2]...[ELSE statement_list]
END CASE;
实例
8. 循环
- while
while 循环是有条件的循环控制语句。满足条件后,再执行循环体中的SQL语句。具体语法为:
-- 先判定条件,如果条件为true,则执行逻辑,否则,不执行逻辑
WHILE 条件 DOSQL逻辑...
END WHILE;
while实例
- repeat
repeat是有条件的循环控制语句,当满足条件的时候退出循环。具体语法为:
-- 先执行一次逻辑,然后判定逻辑是否满足,如果满足,则退出。如果不满足,则继续下一次循环
REPEATSQL逻辑…UNTIL 条件
END REPEAT;
repeat实例
- loop
LOOP实现简单的循环,如果不在SQL逻辑中增加退出循环的条件,可以用其来实现简单的死循环。LOOP可以配合以下两个语句使用:
LEAVE:配合循环使用,退出循环。
ITERATE:必须用在循环中,作用是跳过当前循环剩下的语句,直接进入下一次循环。
语法
[begin_label:] LOOPSQL逻辑…
END LOOP [end_label];
ITERATE label;直接进入下一次循环
实例
-
计算从1累加到n的值,n为传入的参数值。
-
计算从1到n之间的偶数累加的值,n为传入的参数值。
9. 条件处理程序
条件处理程序(Handler)可以用来定义在流程控制结构执行过程中遇到问题时相应的处理步骤。具体语法为:
10. 游标(光标)
游标(CURSOR)是用来存储查询结果集的数据类型,在存储过程和函数中可以使用游标对结果集进行循环的处理。游标的使用包括游标的声明、OPEN、 FETCH 和 CLOSE,其语法分别如下:
-
声明游标
DECLARE 游标名称 CURSOR FOR 查询语句;
-
打开游标
OPEN 游标名称;
-
获取游标记录
FETCH 游标名称 INTO 变量[变量];
-
关闭游标
CLOSE 游标名称;
实例
需求:根据传入的参数uage,来查询用户表tb_user中,所有的用户年龄小于等于uage的用户姓名(name)和专业(profession),并将用户的姓名和专业插入到所创建的一张新表(id,name,profession)中。
sql:
十四、存储函数
存储函数是有返回值的存储过程,存储函数的参数只能是IN类型的。具体语法如下:
实例
需求:计算从1累加到n的值,n为传入的参数值。
sql:
十五、触发器
触发器是与表有关的数据库对象,指在 insert/update/delete 之前或之后,触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性,日志记录,数据校验等操作。
使用别名 OLD 和 NEW 来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还只支持行级触发,不支持语句级触发。
行级触发器:一条sql语句执行后影响几行就触发几次。
语句级触发器:执行几条sql语句就触发几次。
1. 语法
-
创建
CREATE TRIGGER trigger name BEFORE/AFTER INSERT/UPDATE/DELETE ON tb_name FOR EACH ROW -- 行级触发器 BEGINtrigger_stmt; END;
-
查看
SHOW TRIGGERS;
-
删除
DROP TRIGGER [schema_name.]trigger_name; -- 如果没有指定schema_name,默认为当前数据库
2. 案例
案例1:通过触发器记录 tb_user 表的数据变更日志,将变更日志插入到日志表user_logs中,包含增加,修改,删除。
- 创建user_logs表
- 创建插入数据触发器
- 创建修改数据触发器
- 创建删除数据触发器
- 测试
向tb_user插入一条记录
检查user_logs是否有数据
十六、 锁
1. 介绍
锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、RAM、 I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
- 分类
MySQL中的锁,按照锁的粒度分,分为以下三类:
1、全局锁:锁定数据库中的所有表。
2、表级锁:每次操作锁住整张表。
3、行级锁:每次操作锁住对应的行数据。
2. 全局锁
全局锁就是对整个数据库实例(Mysql中的所有databases的所有tables)加锁,加锁后整个实例就处于只读状态,后续的DML 语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。
其典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。
- 语法
添加全局锁
flush tables with read lock;
解锁
unlock tables;
- 特点
数据库中加全局锁,是一个比较重的操作,存在以下问题:
- 如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆。
- 如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟。
在InnoDB引擎中,我们可以在备份时加上参数 --single-transaction 参数来完成不加锁的一致性数据备份。
mysqldump --single-transaction -uroot -p123456 itcast> itcast.sql
3. 表级锁
表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中。
对于表级锁,主要分为以下三类:
1、表锁
2、元数据锁 (meta data lock, MDL)
3、意向锁
- 表锁
对于表锁,分为两类:
1、表共享读锁 (read lock)
加了读锁后,当前会话只能读,不能写,执行写操作时会报错
其他会话只能读,不能写,执行写操作时会阻塞,直到加锁的会话释放锁
2、表独占写锁 (write lock)
写锁既会阻塞其他会话的读,又会阻塞其他会话的写。但是当前客户端既可以写,又可以读。
语法:
1、 加锁:lock tables 表名... read/write
2、释放锁:unlock tables
或客户端断开连接
- 元数据锁(MDL)
MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。为了避免DML与DDL冲突,保证读写的正确性。
在MySQL5.5中引入了MDL,当对一张表进行增删改查的时候,加MDL读;当对表结构进行变更操作的时候,加MDL写锁(排他)。
案例1:
会话1,执行select语句会加SHARED_READ
会话2,执行update语句会加SHARED_WRITE,SHARED_READ和SHARED_WRITE兼容,所以会话2可以在会话1开启事务的情况下执行update语句
案例2:
会话1,执行select语句会加SHARED_READ
会话2,执行alter语句会加EXCLUSIVE,SHARED_READ和EXCLUSIVE互斥,所以会话2在执行alter语句时会发生阻塞。
案例3:
会话1,查看元数据锁
会话2,开启事务,并执行select语句
会话1,查看元数据锁
- 意向锁
为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。
意向锁分为2类:
1、意向共享锁(IS):由语句 select ... lock in share mode
添加。与表锁共享锁(read))兼容,与表锁排它锁(write)互斥。
2、意向排他锁(IX):由insert、 update、 delete、 select.... for update
添加。与表锁共享锁(read)及排它锁(write)都互斥。意向锁之间不会互斥。
查看意向锁及行锁的加锁情况:
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;
案例1:
会话1,开启事务并执行select语句
会话2,查看意向锁
4. 行级锁
行级锁,每次操作只锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。
InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类:
1、 行锁(Record Lock):锁定单个行记录的锁,防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持。
2、 间隙锁(Gap Lock):锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持。
3、 临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。
- 行锁
InnoDB实现了一下两种类型的行锁:
1、共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁。
2、排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。
- 行锁案例演示
默认情况下,InnoDB在 REPEATABLE READ事务隔离级别运行,InnoDB使用 next-key 锁进行搜索和索引扫描,以防止幻读。
1、 针对唯一索引进行检索时,对已存在的记录进行等值匹配时,将会自动优化为行锁。
会话1:id为主键。开启事务,并添加共享锁,会将共享锁优化为行锁,只锁id=1的这一条数据
会话2:
2、 InnoDB的行锁是针对于索引加的锁,不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁。
会话1:
会话2:
会话1:为name列添加索引
会话2:
- 间隙锁/临键锁案例演示
默认情况下,InnoDB在 REPEATABLE READ事务隔离级别运行,InnoDB使用 next-key 锁进行搜索和索引扫描,以防止幻读。
1、 索引上的等值查询(唯一索引),给不存在的记录加锁时,优化为间隙锁。
会话1:
会话2:查看行锁
会话2:在主键3和8之间插入数据时会阻塞,直到会话1将事务提交
2、 索引上的等值查询(普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock 退化为间隙锁。
会话1:
会话2:
3、 索引上的范围查询(唯一索引)–会访问到不满足条件的第一个值为止。
会话1:
会话2:
注意:间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。
十七、事务
https://javaguide.cn/database/mysql/mysql-questions-01.html#何谓事务
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
MySQL在执行多个语句的过程中,遇到异常时会停止执行。默认MySQL的事务是自动提交的,也就是说,当执行一条DML语句(每一条DML都看做是一个事务),MySQL会立即隐式的提交事务。DDL语句不支持事务。
1. 事务操作
方式1:
-
查看/设置事务提交方式为手动提交
SELECT @@autocommit; SET @@autocommit=0; -- 0表示需要手动提交事务
-
提交事务
commit;
-
回滚事务
rollback;
方式2:
-
开启事务
start transaction; -- 或 begin;
-
提交事务
commit;
-
回滚事务
rollback;
2. 事务四大特性ACID
原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
3. 并发事务问题
-
脏读
一个事务读到另外一个事务还没有提交的数据。
-
不可重复读
一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。
-
幻读
一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了“幻影”。
4. 并发事务演示及隔离级别
- 事务的隔离级别
从上到下级别越来越高,数据安全性越来越高,但是性能越来越低。一般使用mysql默认的隔离级别,不会进行修改。
-
查看事务的隔离级别
SELECT @@TRANSACTION_ISOLATION;
-
设置事务隔离级别
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE};
-
脏读案例演示
- 设置隔离级别
READ UNCOMMITTED
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-
打开两个mysql会话
会话1做如下操作:
会话2做如下操作:
会话1做如下操作:
- 设置隔离级别
-
不可重复读案例演示
-
设置隔离级别
READ COMMITTED
-
解决脏读问题
会话1执行如下操作:
会话2执行如下操作:
会话1执行如下操作:
会话2执行如下操作:
会话1执行如下操作:
结论:设置隔离级别为
READ COMMITTED
时,可以避免脏读。 -
会发生不可重复读的问题
会话1执行如下操作:
会话2执行如下操作:
会话1执行如下操作:
结论:会话1在同一个事务中,读取两次account表,但是两次读取的内容不一致,出现了不可重复读的问题。
-
-
幻读案例演示
-
设置事务隔离级别为
REPEATABLE READ
-
解决不可重复读问题
会话1:
会话2:
此时张三的money应该为3000
会话1:
结论:解决了不可重复读问题
-
会出现幻读问题
会话1:
会话2:
会话1:
-
-
解决幻读案例演示
-
设置事务隔离级别为
SERIALIZABLE
-
解决幻读问题
会话1:会话1先开启事务
会话2:会话2开启事务的时间晚于会话1
会话1:
会话2:
-
5. 事务原理
- 持久性实现原理
重做日志(redo log)记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。
该日志文件由两部分组成:重做日志缓冲 (redo log buffer)以及重做日志文件(redo log file),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都存到该日志文件中,用于在刷新脏页到磁盘,发生错误时,进行数据恢复使用。
redo log的作用:
-
保证事务的持久性
如果buffer pool缓冲池中的脏页【脏数据】还没有进行刷盘的时候,此时数据库发生crash,重启服务后,我们可以通过redo log日志找到需要重放到磁盘文件的那些数据记录。
-
提高事务提交的速度
buffer pool缓冲池中的数据直接刷新到磁盘,是一个随机IO,效率较差,而把buffer pool中的数据记录到redo log,是一个顺序IO,可以提高事务提交的速度。
- 原子性实现原理
回滚日志(undo log)用于记录数据被修改前的信息,作用包含两个:提供回滚 和MVCC(多版本并发控制),undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。
undo log销毁:undo log在事务执行时产生,事务提交时,并不会立即州除undo log,因为这些日志可能还用于MVCC。
undo log存储:undo log采用段的方式进行管理和记录,存放在前面介绍的rolback segment 回滚段中,内部包含1024个undo log segment。
- 隔离性实现原理
MVC+锁
- 一致性实现原理
undolog和redolog共同保证了一致性。
原子性、持久性、隔离性同时满足时,一致性肯定也会满足。
十八、InnoDB存储引擎
1. 逻辑存储结构
2. 架构
MySQL5.5 版本开始,默认使用InnoDB存储引擎,它擅长事务处理,具有崩溃恢复特性,在日常开发中使用非常广泛。下面是InnoDB架构图,左侧为內存结构,
右侧为磁盘结构。
3. 内存结构
- 内存结构-BufferPool
- 内存结构-ChangeBuffer
- 内存结构-自适应hash索引
查看是否开启自适应hash索引:
- 内存结构-LogBuffer
4. 磁盘结构
5. 后台线程
十九、MVCC
1. 当前读
读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:select… lock in share mode(共享锁),select… for update、update、 insert、 delete(排他锁)都是一种当前读。
案例演示
开启两个会话,会话1执行如下操作:
会话2执行如下操作:
会话1:
2. 快照读
简单的select(正常的select语句:select * from user;
)是快照读,快照读读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。
事务隔离级别为Read Committed时,每次执行select,都生成一个快照(ReadView)。
事务隔离级别为Repeatable Read时,开启事务后,执行第一个select语句后,会将查询的数据打一个快照(ReadView),之后每次执行普通select语句,读取的都是快照中的数据。
事务隔离级别为Serializable时,快照读会退化为当前读。
3. MVCC
全称 Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为MySQL实现MVCC提供了一个非阻塞读功能。MVCC的具体实现,需要依赖于数据库记录中的三个隐式字段、undo log日志、readView。
- 三个隐藏字段
创建一个表后,mysql会为这个表生成两个或三个隐藏字段列。
比如创建stu表,声明了三个字段:id、age、name,其中id为主键,此外mysql还会为stu表生成隐藏字段:DB_TRX_ID、DB_ROLL_PTR。
验证是否会生成隐藏字段
在磁盘中找到stu表
使用ibd2sdi
命令查看stu表结构
- undo log日志
回滚日志,在insert、 update、delete的时候产生的便于数据回滚的日志。
当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除。而update、delete的时候,产生的undo log日志不仅在回滚时需要,在快照读时也需要,不会立即被删除。
undo log版本链
不同事务或相同事务对同一条记录进行修改,会导致该记录的undolog生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。
- readview
ReadView(读视图)是快照读 SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
ReadView中包含了四个核心字段:
快照读读取undo log版本链的匹配规则如下:
其中trx_id就是db_trx_id
不同的隔离级别,生成ReadView的时机不同:
READ COMMITTED:在事务中每一次执行快照读时生成Readview。
REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。
二十、常用工具
- mysql
- mysqladmin
- mysqlbinlog
进入到binlog所在的目录,然后执行:
- mysqlshow
- mysqldump
- mysqlimport/source
常见问题
MySQL表中创建时间字段和更新时间字段的自动更新
来源博客:https://blog.csdn.net/ChineseSoftware/article/details/122879108
需求:
当新增记录的时候,MySQL自动将系统的当前时间 set 到创建时间和更新时间这两个字段中。
当更新记录的时候,MySQL 只 update 更新时间字段的时间,而不修改创建时间字段对应的值。
解决方案:
- 创建时间字段
creat_time timestamp NULL DEFAULT CURRENT_TIMESTAMP
- 更新时间字段
update_time timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
或
在 navicat 中的操作:找到相应的表—右击—>点击“设计表”—>如下图:
- 插入记录时自动生成创建时间:
-
更新记录时自动生成修改时间:
insert、replace、insert ignore、on duplicate key update区别
四条插入语句
-
insert into表示插入数据,数据库会检查主键,如果出现重复会报错;
-
replace into表示插入替换数据,需求表中有PrimaryKey,或者unique索引,如果数据库已经存在数据,删除旧数据,插入新数据,如果没有数据效果则和insert into一样;
-
insert ignore表示,如果中已经存在相同的记录,则忽略当前新数据;
-
ON DUPLICATE KEY UPDATE 关注非主键列,注意与ignore的区别。有则更新指定列,无则添加。
使用场景
如果发生约束建重复时:
-
要捕获异常,则选用insert into
-
想要删除老的数据,插入新数据,则选用 replace into
-
想要保存老数据,忽略新数据,则选用 insert ignore into
-
想要更新老数据中的某几列,其他列不变化,则选用 ON DUPLICATE KEY UPDATE
新建 MySQL 账户、修改用户密码时报错
Column count of mysql.user is wrong. Expected 45, found 43. Created with MySQL 5
该错误是由于你曾经升级过数据库,升级完后没有使用mysql_upgrade升级数据结构造成的。
**解决办法:**使用mysql_upgrade
命令
mysql_upgrade -uroot -p
linux中mysql忘记密码(重置密码)
1.修改mysql的配置文件/etc/my.cnf,添加如下内容:
[mysqld]
skip-grant-tables #登录mysql的时候跳过密码验证
2.重启mysql服务
systemctl restart mysqld.service
3.使用root登陆mysql,无需输入密码
mysql -uroot
4.修改密码
update mysql.user set authentication_string=password('new_root_password') where user='root' and host='localhost';
flush privileges;
禁止mysql匿名登录
大多数版本的mysql数据库默认会常见匿名用户,一般情况下,匿名用户的名字为空,host为localhost,authentication_string为空,只有登录mysql的权限,没有其他任何权限。在本机上运行mysql -u任意不存在mysql.user表中的用户名
命令即表示使用匿名用户登录。
1.查看匿名用户
SELECT host,user,authentication_string FROM mysql.user WHERE user =‘’;
该SQL语句会显示出MySQL数据库中所有的匿名用户信息
2.删除匿名用户
DELETE FROM mysql.user WHERE user ='';
这句SQL语句会删除MySQL数据库中所有的匿名用户。在执行完这句SQL语句之后,你还需要执行下面的SQL语句来使刚刚所做的更改生效
3.刷新
FLUSH PRIVILEGES;
执行完上述SQL语句以后,就可以查看一下MySQL数据库中是否还存在匿名用户,如果不存在就说明删除成功了。
查看mysql初始化密码
查看mysql初始化密码:
cat /var/log/mysqld.log | grep 'temporary password'
使用初始化密码登录后必须要先使用alter user语句来更新密码。
ERROR 1366(HY000)报错
运行sql语句,报错像这样的ERROR 1366(HY000):Incorrect string value:XXX一堆乱码:
1.查看表的编码
show create table 表名;
2.查看表中所有列的编码
show full columns from 表名;
修改表和列的编码和sql语句的编码相同即可。
修改方式:
修改表字符集和排序规则
ALTER TABLE 表名 [DEFAULT CHARACTER SET '字符集'] [COLLATE '排序规则'];
修改表指定列的字符集
ALTER TABLE 表名 MODIFY COLUMN 列名 数据类型 [CHARACTER SET '字符集'] [COLLATE '排序规则'];#实例
ALTER TABLE t1 MODIFY COLUMN name VARCHAR(10) CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_0900_ai_ci';
[注]:
- 在执行命令时,如果表格中已存在数据,修改字符集可能会导致某些数据无法正确存储。因此,在修改字符集之前,先备份好数据,以便于后续恢复数据。
参考:
https://blog.csdn.net/qq_36411874/article/details/63251141