MySQL是C/S 架构的,connectors是连接器;可供Native C API、JDBC、ODBC、NET、PHP、Perl、Python、Ruby、Cobol等连接mysql;ODBC叫开放数据库(系统)互联,open database
connection;每种语言都有各自的连接数据库的API接口
JDBC是主要用于java语言利用较为底层的驱动连接数据库;以上这些,站在编程角度可以理解为连入数据库管理系统的驱动,站在mysql角度称作专用语言对应的链接器.
任何链接器连入mysql以后,mysql是单进程多线程模型的,因此,每个用户连接,都会创建一个单独的连接线程;其实mysql连接也有长短连接两种方式,使用mysql客户端连入数据库后,直到使用quit命令才退出,可认为是长连接;使用mysql中的-e选项,在mysql客户端向服务器端申请运行一个命令后则立即退出,也就意味着连接会立即断开;所以,mysql也支持长短连接类似于两种类型;所以,用户连入mysql后,创建一个连接线程,完成之后,能够通过这个链接线程完成接收客户端发来的请求,为其处理请求,构建响应报文并发给客户端;由于是单进程模型,就意味着必须要维持一个线程池,跟之前介绍过的varnish很接近,需要一个线程池来管理这众多线程是如何对众多客户端的并发请求,完成并发响应的,组件connection pool就是实现这样功能;connection pool对于mysql而言,它所实现的功能,包括authentication认证,用户发来的账号密码是否正确要完成认证功能;thread reuse线程重用功能,一般当一个用户连接进来以后要用一个线程来响应它,而后当用户退出这个线程有可能并非被销毁,而是把它清理完以后,重新收归到线程池当中的空闲线程中去,以完成所谓的线程重用;
connection limit 线程池的大小决定了连接并发数量的上限,例如,最多容纳100线程,一旦到达此上限后续到达的连接请求则只能排队或拒绝连接;check memory用来检测内存,caches实现线程缓存;
整个都属于线程池的功能.当用户请求之后,通过线程池建立一个用户连接,这个线程一直存在,然后用户就通过这个会话,发送对应的SQL语句到服务器端.
服务器收到SQL语句后,要对语句完成执行,首先要能理解sql语句需要有sql解释器或叫sql接口,sql interface就可理解为是整个mysql的外壳,就像shell是linux操作系统的外壳一样道理;用户无论通过哪种链接器发来的基本的SQL请求,当然,事实上通过native C API也有发过来的不是SQL 请求,而仅仅是对API中的传递参数后的调用;不是SQL语句不过都统统理解为sql语句罢了;对SQL而言分为DDL和DML两种类型,但是无论哪种类型,提交以后必须交给内核,让内核来运行,在这之前必须要告诉内核哪个是命令,哪个是选项,哪些是参数,是否存在语法错误等等;因此,这个整个SQL 接口就是一个完完整整的sql命令的解释器,并且这个sql接口还有提供完整的sql接口应该具备的功能,比如支持所谓过程式编程,支持代码块的实现像存储过程、存储函数,触发器、必要时还要实现部署一个关系型数据库应该具备的基本组件例如视图等等,其实都在sql interface这个接口实现的;SQL接口做完词法分析、句法分析后,要分析语句如何执行让parser解析器或分析器实现,parser是专门的分析器,这个分析器并不是分析语法问题的,语法问题在sql接口时就能发现是否有错误了,一个语句没有问题,就要做执行分析,所谓叫查询翻译,把一个查询语句给它转换成对应的能够在本地执行的特定操作;比如说看上去是语句而背后可能是执行的一段二进制指令,这个时候就完成对应的指令,还要根据用户请求的对象,比如某一字段查询内容是否有对应数据的访问权限,或叫对象访问权限;在数据库中库、表、字段、字段中的数据有时都称为object,叫一个数据库的对象,用户认证的通过,并不意味着就能一定能访问数据库上的所有数据,所以说,mysql的认证大概分为两过程都要完成,第一是连入时需要认证账号密码是否正确这是authentication,然后,验证成功后用户发来sql语句还要验证用户是否有权限获取它期望请求获取的数据;这个称为object privilege,这一切都是有parser分析器进行的
分析器分析完成之后,可能会生成多个执行树,也就意味着为了能够达到访问期望访问到的目的,可能有多条路径都可实现,就像文件系统一样可以使用相对路径也可使用绝对路径;它有多种方式,在多种路径当中一定有一个是最优的,类似路由选择,因此,optimizer就要去衡量多个访问路径中哪一个代价或开销是最小的,这个开销的计算要依赖于索引等各种内部组件来进行评估;而且这个评估的只是近似值,同时还要考虑到当前mysql内部在实现资源访问时统计数据,比如,根据判断认为是1号路径的开销最小的,但是众多统计数据表明发往1号路径的访问的资源开销并不小,并且比3号路径大的多,因此,可能会依据3号路径访问;这就是所谓的optimizer它负责检查多条路径,每条路径的开销,然后评估开销,这个评估根据内部的静态数据,索引,根域根据动态生成的统计数据来判定每条路径的开销大小,因此这里还有statics;一旦优化完成之后,还要生成统计数据,这就是优化器的作用;如果没有优化器mysql执行语句是最慢的,其实优化还包括一种功能,一旦选择完一条路径后,例如用户给的这个命令执行起来,大概需要100个开销,如果通过改写语句能够达到同样目的可能只需要30个开销;于是,optimizer还要试图改写sql语句;所以优化本身还包括查询语句的改写;一旦优化完成,接下来就交给存储引擎完成.
mysql是插件式存储引擎,它就能够替换使用选择多种不同的引擎,MyISAM是MySQL 经典的存储引擎之一,InnoDB是innobase提供给MySQL的,NDB主要用于MySQL Cluster 分布式集群环境,archive做归档的等等,还有许多第三方开发的存储引擎;存储引擎负责把具体分析的结果完成对磁盘上文件路径访问的转换,数据库中的行数据都是存储在磁盘块上的,因此存储引擎要把数据库数据映射为磁盘块,并把磁盘块加载至内存中;进程实现数据处理时,是不可能直接访问磁盘上的数据的,因为它没有权限,只有让内核来把它所访问的数据加载至内存中以后,进程在内存中完成修改,由内核再负责把数据存回磁盘;对于文件系统而言,数据的存储都是以磁盘块方式存储的,但是,mysql在实现数据组织时,不完全依赖于磁盘,而是把磁盘块再次组织成更大一级的逻辑单位,类似于lvm中的PE或LE的形式;其实,MySQL的存储引擎在实现数据管理时,也是在文件系统之上布设文件格式,对于文件而言在逻辑层上还会再次组织成一个逻辑单位,这个逻辑单位称为mysql的数据块datablock 一般为16k ,对于关系型数据库,数据是按行存储的;一般一行数据都是存储在一起的,因此,MySQL 在内部有一个数据块datablock,在datablock就存储一行数据,一个数据块里可能存放了n行数据;将来在查询加载一行数据时,内核会把整个一个数据数据块加载至内存中,而mysql存储引擎,就从中挑出来某一行返回给查询者,是这样实现的;所以整个存储是以datablock在底层为其最终级别的.
事实上,整个存取过程,尤其是访问比较热点的数据,也不可能每一次当用户访问时或当某SQL语句用到时再临时从磁盘加载到内存中,因此,为了能够加上整个性能,mysql的有些存储引擎可以实现,把频繁访问到的热点数据,统统装入内存,用户访问、修改时直接在内存中操作,只不过周期性的写入磁盘上而已,比如像InnoDB,所以caches和buffers组件就是实现此功能的;MySQL为了执行加速,因为它会不断访问数据,而随计算机来说io是最慢的一环,尤其是磁盘io,所以为了加速都载入内存中管理;这就需要MySQL 维护cache和buffer缓存或缓冲;这是由MySQL 服务器自己维护的;有很多存储引擎自己也有cache和buffer;一个数据库提供了3种视图,物理视图就是看到的对应的文件系统存储为一个个的文件,MySQL的数据文件类型,常见的有redo log重做日志,undo log撤销日志,data是真正的数据文件,index是索引文件,binary log是二进制日志文件,error log错误日志,query log查询日志,slow query log慢查询日志,在复制架构中还存在中继日志文件,跟二进制属于同种格式;这是mysql数据文件类型,也就是物理视图;逻辑视图这是在mysql接口上通过存储引擎把mysql文件尤其是data文件,给它映射为一个个关系型数据库应该具备组成部分,比如表,一张表在底层是一个数据文件而已,里面组织的就是datablock,最终映射为磁盘上文件系统的block,然后再次映射为本地扇区的存储,但是整个mysql需要把他们映射成一个二维关系表的形式,需要依赖sql接口以及存储引擎共同实现;所以,把底层数据文件映射成关系型数据库的组件就是逻辑视图;DBA 就是关注内部组件是如何运作的,并且定义、配置其运作模式,而链接器都是终端用户通过链接器的模式进入数据库来访问数据;数据集可能非常大,每一类用户可能只有一部分数据的访问权限,这个时候,最终的终端用户所能访问到的数据集合称作用户视图;为了保证MySQL运作还提供了管理和服务工具,例如:备份恢复工具,安全工具,复制工具,集群服务,管理、配置、迁移、元数据等工具
Feature | MyISAM | InnoDB | Memory | Archive | NDB |
---|---|---|---|---|---|
Storage limits | 256TB | 64TB | RAM | None | 384EB |
Transactions事务 | No | Yes | No | No | Yes |
Locking granularity 锁粒度 | Table表级锁 | Row行级锁 | Table | Row | Row |
MVCC多版本并发控制机制 | No | Yes | No | No | No |
Clustered indexes聚簇索引 | No | Yes | No | No | No |
Foreign key support外键支持 | No | Yes | No | No | Yes |
Data caches数据缓存 | No | Yes | N/A | No | Yes |
Geospatial data type support | Yes | Yes | No | Yes | Yes |
Geospatial indexing support | Yes | Yes | No | No | No |
B-tree indexes | Yes | Yes | Yes | No | No |
T-tree indexes | No | No | No | No | Yes |
Hash indexes | No | No | Yes | No | Yes |
Index caches | Yes | Yes | N/A | No | Yes |
Cluster database support | No | No | No | No | Yes |
Compressed data | Yes | Yes | No | Yes | No |
Encrypted data | Yes | Yes | Yes | Yes | Yes |
Full-text search indexes | Yes | Yes | No | No | No |
Backup/point-in-time recovery | Yes | Yes | Yes | Yes | Yes |
Replication support | Yes | Yes | Limited | Yes | Yes |
Update statistics for data dictionary | Yes | Yes | Yes | Yes | Yes |
MySQL中的数据用各种不同的技术存储在文件(或者内存)中。这些技术中的每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的不同的功能和能力,此种技术称为存储擎,MySQL 支持多种存储引擎,其中目前应用最广泛的是InnoDB和MyISAM两种
官方参考资料:https://docs.oracle.com/cd/E17952_01/mysql-5.7-en/storage-engines.html
MyISAM引擎特点
MyISAM存储引擎适用场景
MyISAM引擎文件
InnoDB引擎特点
MVCC说明:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KHOq7ION-1592135370679)(C:\Users\cui\AppData\Roaming\Typora\typora-user-images\image-20200609092801783.png)]
InnoDB数据库文件
大概流程:一行里有若干域,一页(16K)里有若干行,多个页放到extent盘区里,extent合并成一个段,最后段放在表空间中;其中页是存放数据的最小空间
数据文件:ibdata1, ibdata2,存放在datadir定义的目录下; #centos6默认在/var/lib/mysql/
表格式定义:tb_name.frm,存放在datadir定义的每个数据库对应的目录下
两类文件放在对应每个数据库独立目录中
数据文件(存储数据和索引):tb_name.ibd
表格式定义:tb_name.frm
启用:innodb_file_per_table=ON (MariaDB 5.5以后版是默认值)
参看:https://mariadb.com/kb/en/library/xtradbinnodb-server-system-variables/#innodb_file_per_table
查看mysql支持的存储引擎
show engines;
查看当前默认的存储引擎
show variables like '%storage_engine%';
设置默认的存储引擎
vim /etc/my.conf
[mysqld]
default_storage_engine= InnoDB
查看库中所有表使用的存储引擎
show table status from db_name;
查看库中指定表的存储引擎
show table status like 'tb_name';
show create table tb_name;
设置表的存储引擎:
CREATE TABLE tb_name(... ) ENGINE=InnoDB;
ALTER TABLE tb_name ENGINE=InnoDB;
可以通过mysqld选项,服务器系统变量和服务器状态变量进行MySQL的配置和查看状态
官方帮助:
https://dev.mysql.com/doc/refman/5.7/en/server-option-variable-reference.html
https://mariadb.com/kb/en/library/full-list-of-mariadb-options-system-and-status-variables/
说明:
Cmd-Line和Option File 都为yes时就是服务器选项
System Var 为yes时就是服务器变量
Dynamic 为 No 时代表变量为只读,只能看不能改
注意:
获取mysqld的可用选项列表:
#查看mysqld可用选项列表和及当前值
mysqld --verbose --help
#获取mysqld当前启动选项
mysqld --print-defaults
范例:
#查看可用选项列表和当前值
[root@centos8 ~]#/usr/libexec/mysqld --verbose --help
#查看mysqld的当前启动选项
[root@centos8 ~]#/usr/libexec/mysqld --print-defaults
/usr/libexec/mysqld would have been started with the following arguments:
--plugin-load-add=auth_gssapi.so --datadir=/var/lib/mysql --
socket=/var/lib/mysql/mysql.sock --log-error=/var/log/mariadb/mariadb.log --pidfile=/run/mariadb/mariadb.pid
设置服务器选项方法:
shell> /usr/bin/mysqld_safe --skip-name-resolve=1
shell> /usr/libexec/mysqld --basedir=/usr
范例:
vim /etc/my.cnf
[mysqld]
skip_name_resolve=1
skip-grant-tables
范例: skip-grant-tables是服务器选项,但不是系统变量
[root@centos8 ~]#mysqladmin variables |grep skip_grant_tables
[root@centos8 ~]#mysql
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 12
Server version: 10.3.17-MariaDB MariaDB Server
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> show variables like 'skip_grant_tables';
Empty set (0.001 sec)
服务器系统变量:可以分全局和会话两种
获取系统变量
SHOW GLOBAL VARIABLES; #只查看global变量
SHOW [SESSION] VARIABLES; #查看所有变量(包括global和session)
#查看指定的系统变量
SHOW VARIABLES LIKE 'VAR_NAME';
SELECT @@VAR_NAME;
#查看选项和部分变量
[root@centos8 ~]#mysqladmin variables
修改服务器变量的值:
help SET
修改全局变量:仅对修改后新创建的会话有效;对已经建立的会话无效
SET GLOBAL system_var_name=value;
SET @@global.system_var_name=value;
修改会话变量:
SET [SESSION] system_var_name=value;
SET @@[session.]system_var_name=value;
范例: character_set_results是系统变量并非服务器选项
[root@centos8 ~]#mysql
MariaDB [(none)]> show variables like 'character_set_results';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| character_set_results | utf8 |
+-----------------------+-------+
1 row in set (0.001 sec)
MariaDB [(none)]> set character_set_results="utf8mb4";
Query OK, 0 rows affected (0.000 sec)
MariaDB [(none)]> show variables like 'character_set_results';
+-----------------------+---------+
| Variable_name | Value |
+-----------------------+---------+
| character_set_results | utf8mb4 |
+-----------------------+---------+
1 row in set (0.001 sec)
[root@centos8 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
character_set_results=utf8mb4
#character_set_results不是服务器选项,写入配置文件将导致无法启动
[root@centos8 ~]#systemctl restart mariadb
Job for mariadb.service failed because the control process exited with error code.
See "systemctl status mariadb.service" and "journalctl -xe" for details.
范例:修改mysql的最大并发连接数
#默认值为151
[root@centos8 ~]#mysqladmin variables |grep '\
| max_connections | 151
[root@centos8 ~]#mysql
MariaDB [(none)]> show variables like 'max_connections';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 151 |
+-----------------+-------+
1 row in set (0.001 sec)
MariaDB [hellodb]> set global max_connections=2000;
Query OK, 0 rows affected (0.000 sec)
MariaDB [hellodb]> show variables like 'max_connections';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 2000 |
+-----------------+-------+
1 row in set (0.001 sec)
[root@centos8 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
max_connections = 8000
[root@centos8 ~]#systemctl restart mariadb
[root@centos8 ~]#mysql -uroot -p
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 9
Server version: 10.3.11-MariaDB-log MariaDB Server
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> select @@max_connections;
+-------------------+
| @@max_connections |
+-------------------+
| 594 |
+-------------------+
1 row in set (0.000 sec)
#方法1
[root@centos8 ~]#vim /usr/lib/systemd/system/mariadb.service
[Service]
#加下面一行
LimitNOFILE=65535
#方法2
[root@centos8 ~]#mkdir /etc/systemd/system/mariadb.service.d/
[root@node3 ~]#vim /etc/systemd/system/mariadb.service.d/limits.conf
[Service]
LimitNOFILE=65535
[root@centos8 ~]#systemctl daemon-reload
[root@centos8 ~]#systemctl restart mariadb
[root@centos8 ~]#mysql -uroot -p -e "select @@max_connections"
Enter password:
+-------------------+
| @@max_connections |
+-------------------+
| 8000 |
+-------------------+
范例:修改页大小
参看:https://mariadb.com/kb/en/innodb-system-variables/#innodb_page_size
说明:初始化数据目录后,不能更改此系统变量的值。 在MariaDB实例启动时设置InnoDB的页面大小,此后保持不变。
[root@centos8 ~]#mysqladmin variables |grep innodb_page_size
| innodb_page_size | 16384
[root@centos8 ~]#mysql
MariaDB [(none)]> show variables like "innodb_page_size";
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| innodb_page_size | 16384 |
+------------------+-------+
1 row in set (0.001 sec)
[root@centos8 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
innodb_page_size=64k
[root@centos8 ~]#rm -rf /var/lib/mysql/*
[root@centos8 ~]#systemctl restart mariadb
[root@centos8 ~]#mysql
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 9
Server version: 10.3.11-MariaDB MariaDB Server
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> show variables like "innodb_page_size";
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| innodb_page_size | 65536 |
+------------------+-------+
1 row in set (0.001 sec)
服务器状态变量:分全局和会话两种
状态变量(只读):用于保存mysqld运行中的统计数据的变量,不可更改,只影响自己的session
SHOW GLOBAL STATUS;
SHOW [SESSION] STATUS;
范例:
MariaDB [(none)]> show status like "innodb_page_size";
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| Innodb_page_size | 16384 |
+------------------+-------+
1 row in set (0.001 sec)
MariaDB [hellodb]> SHOW GLOBAL STATUS like 'com_select';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_select | 5 |
+---------------+-------+
1 row in set (0.001 sec)
SQL_MODE:对其设置可以完成一些约束检查的工作,可分别进行全局的设置或当前会话的设置
参考:
https://mariadb.com/kb/en/library/sql-mode/
https://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_sql-mode
常见MODE:
范例:CentOS 8 修改SQL_MODE变量实现分组语句控制
MariaDB [hellodb]> show variables like 'sql_mode';
+---------------+-------------------------------------------------------------------------------------------+
| Variable_name | Value |
+---------------+-------------------------------------------------------------------------------------------+
| sql_mode | STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+---------------+-------------------------------------------------------------------------------------------+
1 row in set (0.001 sec)
MariaDB [hellodb]> select classid,count(*) from students group by classid;
+---------+----------+
| classid | count(*) |
+---------+----------+
| NULL | 2 |
| 1 | 4 |
| 2 | 3 |
| 3 | 4 |
| 4 | 4 |
| 5 | 1 |
| 6 | 4 |
| 7 | 3 |
+---------+----------+
8 rows in set (0.000 sec)
MariaDB [hellodb]> select stuid,classid,count(*) from students group by classid;
+-------+---------+----------+
| stuid | classid | count(*) |
+-------+---------+----------+
| 24 | NULL | 2 |
| 2 | 1 | 4 |
| 1 | 2 | 3 |
| 5 | 3 | 4 |
| 4 | 4 | 4 |
| 6 | 5 | 1 |
| 9 | 6 | 4 |
| 8 | 7 | 3 |
+-------+---------+----------+
8 rows in set (0.000 sec)
#修改SQL_MODE
MariaDB [hellodb]> set sql_mode='ONLY_FULL_GROUP_BY';
Query OK, 0 rows affected (0.000 sec)
MariaDB [hellodb]> show variables like 'sql_mode';
+---------------+--------------------+
| Variable_name | Value |
+---------------+--------------------+
| sql_mode | ONLY_FULL_GROUP_BY |
+---------------+--------------------+
1 row in set (0.001 sec)
MariaDB [hellodb]> select classid,count(*) from students group by classid;
+---------+----------+
| classid | count(*) |
+---------+----------+
| NULL | 2 |
| 1 | 4 |
| 2 | 3 |
| 3 | 4 |
| 4 | 4 |
| 5 | 1 |
| 6 | 4 |
| 7 | 3 |
+---------+----------+
8 rows in set (0.001 sec)
MariaDB [hellodb]> select stuid,classid,count(*) from students group by classid;
ERROR 1055 (42000): 'hellodb.students.StuID' isn't in GROUP BY
范例:CentOS 7 修改SQL_MODE变量
MariaDB [hellodb]> create table test (id int,name varchar(3));
Query OK, 0 rows affected (0.008 sec)
MariaDB [hellodb]> insert test values(1,'abcde');
Query OK, 1 row affected, 1 warning (0.003 sec)
MariaDB [hellodb]> show warnings;
+---------+------+-------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------+
| Warning | 1265 | Data truncated for column 'name' at row 1 |
+---------+------+-------------------------------------------+
1 row in set (0.000 sec)
MariaDB [hellodb]> select * from test;
+------+------+
| id | name |
+------+------+
| 1 | abc |
+------+------+
1 row in set (0.000 sec)
MariaDB [hellodb]> show variables like 'SQL_MODE';
+---------------+-----------+
| Variable_name | Value |
+---------------+-----------+
| sql_mode | |
+---------------+-----------+
1 row in set (0.001 sec)
MariaDB [hellodb]> set SQL_MODE=TRADITIONAL;
Query OK, 0 rows affected (0.000 sec)
MariaDB [hellodb]> show variables like 'SQL_MODE';
+---------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| Variable_name | Value |
+---------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| sql_mode | STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+---------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.002 sec)
MariaDB [hellodb]> insert test values(1,'magedu');
ERROR 1406 (22001): Data too long for column 'name' at row 1
查询执行路径
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vEm6Iunl-1592135370682)(C:\Users\cui\AppData\Roaming\Typora\typora-user-images\image-20200609114140913.png)]
查询缓存原理:
缓存SELECT操作或预处理查询的结果集和SQL语句,当有新的SELECT语句或预处理查询语句请求,先去查询缓存,判断是否存在可用的记录集,查询命令会转换成hash值,判断标准:hash值与缓存的SQL语句hash值,是否完全一样,区分大小写
优缺点:
不需要对SQL语句做任何解析和执行,当然语法解析必须通过在先,直接从Query Cache中获得查询结果,提高查询性能
查询缓存的判断规则,不够智能,也即提高了查询缓存的使用门槛,降低效率
查询缓存的使用,会增加检查和清理Query Cache中记录集的开销
哪些查询可能不会被缓存
官方帮助:
https://mariadb.com/kb/en/library/server-system-variables/#query_cache_type
https://dev.mysql.com/doc/refman/5.7/en/query-cache-configuration.html
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x0qvWpV0-1592135370684)(C:\Users\cui\AppData\Roaming\Typora\typora-user-images\image-20200609142827890.png)]
(query_cache_size - Qcache_free_memory) / Qcache_queries_in_cache
#(查询缓存的总容量 - 剩余内存的空间)/已经缓存下来的记录数
Qcache_hits / ( Qcache_hits + Qcache_inserts ) * 100%
(query_cache_size – qcache_free_memory) / query_cache_size * 100%
#(查询缓存的总容量 - 剩余内存的空间)/总的查询缓存空间
#(目前占用的空间)
范例:
[root@centos7 ~]#vim /etc/my.cnf
[mysqld]
query_cache_type=ON
query_cache_size=10M
[root@centos8 ~]#systemctl restart mariadb
[root@centos8 ~]#mysql -uroot -p
MariaDB [(none)]> show variables like 'query_cache%';
+------------------------------+----------+
| Variable_name | Value |
+------------------------------+----------+
| query_cache_limit | 1048576 |
| query_cache_min_res_unit | 4096 |
| query_cache_size | 10485760 |
| query_cache_strip_comments | OFF |
| query_cache_type | ON |
| query_cache_wlock_invalidate | OFF |
+------------------------------+----------+
6 rows in set (0.001 sec)
MariaDB [hellodb]> show GLOBAL STATUS like 'Qcache%';
+-------------------------+---------+
| Variable_name | Value |
+-------------------------+---------+
| Qcache_free_blocks | 1 |
| Qcache_free_memory | 1027736 |
| Qcache_hits | 4 |
| Qcache_inserts | 3 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 0 |
| Qcache_queries_in_cache | 3 |
| Qcache_total_blocks | 8 |
+-------------------------+---------+
8 rows in set (0.001 sec)
尽管MySQL Query Cache旨在提高性能,但它存在严重的可伸缩性问题,并且很容易成为严重的瓶颈。
自MySQL 5.6(2013)以来,默认情况下已禁用查询缓存,其不能与多核计算机上在高吞吐量工作负载情况下进行扩展。
另外有时因为查询缓存往往弊大于利。比如:查询缓存的失效非常频繁,只要有对一个表的更新,这个表上的所有的查询缓存都会被清空。因此很可能你费劲地把结果存起来,还没使用呢,就被一个更新全清空了。对于更新压力大的数据库来说,查询缓存的命中率会非常低。除非你的业务有一张静态表,很长时间更新一次,比如系统配置表,那么这张表的查询才适合做查询缓存。
目前大多数应用都把缓存做到了应用逻辑层,比如:使用redis或者memcache
索引:是排序的快速查找的特殊数据结构,定义作为查找条件的字段上,又称为键key,索引通过存储引擎实现
优点:
缺点:
索引类型:
二叉树:可能会有不平衡的情况
红黑树:平衡树
B Tree 索引
每次查询时效率可能不一样,时高时低。
数据越小,分支越多,层数越低,磁盘IO次数越少;(可以考虑只放编号,占用空间少,分支多,也就是B+TREE)
B+Tree索引
每次查询时效率相同,磁盘IO次数少,索引节点不放数据,叶子节点之间是有关系的
B+Tree索引:只有叶子节点放数据,按顺序存储,每一个叶子节点到根结点的距离是相同的;左前缀索引,适合查询范围类的数据
可以使用B+Tree索引的查询类型:
B+Tree索引的限制:
特别提示:
索引列的顺序和查询语句的写法应相匹配,才能更好的利用索引
为优化性能,可能需要针对相同的列但顺序不同创建不同的索引来满足不同类型的查询需求
Hash索引
Hash索引:基于哈希表实现,只有精确匹配索引中的所有列的查询才有效,索引自身只存储索引列对应的哈希值和数据指针,索引结构紧凑,查询性能好
Memory存储引擎支持显式hash索引,InnoDB和MyISAM存储引擎不支持
适用场景:只支持等值比较查询,包括=, <=>, IN()
不适合使用hash索引的场景
空间数据索引R-Tree( Geospatial indexing )
MyISAM支持地理空间索引,可使用任意维度组合查询,使用特有的函数访问,常用于做地理数据存储,使用不多
InnoDB从MySQL5.7之后也开始支持
全文索引(FULLTEXT)
在文本中查找关键词,而不是直接比较索引中的值,类似搜索引擎
InnoDB从MySQL 5.6之后也开始支持
聚簇和非聚簇索引,主键和二级索引
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-64tm2FRx-1592135370689)(C:\Users\cui\AppData\Roaming\Typora\typora-user-images\image-20200609163341701.png)]
MyISAM 主索引结构——非聚簇索引
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fpQcLqYX-1592135370692)(C:\Users\cui\AppData\Roaming\Typora\typora-user-images\image-20200609164054141.png)]
冗余和重复索引:
创建索引:
CREATE [UNIQUE] INDEX index_name ON tbl_name (index_col_name[(length)],...); #uniqe唯一键索引
ALTER TABLE tbl_name ADD INDEX index_name(index_col_name[(length)]);
help CREATE INDEX;
删除索引:
DROP INDEX index_name ON tbl_name;
ALTER TABLE tbl_name DROP INDEX index_name(index_col_name);
查看索引:
SHOW INDEXES FROM [db_name.]tbl_name;
优化表空间:
OPTIMIZE TABLE tb_name;
查看索引的使用
SET GLOBAL userstat=1; #统计索引的利用情况
SHOW INDEX_STATISTICS;
范例:KEY上有值代表有索引
MariaDB [hellodb]> SET GLOBAL userstat=1;
Query OK, 0 rows affected (0.000 sec)
MariaDB [hellodb]> SHOW INDEX_STATISTICS;
Empty set (0.000 sec)
MariaDB [hellodb]> desc students;
+-----------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------------------+------+-----+---------+----------------+
| StuID | int(10) unsigned | NO | PRI | NULL | auto_increment |
| Name | varchar(50) | NO | | NULL | |
| Age | tinyint(3) unsigned | NO | | NULL | |
| Gender | enum('F','M') | NO | | NULL | |
| ClassID | tinyint(3) unsigned | YES | | NULL | |
| TeacherID | int(10) unsigned | YES | | NULL | |
+-----------+---------------------+------+-----+---------+----------------+
6 rows in set (0.001 sec)
MariaDB [hellodb]> show indexes from students\G;
*************************** 1. row ***************************
Table: students
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 1
Column_name: StuID
Collation: A
Cardinality: 25
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
1 rows in set (0.001 sec)
MariaDB [hellodb]> SHOW INDEX_STATISTICS;
Empty set (0.000 sec)
MariaDB [hellodb]> select * from students where stuid=10;
+-------+--------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+--------------+-----+--------+---------+-----------+
| 10 | Yue Lingshan | 19 | F | 3 | NULL |
+-------+--------------+-----+--------+---------+-----------+
1 row in set (0.000 sec)
MariaDB [hellodb]> show INDEX_STATISTICS;
+--------------+------------+------------+-----------+
| Table_schema | Table_name | Index_name | Rows_read |
+--------------+------------+------------+-----------+
| hellodb | students | PRIMARY | 1 |
+--------------+------------+------------+-----------+
1 row in set (0.000 sec)
MariaDB [hellodb]> select * from students where stuid=20;
+-------+-----------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-----------+-----+--------+---------+-----------+
| 20 | Diao Chan | 19 | F | 7 | NULL |
+-------+-----------+-----+--------+---------+-----------+
1 row in set (0.001 sec)
MariaDB [hellodb]> show INDEX_STATISTICS;
+--------------+------------+------------+-----------+
| Table_schema | Table_name | Index_name | Rows_read |
+--------------+------------+------------+-----------+
| hellodb | students | PRIMARY | 2 |
+--------------+------------+------------+-----------+
1 row in set (0.000 sec)
可以通过EXPLAIN来分析索引的有效性,获取查询执行计划信息,用来查看查询优化器如何执行查询
参考资料: https://dev.mysql.com/doc/refman/5.7/en/explain-output.html
语法:
EXPLAIN SELECT clause
EXPLAIN输出信息说明:
列名 | 说明 |
---|---|
id | 执行编号,标识select所属的行。如果在语句中没子查询或关联查询,只有唯一的select,每行都将显示1。否则,内层的select语句一般会顺序编号,对应于其在原始语句中的位置 |
select_type | 简单查询:SIMPLE\ |
table | 访问引用哪个表(引用某个查询,如“derived3”) |
type | 关联类型或访问类型,即MySQL决定的如何去查询表中的行的方式 |
possible_keys | 查询可能会用到的索引 |
key | 显示mysql决定采用哪个索引来优化查询 |
key_len | 显示mysql在索引里使用的字节数 |
ref | 根据索引返回表中匹配某单个值的所有行 |
rows | 为了找到所需的行而需要读取的行数,估算值,不精确。通过把所有rows列值相乘,可粗略估算整个查询会检查的行数 |
Extra | 额外信息 Using index:MySQL将会使用覆盖索引,以避免访问表 Using where:MySQL服务器将在存储引擎检索后,再进行一次过滤 Using temporary:MySQL对结果排序时会使用临时表 Using filesort:对结果使用一个外部索引排序 |
说明: type显示的是访问类型,是较为重要的一个指标,结果值从好到坏依次是:system > const >eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range >index > ALL ,一般来说,得保证查询至少达到range级别,最好能达到ref
类型 | 说明 |
---|---|
All | 最坏的情况,全表扫描 |
index | 和全表扫描一样。只是扫描表的时候按照索引次序进行而不是行。主要优点就是避免了排序, 但是开销仍然非常大。如在Extra列看到Using index,说明正在使用覆盖索引,只扫描索引的数据,它比按索引次序全表扫描的开销要小很多 |
range | 范围扫描,一个有限制的索引扫描。key 列显示使用了哪个索引。当使用=、 <>、>、>=、<、<=、IS NULL、<=>、BETWEEN 或者 IN 操作符,用常量比较关键字列时,可以使用 range |
ref | 一种索引访问,它返回所有匹配某个单个值的行。此类索引访问只有当使用非唯一性索引或唯一性索引非唯一性前缀时才会发生。这个类型跟eq_ref不同的是,它用在关联操作只使用了索引的最左前缀,或者索引不是UNIQUE和PRIMARY KEY。ref可以用于使用=或<=>操作符的带索引的列。 |
eq_ref | 最多只返回一条符合条件的记录。使用唯一性索引或主键查找时会发生 (高效) |
const | 当确定最多只会有一行匹配的时候,MySQL优化器会在查询前读取它而且只读取一次,因此非常快。当主键放入where子句时,mysql把这个查询转为一个常量(高效) |
system | 这是const连接类型的一种特例,表仅有一行满足条件。 |
Null | 意味着mysql能在优化阶段分解查询语句,在执行阶段甚至用不到访问表或索引(高效) |
范例:
MariaDB [hellodb]> explain select * from students where stuid not in (5,10,20);
+------+-------------+----------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+----------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | students | ALL | PRIMARY | NULL | NULL | NULL | 25 | Using where |
+------+-------------+----------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.000 sec)
MariaDB [hellodb]> explain select * from students where stuid <> 10;
+------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | students | range | PRIMARY | PRIMARY | 4 | NULL | 24 | Using where |
+------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.000 sec)
MariaDB [hellodb]> explain select * from students where age > (select avg(age) from teachers);
+------+-------------+----------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+----------+------+---------------+------+---------+------+------+-------------+
| 1 | PRIMARY | students | ALL | NULL | NULL | NULL | NULL | 25 | Using where |
| 2 | SUBQUERY | teachers | ALL | NULL | NULL | NULL | NULL | 4 | |
+------+-------------+----------+------+---------------+------+---------+------+------+-------------+
2 rows in set (0.000 sec)
MariaDB [hellodb]> create index idx_age on students(age);
Query OK, 0 rows affected (0.007 sec)
Records: 0 Duplicates: 0 Warnings: 0
MariaDB [hellodb]> explain select * from students where age > (select avg(age) from teachers);
+------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| 1 | PRIMARY | students | range | idx_age | idx_age | 1 | NULL | 1 | Using where |
| 2 | SUBQUERY | teachers | ALL | NULL | NULL | NULL | NULL | 4 | |
+------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
2 rows in set (0.000 sec)
范例:创建索引和使用索引
MariaDB [hellodb]> create index idx_name on students(name(10));
Query OK, 0 rows affected (0.009 sec)
Records: 0 Duplicates: 0 Warnings: 0
MariaDB [hellodb]> show indexes from students\G;
*************************** 1. row ***************************
Table: students
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 1
Column_name: StuID
Collation: A
Cardinality: 25
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
*************************** 2. row ***************************
Table: students
Non_unique: 1
Key_name: idx_name
Seq_in_index: 1
Column_name: Name
Collation: A
Cardinality: 25
Sub_part: 10
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
2 rows in set (0.000 sec)
MariaDB [hellodb]> explain select * from students where name like 'w%';
+------+-------------+----------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+----------+-------+---------------+----------+---------+------+------+-------------+
| 1 | SIMPLE | students | range | idx_name | idx_name | 32 | NULL | 1 | Using where |
+------+-------------+----------+-------+---------------+----------+---------+------+------+-------------+
1 row in set (0.001 sec)
MariaDB [hellodb]> explain select * from students where name like 'x%';
+------+-------------+----------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+----------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | students | ALL | idx_name | NULL | NULL | NULL | 25 | Using where |
+------+-------------+----------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.000 sec)
说明:锁机制太严格会造成并发性的损失
锁:
S 锁和 S 锁是兼容的,X 锁和其它锁都不兼容,举个例子,事务 T1 获取了一个行 r1 的 S 锁,另外事务 T2 可以立即获得行 r1 的 S 锁,此时 T1 和 T2 共同获得行 r1 的 S 锁,此种情况称为锁兼容,但是另外一个事务 T2 此时如果想获得行 r1 的 X 锁,则必须等待 T1 对行 r 锁的释放,此种情况也成为锁冲突
锁粒度:
实现
分类:
锁策略:在锁粒度及数据安全性寻求的平衡机制
帮助:https://mariadb.com/kb/en/lock-tables/
加锁
LOCK TABLES tbl_name [[AS] alias] lock_type [, tbl_name [[AS] alias]
lock_type] ...
lock_type:
READ
WRITE
解锁
UNLOCK TABLES
关闭正在打开的表(清除查询缓存),通常在备份前加全局读锁
FLUSH TABLES [tb_name[,...]] [WITH READ LOCK]
#创建用户本质是修改数据库,修改user表,故也不能执行成功
查询时加写或读锁
SELECT clause [FOR UPDATE | LOCK IN SHARE MODE]
范例: 加读锁
mysql> lock tables students read ;
Query OK, 0 rows affected (0.00 sec)
mysql> update students set classid=2 where stuid=24;
ERROR 1099 (HY000): Table 'students' was locked with a READ lock and can't be updated
mysql> unlock tables ;
mysql> update students set classid=2 where stuid=24;
Query OK, 1 row affected (1 min 45.52 sec)
Rows matched: 1 Changed: 1 Warnings: 0
范例: 同时在两个终端对同一行记录修改
#同时对同一行记录执行update
#在第一终端提示1行成功
MariaDB [hellodb]> update students set classid=1 where stuid=24;
Query OK, 1 row affected (0.002 sec)
Rows matched: 1 Changed: 1 Warnings: 0
#在第二终端提示0行修改
MariaDB [hellodb]> update students set classid=1 where stuid=24;
Query OK, 0 rows affected (0.000 sec)
Rows matched: 1 Changed: 0 Warnings: 0
事务Transactions:一组原子性的SQL语句,或一个独立工作单元
事务日志:记录事务信息,实现undo,redo等故障恢复功能 ;
说明:事务日志会循环覆盖
4.6.3.1 事务特性
ACID特性:
Transaction生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oMkFblF9-1592135370694)(C:\Users\cui\AppData\Roaming\Typora\typora-user-images\image-20200610211501756.png)]
4.6.3.2 管理事务
显式启动事务:
BEGIN
BEGIN WORK
START TRANSACTION
结束事务:
#提交
COMMIT
#回滚
ROLLBACK
注意:只有事务型存储引擎中的DML语句方能支持此类操作
自动提交:
set autocommit={1|0}
默认为1,为0时设为非自动提交
建议:显式请求和提交事务,而不要使用“自动提交”功能
事务支持保存点:
SAVEPOINT identifier
ROLLBACK [WORK] TO [SAVEPOINT] identifier
RELEASE SAVEPOINT identifier
查看事务:
#查看当前正在进行的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
#查看当前锁定的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
#查看当前等锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
范例:找到未完成的导致阻塞的事务
#在第一会话中执行
MariaDB [hellodb]> begin;
Query OK, 0 rows affected (0.000 sec)
MariaDB [hellodb]> update teachers set age=25;
Query OK, 4 rows affected (0.000 sec)
Rows matched: 4 Changed: 4 Warnings: 0
#在第二个会话中执行
MariaDB [hellodb]> update teachers set age=30;
#在第三个会话中执行
MariaDB [hellodb]> show engine innodb status;
...省略...
---TRANSACTION 2384, ACTIVE 162 sec
3 lock struct(s), heap size 1136, 7 row lock(s), undo log entries 4
MySQL thread id 16, OS thread handle 140653843576576, query id 300158 localhost root
...省略...
MariaDB [hellodb]> select * from information_schema.innodb_locks;
+-------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+
| lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+-------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+
| 2394:25:3:2 | 2394 | X | RECORD | `hellodb`.`teachers` | PRIMARY | 25 | 3 | 2 | 1 |
| 2384:25:3:2 | 2384 | X | RECORD | `hellodb`.`teachers` | PRIMARY | 25 | 3 | 2 | 1 |
+-------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+
2 rows in set (0.002 sec)
MariaDB [hellodb]> select * from information_schema.innodb_lock_waits;
+-------------------+-------------------+-----------------+------------------+
| requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |
+-------------------+-------------------+-----------------+------------------+
| 2395 | 2395:25:3:2 | 2384 | 2384:25:3:2 |
+-------------------+-------------------+-----------------+------------------+
1 row in set (0.000 sec)
#查看正在进行的事务
MariaDB [hellodb]> select * from information_schema.innodb_trx\G;
*************************** 1. row ***************************
trx_id: 2395
trx_state: LOCK WAIT
trx_started: 2020-06-11 14:23:26
trx_requested_lock_id: 2395:25:3:2
trx_wait_started: 2020-06-11 14:23:26
trx_weight: 2
trx_mysql_thread_id: 18 #线程ID
trx_query: update teachers set age=30
trx_operation_state: starting index read
trx_tables_in_use: 1
trx_tables_locked: 1
trx_lock_structs: 2
trx_lock_memory_bytes: 1136
trx_rows_locked: 1
trx_rows_modified: 0
trx_concurrency_tickets: 0
trx_isolation_level: REPEATABLE READ
trx_unique_checks: 1
trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
trx_is_read_only: 0
trx_autocommit_non_locking: 0
*************************** 2. row ***************************
trx_id: 2384
trx_state: RUNNING
trx_started: 2020-06-11 14:18:39
trx_requested_lock_id: NULL
trx_wait_started: NULL
trx_weight: 7
trx_mysql_thread_id: 16 #线程ID
trx_query: NULL
trx_operation_state: NULL
trx_tables_in_use: 0
trx_tables_locked: 1
trx_lock_structs: 3
trx_lock_memory_bytes: 1136
trx_rows_locked: 7
trx_rows_modified: 4
trx_concurrency_tickets: 0
trx_isolation_level: REPEATABLE READ
trx_unique_checks: 1
trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
trx_is_read_only: 0
trx_autocommit_non_locking: 0
2 rows in set (0.001 sec)
MariaDB [hellodb]> show processlist;
+----+-------------+-----------+---------+---------+-------+--------------------------+----------------------------+----------+
| Id | User | Host | db | Command | Time | State | Info | Progress |
+----+-------------+-----------+---------+---------+-------+--------------------------+----------------------------+----------+
| 3 | system user | | NULL | Daemon | NULL | InnoDB purge worker | NULL | 0.000 |
| 2 | system user | | NULL | Daemon | NULL | InnoDB purge coordinator | NULL | 0.000 |
| 1 | system user | | NULL | Daemon | NULL | InnoDB purge worker | NULL | 0.000 |
| 4 | system user | | NULL | Daemon | NULL | InnoDB purge worker | NULL | 0.000 |
| 5 | system user | | NULL | Daemon | NULL | InnoDB shutdown handler | NULL | 0.000 |
| 9 | root | localhost | hellodb | Sleep | 13312 | | NULL | 0.000 |
| 11 | root | localhost | hellodb | Sleep | 13312 | | NULL | 0.000 |
| 14 | root | localhost | hellodb | Sleep | 7338 | | NULL | 0.000 |
| 16 | root | localhost | hellodb | Sleep | 268 | | NULL | 0.000 |
| 18 | root | localhost | hellodb | Query | 42 | Updating | update teachers set age=30 | 0.000 |
| 19 | root | localhost | hellodb | Query | 0 | Init | show processlist | 0.000 |
+----+-------------+-----------+---------+---------+-------+--------------------------+----------------------------+----------+
11 rows in set (0.000 sec)
#杀掉未完成的事务
MariaDB [hellodb]> kill 16;
Query OK, 0 rows affected (0.001 sec)
#杀掉之后,第二个会话中的命令立即执行成功,等待了三十多秒
MariaDB [hellodb]> update teachers set age=30;
Query OK, 4 rows affected (36.498 sec)
Rows matched: 4 Changed: 4 Warnings: 0
#查看事务锁的超时时长,默认50s
MariaDB [hellodb]> show global variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50 |
+--------------------------+-------+
1 row in set (0.002 sec)
4.6.3.3 事务隔离级别
MySQL 支持四种隔离级别,事务隔离级别:从上至下更加严格
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WhuynFKt-1592135370696)(C:\Users\cui\AppData\Roaming\Typora\typora-user-images\image-20200610212102406.png)]
READ UNCOMMITTED
可读取到未提交数据,产生脏读
READ COMMITTED
可读取到提交数据,但未提交数据不可读,产生不可重复读,即可读取到多个提交数据,导致每次读取数据不一致(当备份数据库时就很头疼)
REPEATABLE READ
可重复读,多次读取数据都一致,产生幻读,即读取过程中,即使有其它提交的事务修改数据,仍只能读取到未修改前的旧数据。此为MySQL默认设置(适合备份)
SERIALIZABLE
可串行化,未提交的读事务阻塞修改事务(加读锁,但不阻塞读事务),或者未提交的修改事务阻塞读事务(加写锁,其它事务的读,写都不可以执行)。会导致并发性能差
MVCC和事务的隔离级别:
MVCC(多版本并发控制机制)只在REPEATABLE READ和READ COMMITTED两个隔离级别下工作。其他两个隔离级别都和MVCC不兼容,因为READ UNCOMMITTED总是读取最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE则会对所有读取的行都加锁
指定事务隔离级别:
服务器变量tx_isolation指定,默认为REPEATABLE-READ,可在GLOBAL和SESSION级进行设置
SET tx_isolation='READ-UNCOMMITTED|READ-COMMITTED|REPEATABLEREAD|SERIALIZABLE'
服务器选项中指定
vim /etc/my.cnf
[mysqld]
transaction-isolation=SERIALIZABLE
死锁:
两个或多个事务在同一资源相互占用,并请求锁定对方占用的资源的状态
范例:
#第一个会话:修改数据并提交
MariaDB [hellodb]> select * from teachers;
+-----+---------------+-----+--------+
| TID | Name | Age | Gender |
+-----+---------------+-----+--------+
| 1 | Song Jiang | 45 | M |
| 2 | Zhang Sanfeng | 94 | M |
| 3 | Miejue Shitai | 77 | F |
| 4 | Lin Chaoying | 40 | F |
+-----+---------------+-----+--------+
4 rows in set (0.001 sec)
MariaDB [hellodb]> begin;
Query OK, 0 rows affected (0.000 sec)
MariaDB [hellodb]> update teachers set age=4 where tid=4;
Query OK, 1 row affected (0.000 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MariaDB [hellodb]> commit;
Query OK, 0 rows affected (0.001 sec)
#第二个会话:产生幻读,查看到的是修改前的数据
MariaDB [hellodb]> select * from teachers;
+-----+---------------+-----+--------+
| TID | Name | Age | Gender |
+-----+---------------+-----+--------+
| 1 | Song Jiang | 45 | M |
| 2 | Zhang Sanfeng | 94 | M |
| 3 | Miejue Shitai | 77 | F |
| 4 | Lin Chaoying | 40 | F |
+-----+---------------+-----+--------+
4 rows in set (0.000 sec)
MySQL 支持丰富的日志类型,如下:
事务日志:transaction log
事务型存储引擎自行管理和使用,建议和数据文件分开存放,redo log和undo log
Innodb事务日志相关配置:
MariaDB [hellodb]> show variables like '%innodb_log%';
innodb_log_file_size 50331648 每个日志文件大小
innodb_log_files_in_group 2 日志组成员个数
innodb_log_group_home_dir ./ 事务文件路径
innodb_flush_log_at_trx_commit 默认为1
范例:修改事务文件路径
[root@centos8 ~]#mkdir /data/trans_log
[root@centos8 ~]#chown mysql.mysql /data/trans_log
[root@centos8 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
innodb_log_group_home_dir=/data/trans_log
[root@centos8 ~]#ll /data/trans_log/
total 614400
-rw-rw---- 1 mysql mysql 209715200 Jun 11 16:14 ib_logfile0
-rw-rw---- 1 mysql mysql 209715200 Jun 11 16:14 ib_logfile1
-rw-rw---- 1 mysql mysql 209715200 Jun 11 16:14 ib_logfile2
事务日志性能优化
innodb_flush_log_at_trx_commit=0|1|2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lUTAupDR-1592135370699)(C:\Users\cui\AppData\Roaming\Typora\typora-user-images\image-20200610230835478.png)]
1 此为默认值,日志缓冲区将写入日志文件,并在每次事务后执行刷新到磁盘。 这是完全遵守ACID特性(若一秒钟发生了一百次事务就写一百次磁盘,保证数据不丢失)
0 提交时没有写磁盘的操作; 而是每秒执行一次将日志缓冲区的提交的事务写入刷新到磁盘。 这样可提供更好的性能,但服务器崩溃可能丢失最后一秒的事务(追求性能时)
2 每次提交后都会写入OS的缓冲区,但每秒才会进行一次刷新到磁盘文件中。 性能比0略差一些,但操作系统或停电可能导致最后一秒的交易丢失(追求性能时)
高并发业务行业最佳实践,是使用第三种折衷配置(=2):
1.配置为2和配置为0,性能差异并不大,因为将数据从Log Buffer拷贝到OS cache,虽然跨越用户态与内核态,但毕竟只是内存的数据拷贝,速度很快
2.配置为2和配置为0,安全性差异巨大,操作系统崩溃的概率相比MySQL应用程序崩溃的概率,小很多,设置为2,只要操作系统不奔溃,也绝对不会丢数据
说明:
设置为1,同时sync_binlog = 1表示最高级别的容错
innodb_use_global_flush_log_at_trx_commit=0 时,将不能用SET语句重置此变量( MariaDB 10.2.6后废弃)
范例: 十万次事务测试
#模式1
MariaDB [hellodb]> select @@innodb_flush_log_at_trx_commit;
+----------------------------------+
| @@innodb_flush_log_at_trx_commit |
+----------------------------------+
| 1 |
+----------------------------------+
1 row in set (0.000 sec)
MariaDB [hellodb]> call sp_testlog;
Query OK, 100000 rows affected (38.593 sec)
#模式0
MariaDB [hellodb]> select @@innodb_flush_log_at_trx_commit;
+----------------------------------+
| @@innodb_flush_log_at_trx_commit |
+----------------------------------+
| 0 |
+----------------------------------+
1 row in set (0.000 sec)
MariaDB [hellodb]> call sp_testlog;
Query OK, 100000 rows affected (3.639 sec)
#模式2
MariaDB [hellodb]> select @@innodb_flush_log_at_trx_commit;
+----------------------------------+
| @@innodb_flush_log_at_trx_commit |
+----------------------------------+
| 2 |
+----------------------------------+
1 row in set (0.000 sec)
MariaDB [hellodb]> call sp_testlog;
Query OK, 100000 rows affected (4.422 sec)
错误日志
mysqld启动和关闭过程中输出的事件信息
mysqld运行中产生的错误信息
event scheduler运行一个event时产生的日志信息
在主从复制架构中的从服务器上启动从服务器线程时产生的信息
错误文件路径
SHOW GLOBAL VARIABLES LIKE 'log_error'
范例:查看错误文件路径
MariaDB [hellodb]> show global variables like 'log_error';
+---------------+------------------------------+
| Variable_name | Value |
+---------------+------------------------------+
| log_error | /var/log/mariadb/mariadb.log |
+---------------+------------------------------+
1 row in set (0.002 sec)
记录哪些警告信息至错误日志文件
#CentOS7 mariadb 5.5 默认值为1
#CentOS8 mariadb 10.3 默认值为2
log_warnings=0|1|2|3...
范例:
MariaDB [hellodb]> SHOW GLOBAL VARIABLES LIKE 'log_warnings';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_warnings | 2 |
+---------------+-------+
1 row in set (0.001 sec)
通用日志:记录对数据库的通用操作,包括:错误的SQL语句
通用日志可以保存在:file(默认值)或 table(mysql.general_log表)
通用日志相关设置
general_log=ON|OFF
general_log_file=HOSTNAME.log
log_output=TABLE|FILE|NONE
范例:
#修改通用日志,记录通用日志至mysql.general_log表中
MariaDB [hellodb]> set global log_output="table";
Query OK, 0 rows affected (0.000 sec)
MariaDB [hellodb]> show global variables like 'log_output';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_output | TABLE |
+---------------+-------+
1 row in set (0.001 sec)
MariaDB [mysql]> select * from mysql.general_log\G
...省略...
*************************** 37. row ***************************
event_time: 2020-06-11 19:05:16.450156
user_host: root[root] @ localhost []
thread_id: 8
server_id: 1
command_type: Query
argument: show tables
*************************** 38. row ***************************
event_time: 2020-06-11 19:05:23.554815
user_host: root[root] @ localhost []
thread_id: 8
server_id: 1
command_type: Query
argument: select * from mysql.general_log
38 rows in set (0.001 sec)
范例:对访问的语句进行排序
[root@centos8 ~]#mysql -e 'select argument from mysql.general_log' | awk '{sql[$0]++}END{for(i in sql){print sql[i],i}}'|sort -nr
[root@centos8 ~]#mysql -e 'select argument from mysql.general_log' |sort |uniq -c |sort -nr
慢查询日志:记录执行查询时长超出指定时长的操作
慢查询相关变量
slow_query_log=ON|OFF #开启或关闭慢查询,支持全局和会话,只有全局设置才会生成慢查询文件
long_query_time=N #慢查询的阀值,单位秒
slow_query_log_file=HOSTNAME-slow.log #慢查询日志文件
log_slow_filter = admin,filesort,filesort_on_disk,full_join,full_scan,
query_cache,query_cache_miss,tmp_table,tmp_table_on_disk
#上述查询类型且查询时长超过long_query_time,则记录日志
log_queries_not_using_indexes=ON #不使用索引或使用全索引扫描,不论是否达到慢查询阀值的语句是否记录日志,默认OFF,即不记录
log_slow_rate_limit = 1 #多少次查询才记录,mariadb特有
log_slow_verbosity= Query_plan,explain #记录内容
log_slow_queries = OFF #同slow_query_log,MariaDB 10.0/MySQL 5.6.1 版后已删除
#打开后,会显示语句执行详细的过程
set profiling = ON
#查看语句,注意结果中的query_id值
show profiles ;
MariaDB [hellodb]> show profiles;
+----------+------------+-------------------------------+
| Query_ID | Duration | Query |
+----------+------------+-------------------------------+
| 1 | 0.00028782 | select @@profiling |
| 2 | 0.00012160 | show profiling |
| 3 | 0.00042853 | select * from teachers |
| 4 | 4.00436834 | select sleep(1) from teachers |
+----------+------------+-------------------------------+
4 rows in set (0.000 sec)
#显示语句的详细执行步骤和时长
Show profile for query #
MariaDB [hellodb]> show profile for query 4;
+------------------------+----------+
| Status | Duration |
+------------------------+----------+
| Starting | 0.000103 |
| Checking permissions | 0.000009 |
| Opening tables | 0.000017 |
| After opening tables | 0.000007 |
| System lock | 0.000003 |
| Table lock | 0.000004 |
| Init | 0.000011 |
| Optimizing | 0.000007 |
| Statistics | 0.000011 |
| Preparing | 0.000086 |
| Executing | 0.000008 |
| Sending data | 0.000035 |
| User sleep | 1.001038 |
| User sleep | 1.000877 |
| User sleep | 1.000612 |
| User sleep | 1.000947 |
| End of update loop | 0.000039 |
| Query end | 0.000007 |
| Commit | 0.000025 |
| Closing tables | 0.000007 |
| Unlocking tables | 0.000006 |
| Closing tables | 0.000019 |
| Starting cleanup | 0.000006 |
| Freeing items | 0.000011 |
| Updating status | 0.000169 |
| Logging slow query | 0.000287 |
| Reset for next command | 0.000016 |
+------------------------+----------+
27 rows in set (0.000 sec)
#显示cpu使用情况
Show profile cpu for query #
MariaDB [hellodb]> show profile cpu for query 99999;
+---------------------------+----------+----------+------------+
| Status | Duration | CPU_user | CPU_system |
+---------------------------+----------+----------+------------+
| continuing inside routine | 0.000010 | 0.000002 | 0.000007 |
| Checking permissions | 0.000002 | 0.000000 | 0.000002 |
| Opening tables | 0.000008 | 0.000002 | 0.000006 |
| After opening tables | 0.000002 | 0.000000 | 0.000002 |
| System lock | 0.000002 | 0.000001 | 0.000002 |
| Table lock | 0.000002 | 0.000000 | 0.000002 |
| Init for update | 0.000012 | 0.000002 | 0.000009 |
| Update | 0.000039 | 0.000008 | 0.000032 |
| End of update loop | 0.000003 | 0.000000 | 0.000002 |
| Query end | 0.000002 | 0.000001 | 0.000002 |
| Commit | 0.000508 | 0.000000 | 0.000283 |
| Closing tables | 0.000010 | 0.000000 | 0.000009 |
| Unlocking tables | 0.000005 | 0.000000 | 0.000005 |
| Closing tables | 0.000011 | 0.000000 | 0.000011 |
| Starting cleanup | 0.000008 | 0.000000 | 0.000007 |
+---------------------------+----------+----------+------------+
15 rows in set (0.001 sec)
功能:通过“重放”日志文件中的事件来生成数据副本
注意:建议二进制日志和数据文件分开存放
二进制日志记录三种格式
格式配置
MariaDB [hellodb]> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | MIXED |
+---------------+-------+
1 row in set (0.001 sec)
#MySQL 8.0 默认使用ROW方式
mysql> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW |
+---------------+-------+
1 row in set (0.07 sec)
二进制日志文件的构成
有两类文件
1.日志文件:mysql|mariadb-bin.文件名后缀,二进制格式,如: mariadb-bin.000001
2.索引文件:mysql|mariadb-bin.index,文本格式
二进制日志相关的服务器变量:
sql_log_bin=ON|OFF:#是否记录二进制日志,默认ON,支持动态修改,系统变量,而非服务器选项,session级
log_bin=/PATH/BIN_LOG_FILE:#指定文件位置;默认OFF,表示不启用二进制日志功能,上述两项都开启才可以,不能动态更改
binlog_format=STATEMENT|ROW|MIXED:#二进制日志记录的格式,默认STATEMENT
max_binlog_size=1073741824:#单个二进制日志文件的最大体积,到达最大值会自动滚动,默认为1G
#说明:文件达到上限时的大小未必为指定的精确值
binlog_cache_size=4m #此变量确定在每次事务中保存二进制日志更改记录的缓存的大小(每次连接)
max_binlog_cache_size=512m #限制用于缓存多事务查询的字节大小。
sync_binlog=1|0:#设定是否启动二进制日志即时同步磁盘功能,默认0,由操作系统负责同步日志到磁盘
expire_logs_days=N:#二进制日志可以自动删除的天数。 默认为0,即不自动删除
二进制日志相关配置
查看mariadb自行管理使用中的二进制日志文件列表,及大小
SHOW {BINARY | MASTER} LOGS
查看使用中的二进制日志文件
SHOW MASTER STATUS
在线查看二进制文件中的指定内容
SHOW BINLOG EVENTS [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]
范例:
show binlog events in 'mysql-bin.000001' from 6516 limit 2,3
mysqlbinlog:二进制日志的客户端命令工具,支持离线查看二进制日志
命令格式:
mysqlbinlog [OPTIONS] log_file…
--start-position=# 指定开始位置
--stop-position=#
--start-datetime= #时间格式:YYYY-MM-DD hh:mm:ss
--stop-datetime=
--base64-output[=name]
-v -vvv
范例:
mysqlbinlog --start-position=678 --stop-position=752 /var/lib/mysql/mariadb-bin.000003 -v
mysqlbinlog --start-datetime="2018-01-30 20:30:10" --stop-datetime="2018-01-30 20:35:22" mariadb-bin.000003 -vvv
二进制日志事件的格式:
# at 328
#151105 16:31:40 server id 1 end_log_pos 431 Query thread_id=1
exec_time=0 error_code=0
use `mydb`/*!*/;
SET TIMESTAMP=1446712300/*!*/;
CREATE TABLE tb1 (id int, name char(30))
/*!*/;
事件发生的日期和时间:151105 16:31:40
事件发生的服务器标识:server id 1
事件的结束位置:end_log_pos 431
事件的类型:Query
事件发生时所在服务器执行此事件的线程的ID:thread_id=1
语句的时间戳与将其写入二进制文件中的时间差:exec_time=0
错误代码:error_code=0
事件内容:
GTID:Global Transaction ID,mysql5.6以mariadb10以上版本专属属性:GTID
清除指定二进制日志
PURGE { BINARY | MASTER } LOGS { TO 'log_name' | BEFORE datetime_expr }
范例:
PURGE BINARY LOGS TO 'mariadb-bin.000003'; #删除mariadb-bin.000003之前的日志
PURGE BINARY LOGS BEFORE '2017-01-23';
PURGE BINARY LOGS BEFORE '2017-03-22 09:25:30';
删除所有二进制日志,index文件重新记数
RESET MASTER [TO #]; #删除所有二进制日志文件,并重新生成日志文件,文件名从#开始记数,默认从1开始,一般是master主机第一次启动时执行,MariaDB 10.1.6开始支持TO #
切换日志文件:
FLUSH LOGS;