当前位置:   article > 正文

数据库—MySQL基础_xustudyxu's blog

xustudyxu's blog

参考视频:

黑马程序员 MySQL数据库入门到精通,从mysql安装到mysql高级、mysql优化全囊括_哔哩哔哩_bilibili

参考笔记:

♥MySQL语言知识体系详解♥ | C++ 全栈知识体系

MySQL-简介 | xustudyxu's Blog

概念

数据库:Database,简称DB。

数据库管理系统:Database Management System,简称DBMS。

结构化查询语言:Structured Query Language,简称SQL。

分类

类型

全称

数据结构/主流产品

层次式数据库

HDBMS(Hierarchical Database Management System)

层次式数据库管理系统

有根结点的定向有序树

网络式数据库

NDBMS(Network Database Management System)

网络式数据库管理系统

网状数据结构

关系型数据库

RDBMS(Relational Database Management System)

关系数据库管理系统

二元关系(即二维表格形式)。

主要有:Oracle(Oracle)、MySQL(Oracle)、SQL Server(MS)、DB2(IBM)、Sybase(SAP)、PostgreSQL、INFORMIX

非关系型数据库

NoSQL

主要分类:

键值存储:Redis、MemcacheDB

文档存储:MongoDB、Elastic Search

列存储:HBase、Cassandra

图关系存储:Neo4J

新关系型数据库

NewSQL

结合关系型和非关系的优点。

主要有:Google Spanner、OceanBase、TiDB

数据库三级模式和二级映像

模式/映像

含义

外模式(用户级)

与某一应用有关的数据逻辑表示

外模式/模式映像

模式(概念级)

对数据库中全部数据的逻辑结构和特征的总体描述(DDL)

模式/内模式映像

内模式(物理级/存储级)

全体数据的内部表示(底层描述)

数据库对象

表(Table):代表单个实体。由行(Row)和列(Column)构成。行又称记录,代表实体的实例;列又称字段,代表实体的属性,列的标题被称为列名(字段名)。

视图(View):从表中查询出来的记录构成的虚拟表。目的:存放经常使用的查询结果集,缩短查询时间。

函数:必须返回值的存储程序。

存储过程(StoredProcedure):用于执行指定任务的由SQL语句和控制流语句(PL/SQL)组成的语句块,可由应用程序调用执行。目的:将数据操作放到数据库中执行,只返回需要的数据,减少传输量。

触发器(Trigger):不需调用而自动执行的存储过程,在表上执行某个动作时被自动调用。

约束:保证数据完整性的手段。有:主键约束、唯一约束、非空约束、检查约束、外键约束。

索引(Index)

索引组织表:物理存储像索引一样组织的表。

分区:可将表分为若干分区,分区可存储于不同的物理磁盘上,通过多分区并行读或分区排除读来优化查询。

群集:一种存储机制,将多个表中的若干记录放置在相同的物理块中,以优化查询。

序列:用来创建计数器,该计数器产生的数值用作表的主键值。

同义词:现有对象的别名,用来简化名称较长的对象。

缺省值(Default)

图表(Diagram):数据库表之间的一种关系示意图

用户(User)

规则(Rule):实现对数据库表中列数据的一种限制

程序包:根据公共名称分组的若干存储过程与函数的集合

数据库设计三大范式

第一范式:要求表的每个字段都必须是不可分割的最小数据单元。(确保每列的原子性)

第二范式:要求表的除主键外的其他字段都依赖于该主键。(确保每张表只描述一件事情)

第三范式:要求表的除主键外的其他字段都只能由主键决定。 (除主键外其他字段都不传递依赖于该主键)

注:数据库的性能比数据库规范化更重要,在数据规范化同时,要综合考虑数据库的性能,例如通过在表中添加额外的字段(如插入计算列(如成绩总分)),以减少需要从中搜索信息所需的时间。

E-R图(Entity-Relationship,实体关系图)

实体(Entity):用矩形表示。

属性(Attribute):用椭圆表示。

联系(Relationship):实体与其属性之间的联系用无方向连线表示;实体之间的联系用菱形和无方向连线表示。

实体间有三种联系:一对一、一对多、多对多。

实体及其属性表示

实体关系表示

    1. 安装、配置、连接、卸载Windows

安装

下载地址:MySQL :: Download MySQL Community Server 

1)64位系统亦可安装32位的。若以前安装过,确保以前的卸载干净!

2)在Choose Setup Type界面选择Custom,以自定义安装内容和安装路径。不要装在中文路径中!不建议安装在系统分区中(防止重装系统数据被清空)!

3)点击Finish前勾选“launch the MySQL Instance Configuration Wizard”以启动配置向导。

4)配置向导:

configuration type(配置方式)选 detailed configuration(手动精确配置),

sever type(服务器类型) 开发时选 Developer Machine,

database usage(数据库用途) 建议选 Multifunctional Database(通用多功能型)。

approximate number(网站并发连接数) 建议选 Manual Setting(手动设置),自己输一个数。

networking options:若启用TCP/IP连接,可在远程机器上访问mysql数据库。若启用严格模式,可降低有害数据进入数据库的可能性。

default character set 建议选 Best Support For Multilingualism,欲用原来数据库的数据,选 Manual Selected … 并指定字符集。

Windows options 安装为系统服务、自动启动、设置系统变量建议全打勾

security options 设置密码

最后点击 Execute 使设置生效,如果有误,按Back返回检查。

重装系统后配置使可用

  1. 将MySQL的bin目录追加到系统环境变量path
  2. 管理员身份运行cmd执行命令mysqld --install
  3. 修改MySQL的配置文件my.ini,确保basedir的值为MySQL程序主目录路径,datadir值为MySQL程序主目录下的data目录路径
  4. 启动服务MySQL并确保其启动类型为自动
  5. 重装系统后MySQL的root密码为空,设置root密码例:mysqladmin -u root password "root"

卸载

1)停止window的MySQL服务。

2)卸载MySQL安装程序。

3)删除MySQL安装目录下的所有文件。

4)删除C盘ProgramData隐藏目录中关于MySQL的目录(Windows 7)。

服务启动和停止

1、windows下重新启动mysql5的方法:
在安装mysql时系统会添加服务,可以通过管理工具或任务管理器里面的 <服务> 一项来停止和启动mysql。

2、也可以用命令行模式,在运行里输入:
停止:net stop mysql(其中mysql为你安装的mysql服务名称)

启动:net start mysql

3、如果你没安装系统服务,也可在命令行模式定位到mysql下的bin目录里,输入:

(关闭)mysqladmin shutdown

(启动)mysqladmin start

    1. 安装、配置、连接、卸载CentOS7

安装

下载地址:MySQL :: Download MySQL Community Server 

卸载MariaDB

    在CentOS中默认安装有MariaDB,是MySQL的一个分支,主要由开源社区维护。

    CentOS 7及以上版本已经不再使用MySQL数据库,而是使用MariaDB数据库。

    如果直接安装MySQL,会和MariaDB的文件冲突。

    因此,需要先卸载自带的MariaDB,再安装MySQL。

查看版本:

rpm -qa|grep mariadb

卸载

rpm -e --nodeps 文件名

检查是否卸载干净:

rpm -qa|grep mariadb

下载MySQL

下载地址:MySQL :: Download MySQL Community Server

解压

.tar.gz后缀:tar -zxvf 文件名

.tar.xz后缀:tar -Jxvf 文件名

重命名

将解压后的文件夹重命名(或者为文件夹创建软链接)

# 重命名

mv 原文件夹名 mysql8

# 软链接

ln -s 文件夹名 mysql8

添加PATH变量

    有两种添加方式:export命令临时生效、修改配置文件永久生效

#临时环境变量,关闭shell后失效,通常用于测试环境

export PATH=$PATH:/data/software/mysql8/bin

创建用户组和用户

# 创建一个用户组:mysql

groupadd mysql

# 创建一个系统用户mysql,指定用户组为mysql(-r:创建系统用户;-g:指定用户组)

useradd -r -g mysql mysql

数据目录

# 创建目录

mkdir -p /data/software/mysql8/datas

# 赋予权限

# 赋权——更改属主

chown -R mysql:mysql /data/software/mysql8/datas

# 赋权——更改模式

chmod -R 750 /data/software/mysql8/datas

配置参数

    在/data/software/mysql8/下,创建my.cnf配置文件,用于初始化MySQL数据库:

# 默认字符集

default-character-set=utf8mb4

[client]

port       = 3306

socket     = /tmp/mysql.sock

[mysqld]

port       = 3306

server-id  = 3306

user       = mysql

socket     = /tmp/mysql.sock

# 安装目录

basedir    = /data/software/mysql8

# 数据存放目录

datadir    = /data/software/mysql8/datas/mysql

log-bin    = /data/software/mysql8/datas/mysql/mysql-bin

innodb_data_home_dir      =/data/software/mysql8/datas/mysql

innodb_log_group_home_dir =/data/software/mysql8/datas/mysql

#日志及进程数据的存放目录

log-error =/data/software/mysql8/datas/mysql/mysql.log

pid-file  =/data/software/mysql8/datas/mysql/mysql.pid

# 服务端使用的字符集默认为8比特编码

character-set-server=utf8mb4

lower_case_table_names=1

autocommit =1

 ##################以上要修改的########################

skip-external-locking

key_buffer_size = 256M

max_allowed_packet = 1M

table_open_cache = 1024

sort_buffer_size = 4M

net_buffer_length = 8K

read_buffer_size = 4M

read_rnd_buffer_size = 512K

myisam_sort_buffer_size = 64M

thread_cache_size = 128

  

#query_cache_size = 128M

tmp_table_size = 128M

explicit_defaults_for_timestamp = true

max_connections = 500

max_connect_errors = 100

open_files_limit = 65535

   

binlog_format=mixed

    

binlog_expire_logs_seconds =864000

    

# 创建新表时将使用的默认存储引擎

default_storage_engine = InnoDB

innodb_data_file_path = ibdata1:10M:autoextend

innodb_buffer_pool_size = 1024M

innodb_log_file_size = 256M

innodb_log_buffer_size = 8M

innodb_flush_log_at_trx_commit = 1

innodb_lock_wait_timeout = 50

transaction-isolation=READ-COMMITTED

      

[mysqldump]

quick

max_allowed_packet = 16M

       

[myisamchk]

key_buffer_size = 256M

sort_buffer_size = 4M

read_buffer = 2M

write_buffer = 2M

        

[mysqlhotcopy]

interactive-timeout

初始化MySQL

mysqld --defaults-file=/data/software/mysql8/my.cnf --basedir=/data/software/mysql8/ --datadir=/data/software/mysql8/datas/mysql --user=mysql --initialize-insecure

参数说明:

    defaults-file:指定配置文件(要放在–initialize 前面)

    user:指定用户

    basedir:指定安装目录

    datadir:指定初始化数据目录

    intialize-insecure:初始化无密码

启动MySQL

查看 MySQL的 bin路径下,是否包含mysqld_safe,用于后台安全启动MySQL。

启动服务

mysqld_safe --defaults-file=/data/software/mysql8/my.cnf &

查看是否启动

ps -ef|grep mysql

登录

# 无密码登录方式

/data/software/mysql8/bin/mysql -u root --skip-password

# 有密码登录方式(初始的随机密码在/data/mysql8_data/mysql/mysql.log下)

mysql -u root -p

password:随机密码

修改密码

# 修改密码

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';

# 刷新权限

FLUSH PRIVILEGES;

设置允许远程登录

登录到mysql里执行

mysql> use mysql

mysql> update user set user.Host='%'where user.User='root';

mysql> flush privileges;

mysql> quit

    1. QL分类

分类

用于

包含语句

事务

数据定义语言(DDL

Data Definition Language

用于操作数据库对象

CREATE、ALTER、DROP、RENAME、SHOW TABLES、DESC等语句

不需要事务的参与,自动提交

数据操纵语言(DML

Data Manipulation Language

用于操作数据库数据

INSERT、UPDATE、DELETE/truncate等语句

与事务相关,执行完DML操作后必须经过事务控制语句提交后才真正的将改变应用到数据库中

事务控制语言(TCL

Transaction Control Language

用于维护数据一致性

提交(COMMIT)、回滚(ROLLBACK)、保存点(SAVEPOINT)三条语句

数据查询语言(DQL

Data Query Language

select语句

数据控制语言(DCL

Data Control Language

用于创建用户、控制访问权限等

授予(GRANT)语句,收回(REVOKE)语句,CREATE USER语句

不需事务参与,自动提交

SQL注释:

单行注释:-- 注释内容 或 #注释内容(MySQL特有)

多行注释:/* 注释内容 */

    1. QL语言关键字

CRUD

Create

Delete

Update

Read

数据库管理

create

drop

alter

show

表管理

create

drop

alter(add/drop/modify/change/rename)

show/desc

数据管理

insert

delete/truncate

update…set…

select

create,drop,alter,show管理数据库或表,truncate管理数据时后面接database(s)或table关键字;

insert,delete,update,select管理数据,desc管理表时后面不要接table关键字。

数据库管理(DDL数据定义语言)

MySQL

Oracle

登陆数据库

本地:mysql -u用户名 -p密码

远程:mysql [-h主机地址] [-P端口号] -u用户名 [-p密码]

注:

  1. 使用mysql命令前需将MySQL的bin目录加入到环境变量。
  2. -h、-P、-p为可选参数,如果未指定:

-h默认本机地址

-P默认3306,

-p未指定则会提示输入密码

  1. -h和-P后面可以有空格,-p后面不能有空格

如遇报错:

ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost' (10061)

解决:

管理员身份运行cmd,输入:mysqld -install

然后启动MySQL服务

mysql8登录如遇报2059错误,原因是8.0之后mysql更改了密码的加密规则,只要在命令窗口把加密方法改回去即可:

alter user root@localhost identified by 'root的密码' password expire never;

alter user root@localhost identified with mysql_native_password by 'root的密码';

flush privileges;

本地:sqlplus

远程:sqlplus 用户名/密码@ip:端口/服务名

退出数据库

exit

quit;

exit;

查看软件版本

select version();

查看数据库文件存储路径

select @@datadir;

查询所有数据库

show databases;

MySQL初始4个数据库:

information_schema:mysql元数据数据库。

mysql:mysql配置数据库。其中user表用于管理mysql用户信息。

performance_schema:--mysql性能监控信息数据库。

test:测试数据库。

查询当前数据库

select DATABASE()

选择数据库

use 库名

创建数据库并指定默认字符集

create database [if not exists] 库名 [default character set 字符集] [collate 排序规则];

修改数据库的字符集

alter database 数据库 default character set utf8;

查看数据库字符集

show variables like 'character%';

select userenv('language') from dual;

设置数据库字符集

set names utf8; 等同于下面三句:

设置客户端字符集:set character_set_client=utf8;

设置连接字符集:set character_set_connection=utf8;

设置输出字符集:set character_set_results=utf8;

注:

1)修改cmd当前代码页为utf-8:chcp 65001

2)mysql有六处使用了字符集:

| character_set_client |客户端使用的默认字符集

| character_set_connection |连接数据库的默认字符集

| character_set_database |数据库使用的默认字符集

| character_set_filesystem |

| character_set_results |返回结果集的默认字符集

| character_set_server |服务器安装时指定的默认字符集

| character_set_system |数据库系统使用的字符集

| character_sets_dir |

创建数据库并指定默认校验规则

create database 库名 collate utf8_general_ci;

查看数据库的创建语句和字符集

show create database 库名;

查看常用的字符串集的校验规则

show character set;

校验规则:一个字符集可以同时存在多种校验规则。

使用A校验规则:取字符的ascii码值比较大小: a<b(97<98)

使用B校验规则:取字符的ascii码值的负数比较大小:a>b(-97<-98)

删除数据库

drop database [if exists] 数据库;

    1. 管理

表空间是数据库中最大的逻辑单位,Oracle数据库采用表空间将相关的逻辑组件组合在一起,一个Oracle数据库至少包含一个表空间。每个表空间由一个或多个数据文件组成,一个数据文件只能与一个表空间相联系。 

在每一个数据库中都有一个名为SYSTEM的表空间,即系统表空间,该表空间是在创建数据库或数据库安装时自动创建的,用于存储系统的数据字典表、程序单元、过程、函数、包和触发器等。

表空间类型

永久性表空间:一般保存表、视图、过程和索引等的数据

临时性表空间:只用于保存系统中短期活动的数据

撤销表空间:用来帮助回退未提交的事务数据

操作与运用

创建表空间(需要sys或system用户权限)

【语法】

CREATE TABLESPACE 表空间名

   DATAFILE '数据文件路径' SIZE 大小

   [AUTOEXTEND ON] [NEXT 大小]

   [MAXSIZE 大小];

【说明】

表空间名会自动转为大写

数据文件路径中若包含目录需要先创建

SIZE为初始表空间大小,要带上单位(K或M)

AUTOEXTEND ON 开启自动扩展

NEXT为文件满了后扩展大小

MAXSIZE为文件最大大小,值为数值或UNLIMITED(表示不限大小)

查询表空间

--管理员角色查看表空间

SELECT file_name,tablespace_name,bytes,autoextensible

FROM dba_data_files

WHERE tablespace_name='ITCAST_TS'; --需按大写查询

【注意】表空间名需全部大写

修改表空间

ALTER TABLESPACE 表空间名

   ADD DATAFILE '文件路径' SIZE 大小

   [AUTOEXTEND ON] [NEXT 大小]

   [MAXSIZE 大小];

删除表空间

DROP TABLESPACE 表空间名; -- 只删除表空间

DROP TABLESPACE 表空间名 INCLUDING CONTENTS AND DATAFILES; --删除表空间及数据文件

    • 管理(DCL数据控制语言)

Oracle系统常见用户

用户

说明

sys

超级用户,主要用来维护系统信息和管理实例,以SYSDBA或SYSOPER角色登录。密码为在安装时设置的管理口令,如一般设置为:orcl

system

默认的系统管理员,拥有DBA权限,通常用来管理Oracle数据库的用户、权限和存储,以Normal方式登录。密码为在安装时设置的管理口令,如一般设置为:orcl

scott

示范用户,使用users表空间。一般该用户默认密码为tiger

Oracle中有个模式(schema)的概念,它是用户的所有数据库对象的集合;一般在创建用户的同时会自动创建一个这样的模式,名称和用户名称一样。

MySQL

Oracle

查询用户

mysql的用户信息保存在mysql.user表中

select * from mysql.user

select * from all_users;

select * from dba_users; --更详细的用户信息

查看当前用户

show user

解锁用户

ALTER USER 用户名 ACCOUNT UNLOCK;

锁定用户

ALTER USER 用户名 ACCOUNT LOCK;

创建用户

CREATE USER '用户名'@'主机名'IDENTIFIED BY '密码'

注1:新创建的用户需要赋予权限才能访问表

注2:主机名为%时表示任何主机

CREATE USER 用户名 IDENTIFIED BY 密码

DEFAULT TABLESPACE 表空间名

[TEMPORARY TABLESPACE 临时表空间名];

【注】新创建的用户需要赋予权限才能登录

查看用户密码

select PASSWORD('root');

-- *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B

删除用户

方法一:drop user 用户名@主机名;

方法二:DELETE FROM mysql.user WHERE User='用户名' and Host='主机名';

DROP USER 用户名 CASCADE;

修改用户密码

5.7之前版本:

①set password for twx@localhost = password('123456');

②update mysql.user set password=password('123456') where user="twx" and host = "localhost";

5.7版本:

①update mysql.user set authentication_string=password('123456') where user="twx" and host="localhost";

8.0以上版本:

①alter user 'twx'@'localhost' identified [with mysql_native_password] by 'root';

②set password for twx@localhost = '123456';

【语法】

ALTER USER 用户名 identified by 密码

修改Oracle的sys或system用户密码

运行cmd:

键入“sqlplus/nolog” 回车

键入“conn/as sysdba” 回车

键入“alter user system identified by 要改成的密码;” 回车

重置密码前要先开启服务,不然执行不了。

切换用户

conn 用户名/密码

以管理员身份登录:conn system as sysdba

MySQL实现远程登录二法:

1、授权法(增加用户):

use mysql;

grant all privileges on *.* to root@'%' identified by "password";

2、改表法(修改用户):

use mysql;

update user set host = '%' where user = 'root' and host='localhost';

Oracle密码过期,取消密码180天限制

1、进入sqlplus模式

sqlplus / as sysdba;

2、查看用户密码的有效期设置(一般默认的配置文件是DEFAULT)

SELECT * FROM dba_profiles WHERE profile='DEFAULT' AND resource_name='PASSWORD_LIFE_TIME';

3、将密码有效期由默认的180天修改成“无限制”,修改之后不需要重启动数据库,会立即生效

ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED;

4、帐户再改一次密码

alter user 用户名 identified by 原密码;

5、使用修改后的用户登录,如果报“ORA-28000:用户已被锁”,解锁

alter user db_user account unlock;

commit;

    • 管理DCL数据控制语言)

MySQL

Oracle

授予

GRANT 权限列表 ON 库名.表名 TO 用户名@主机名 [IDENTIFIED BY '新密码'] [WITH GRANT OPTION];

注一:多个权限用英文逗号分割,常用的权限有:all或all privileges、select、insert、update、delete、alter、drop、create,默认有usage权限。

注二:任意库名、任意表名用*表示,任意主机名用%表示。

注二:with grant option表示允许用户将自己的权限授权给其它用户

注三:授权后若未生效,可刷新权限使生效:flush privileges;

-------------------------------------------------------

【语法1】

GRANT 角色 TO 用户;

【示例1】

--授予CONNECTRESOURCE两个角色

GRANT connect, resource TO itcast;

【备注1】使用如下语句可以查看resource角色下的权限

SELECT * FROM DBA_SYS_PRIVS WHERE GRANTEE='RESOURCE'

【语法2】

GRANT 操作 ON 模式.对象 TO 用户;

【示例2】

--允许用户查看、更新 EMP 表中的记录

GRANT select,update ON SCOTT.emp TO  itcast;

【查询角色、权限】

--查看当前用户的系统权限

select * from user_sys_privs;

--查看当前用户的对象权限

select * from user_tab_privs;

--查看当前用户的所有角色

select * from user_role_privs;

撤销

REVOKE 权限列表 ON 库名.表名 FROM 用户名@主机名

【语法1】

REVOKE 角色权限(角色)[,角色权限] FROM 用户;

【示例1】

--撤销CONNECTRESOURCE两个角色

REVOKE connect, resource FROM itcast;

【语法2】

REVOKE 操作 ON 模式.对象  FROM 用户;

【示例2】

--撤销用户查看、更新 EMP 表中的记录的操作

REVOKE select,update ON SCOTT.emp FROM  itcast;

查询

show grants for 用户名@主机名;

分类

MySQL

Oracle

数值类型

bit(位数):位类型。位数默认值1,范围1-64

tinyint:大小1byte,有符号范围-128~127

boolboolean:使用0或1表示真或假

smallint:大小2byte,有符号范围正负3万多

mediumint:大小3byte,有符号范围正负8百多万

int(最小显示位数):大小4byte,有符号范围正负21亿多,最小显示位数用于限定zerofill样式

bigint:大小8byte,有符号范围正负九百多亿亿

float[(精度, 标度)]

double[(精度, 标度)]

decimal[(精度, 标度)]:精度默认10,标度默认0

number

number[(精度[, 刻度])]

精度的范围从1到38,刻度的范围从-84到127

例:

NUMBER 表示使用默认值NUMBER(5)

NUMBER(5,2) 表示整数部分最大3位,小数部分为2位;

NUMBER(5,-2) 表示数的整数部分最大为7其中对整数的倒数2位为0,前面的取整。

float

存储二进制数据,精度为二进制的1-126,转十进制时需乘以0.30103

字符串(定长/变长)

char(字符数):定长字符串,最大可指定255

varchar(字符数):变长字符串,最大容量65535B(64KB),utf8mb4编码时最大可指定16383

tinytexttinyclob:小文本数据,最大255B

textclob:大文本数据,最大64KB(65535B)

mediumtextmediumclob:更大文本数据,最大16MB

longtextlongclob:超大文本数据,最大4GB

char[(大小)] 定长字符串,最大2000字节,默认1

NCHAR(大小)  定长字符串,根据Unicode标准定义长度

VARCHAR2(大小)  变长字符串。最大4000字节

NVARCHAR2(大小) 变长字符串,依据所选国家字符集。最大4000字节

long 可变长度的字符数据,最大2G字节(少用)

CLOB 大文本数据,最大4G字节。可容纳单字节字符。不支持不等宽字符集

NCLOB 大文本数据,依据所选国家字符集。最大4G字节。可容纳单字节字符。不支持不等宽字符集

二进制

tinyblob:小的二进制数据,最大255B

blob :大的二进制数据,最大64KB(65535B)

mediumblob:更大的二进制数据,最大16MB

longblob:超大的二进制数据,最大4GB

RAW(大小) 二进制数据,最大2000字节

LONG RAW 变长二进制数据,最大2G字节

BLOB 大的二进制数据,最大4G字节

BFILE 大型二进制文件定位器,目标文件最大4G字节,通过字符流访问

日期时间

date :大小3B,格式YYYY-MM-DD

time:大小3B,格式HH:MM:SS

year:大小1B,格式YYYY

datetime:大小8B,格式YYYY-MM-DD HH:MM:SS

timestamp 大小4B,格式YYYY-MM-DD HH:MM:SS自动记录insert、update的时间有2038年问题

DATE 有效范围从BC 4712-01-01到 AD 9999-12-31

timestamp

表管理DDL数据定义语言)

MySQL

Oracle

查询表

查看所有表

show tables

select * from tabs;

select table_name from tabs;

查看表结构

desc 表名;

show columns from 表名;

describe 表名;

desc 表名;(只能写在命令窗口中)

查看建表语句

show create table 表名;

创建表

【员工表(主表/参考表)】:

CREATE TABLE employee(

id BIGINT UNSIGNED AUTO_INCREMENT COMMENT '主键',

name VARCHAR(20) NOT NULL COMMENT '姓名',

id_card CHAR(18) UNIQUE COMMENT '身份证',

age TINYINT CHECK(age > 0 && age <= 120) COMMENT '年龄',

e_id INT(8) ZEROFILL COMMENT '工号',

create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

PRIMARY KEY(id)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '员工表';

【员工考勤表(副表/从表)】:

CREATE TABLE employee_sign(

id BIGINT UNSIGNED AUTO_INCREMENT COMMENT '主键',

employee_id INT COMMENT '员工编号',

sign_in TIME DEFAULT NULL COMMENT '签到时间',

sign_out TIME DEFAULT NULL COMMENT '签退时间',

create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

PRIMARY KEY(id),

[CONSTRAINT] sign_emloyee_fk FOREIGN KEY(employee_id) REFERENCES employee(id) [ON update CASCADE] [ON delete CASCADE]

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '员工签到表';

【约束说明】:

UNSIGNED 无符号(指定非负数可增加数据长度)

ZEROFILL 零填充

PRIMARY KEY 主键(非空+唯一)

PRIMARY KEY(字段1, 字段2) 联合主键

AUTO_INCREMENT 自增长(默认从0开始,不受DELETE影响,受TRUNCATE影响)

NOT NULL 非空(不可赋null,可赋'')

UNIQUE 唯一(可重复赋null)

DEFAULT 默认值(可赋null)

CKECK 检查约束(8.0.16版本开始)

DEFAULT CURRENT_TIMESTAMP 创建时间自动填充

CONSTRAINT 声明约束

FOREIGN KEY 外键约束。

外键:主表主键作为从表外键,两表之间产生数据约束关系。

增加数据时,要先主表后从表。

删除数据时,要先从表后主表。(主先副而生,后副而亡)

外键删除/更新行为:

NO ACTION或RESTRICT:主键列和外键列都不允许直接修改更新,除非先删除彼表相关数据(暂存到临时表或临时变量),再更新此表,最后更新彼表。

CASCADE:直接修改或删除主表数据,从而影响副表数据。(级联修改:ON update CASCADE /级联删除:ON delete CASCADE)

SET NULL:删除主表记录时,对应子表外键值设为null

SET DEFAULT:删除主表记录时,对应子表外键值设为默认值

CREATE TABLE 表名(

字段1 类型 [NOT NULL] [PRIMARY KEY],

字段2 类型 [NOT NULL],

...

[, constraint 约束名 约束类型 (要约束的字段),

... ] );

【说明】

constraint约束类型有:

check,unique,primary key,not null,foreign key);

【示例】

create table t_student(

s_id number(8) PRIMARY KEY,

s_name varchar2(20) not null,

s_sex varchar2(8),

clsid number(8),

constraint u_1 unique(s_name),

constraint c_1 check (s_sex in ('MALE','FEMALE'))

);

复制表结构或兼数据

复制表结构:

create table b like a;

灵活定义表结构和数据的复制(会丢失表注释;若不想要数据,添加条件1=2):

create table c as select ...

--从现有的表创建表及复制其数据

复制表结构:

CREATE TABLE 表名 as <SELECT 语句>

复制表结构和数据:

CREATE TABLE 表名 as <SELECT 语句带where 1=2判断> 

例:

create table emp as select empno,ename from scott.emp;--复制表结构和数据

create table emp as select * from scott.emp where 1=2;--只复制表结构

create table tongwx.dept as select * from scott.dept;--在模式(用户)之间复制表

删除表

drop table [if exists] 表名;

DROP TABLE 表名;--删除到回收站

DROP TABLE 表名 PURGE;--彻底删除

删除并重建表

truncate table 表名;

修改表

添加字段

alter table 表 add [column] 字段名 字段类型 COMMENT '注释'[, add [column] 字段名 字段类型 COMMENT '注释'...];

ALTER TABLE 表名 ADD (字段1 类型 [NOT NULL],

字段2 类型 [NOT NULL] ... );

添加外键约束

ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY(外键字段) REFERENCES 主表(主表字段) [ON update 更新方式] [ON delete 删除方式]

删除字段(慎用

alter table 表 drop [column] 字段名[, drop [column] 字段名…];

ALTER TABLE 表名 DROP(字段1,字段2... );

删除主键约束

ALTER TABLE 表名 DROP PRIMARY KEY;

删除外键约束

ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;

修改字段类型

alter table 表 modify [column] 字段名 新的字段类型;

ALTER TABLE 表名 MODIFY(字段1 新类型,字段2 新类型 ...);

修改字段名称、字段类型[及其他]

alter table 表 change [column] 旧字段名 新字段名 字段类型 [COMMENT '注释'] [约束];

ALTER TABLE 表名 RENAME COLUMN 原字段名 TO 新字段名;

修改表名称

alter table 表名 rename [to] 新表名;

或:

rename table 表名 to 新表名;

修改自增主键id起始值

alter table user AUTO_INCREMENT=100;

修改表的字符集

alter table student CHARACTER SET utf8mb4;

修改表的排序规则

ALTER TABLE 表名 COLLATE=utf8mb4_bin;

查看回收站:

show recyclebin;  

select * from recyclebin;

清空回收站:

purge recyclebin;

MySQL字符集(CHARSET)

8个character_set变量

查看 MySQL中8个 character_set 变量

SHOW VARIABLES LIKE '%char%';

character_set_client

主要用来设置客户端使用的字符集。

character_set_connection

主要用来设置连接数据库时的字符集,如果程序中没有指明连接数据库使用的字符集类型则按照这个字符集设置。

character_set_database

主要用来设置默认创建数据库的编码格式,如果在创建数据库时没有设置编码格式,就按照这个格式设置。

character_set_filesystem

文件系统的编码格式,把操作系统上的文件名转化成此字符集,即把character_set_client转换character_set_filesystem,默认binary是不做任何转换的。

character_set_results

数据库给客户端返回时使用的编码格式,如果没有指明,使用服务器默认的编码格式。

character_set_server

服务器安装时指定的默认编码格式,这个变量建议由系统自己管理,不要人为定义。

character_set_system

数据库系统使用的编码格式,这个值一直是utf8,不需要设置,它是为存储系统元数据的编码格式。

character_sets_dir

这个变量是字符集安装的目录。

在启动mysql后,我们只关注下列变量是否符合我们的要求

    character_set_client

    character_set_connection

    character_set_database

    character_set_results

    character_set_server

下列三个系统变量我们不需要关心,不会影响乱码等问题

    character_set_filesystem

    character_set_system

    character_sets_dir

更改以上字符集直接set如:set character_set_XXX = utf8mb4;(XXX是写以上的变量名)

用户请求的字符集转换流程

mysql Server收到请求时将请求数据从 character_set_client 转换为 character_set_connection

进行内部操作前将请求数据从 character_set_connection 转换为内部操作字符集,步骤如下

A. 使用每个数据字段的 CHARACTER SET 设定值;

B. 若上述值不存在,则使用对应数据表的字符集设定值

C. 若上述值不存在,则使用对应数据库的字符集设定值;

D. 若上述值不存在,则使用 character_set_server 设定值。

最后将操作结果从内部操作字符集转换为 character_set_results

MySQL排序规则(COLLATE)

MySQL的排序规则是干什么的?

MySQL排序规则是指对指定字符集下不同字符的比较规则。每种字符集都有多种它支持的排序规则,并指定一种排序规则为默认值。

排序规则会影响:

ORDER BY语句查询的结果顺序

WHERE条件中大于小于号的筛选结果

DISTINCT语句的查询结果

GROUP BY、HAVING语句的查询结果

字符类索引的创建

排序规则设置

排序规则设置可以分为:MySQL实例级别、库级别、表级别、列级别以及SQL指定。

MySQL实例级别设置(设置完需重启mysql服务)

实例级别的排序规则设置就是MySQL配置文件或启动指令中的collation_connection系统变量。

可以通过修改mysql的配置文件my.ini来修改相应的排序规则,或者通过命令来设置字符集和排序规则:

set character_set_client = utf8mb4;

set character_set_connection = utf8mb4;

set character_set_database = utf8mb4;

set character_set_filesystem = utf8mb4;

set character_set_results = utf8mb4;

set character_set_server = utf8mb4;

set character_set_system = utf8mb4;

set collation_connection = utf8mb4_bin;

set collation_database = utf8mb4_bin;

set collation_server = utf8mb4_bin;

查看MySQL实例级别的字符集和排序规则:

show variables like '%character%';

show variables like '%collation%';

库级别设置(设置完需重启mysql服务?)

创建数据库时指定数据集和排序规则:

CREATE DATABASE DEMO

DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

修改数据库字符集和排序规则:

ALTER DATABASE `demo`

DEFAULT CHARACTER SET utf8mb4

DEFAULT COLLATE utf8mb4_bin;

查看库级别排序规则:

show create database demo;

表级别设置

在创建表的时候指定表的数据集和排序规则:

use demo;

CREATE TABLE user(

  `id` int(11) NOT NULL,

  `name` varchar(255) DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

生成修改库表字符集和排序规则语句:

select

CONCAT('ALTER TABLE ', TABLE_SCHEMA, '.', table_name, ' CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;')

from

information_schema.TABLES

where

TABLE_SCHEMA = 'demo'

查看表级别排序规则:

show table status from demo like 'user';

列级别设置

在创建表的时候指定列的数据集和排序规则

CREATE TABLE `user` (

  `id` int(11) NOT NULL,

  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

生成修改列字符集和排序规则语句:

select

CONCAT('ALTER TABLE ', TABLE_SCHEMA, '.', table_name,

' MODIFY ', column_name, ' ', DATA_TYPE, '(', CHARACTER_MAXIMUM_LENGTH,

') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci',

(case when IS_NULLABLE = 'NO' then ' NOT NULL' else '' end), ';')

from

information_schema.COLUMNS

where

TABLE_SCHEMA = 'demo'

and DATA_TYPE = 'varchar'

and ( CHARACTER_SET_NAME != 'utf8mb4' or COLLATION_NAME != 'utf8mb4_general_ci' );

查看列级别排序规则:

show full columns from user;

SQL指定设置

SQL语句中指定排序规则

SELECT id, name FROM `user`

ORDER BY name COLLATE utf8mb4_unicode_ci;

排序规则优先级

优先级顺序是 SQL语句 > 列级别设置 > 表级别设置 > 库级别设置 > 实例级别设置

几种常用排序规则介绍

我们以字符集utf8mb4为例,常用的排序规则有:

utf8mb4_general_ci

ci即case insensitive,不区分大小写。没有实现Unicode排序规则,在遇到某些特殊语言或者字符集,排序结果可能不一致,但是,在绝大多数情况下,这些特殊字符的顺序并不需要那么精确。另外,在比较和排序的时候速度更快。

utf8mb4_bin

bin即binary,将字符串每个字符用二进制数据编译存储,区分大小写,而且可以存二进制的内容。排序准确度高,但校对速度稍慢。

utf8mb4_unicode_ci

不区分大小写,基于标准的Unicode来排序和比较,能够在各种语言之间精确排序,在特殊情况下,Unicode排序规则为了能够处理特殊字符的情况,实现了略微复杂的排序算法,所以兼容度比较高,但是性能不高。

utf8mb4_general_cs

cs即case sensitive,区分大小写。

    • DML数据操作语言)
    • SQL的执行顺序

MySQL

Oracle

增加数据

插入所有字段:INSERT INTO 表名 VALUES(1,'张三','男',20)[,(2,'李四','女',30)...];

插入部分字段:INSERT INTO 表名(id,name) VALUES(1,'张三')[,(2,'李四')...];

删除数据

带条件的删除:delete from student where id=2;

全部删除(慎用):去掉where子句

全部删除另一种方式(属于DDL慎用):truncate table student;

delete和truncate比较:

delete: 可带条件;不删约束(不重置自增长索引);可回滚。

truncate:不带条件;删约束(重置自增长索引);不回滚。(原理:先删除表再创建表)

修改数据

带条件的修改:update student set gender='男',age=30 where id=2;

全部修改(慎用):去掉where子句。

拷贝数据

create table t1 as select * from emp where 1=2;

insert into t1 select * from emp where sal>2000;

查询语句的书写顺序

<SELECT> <FROM> [WHERE] [GROUP BY] [HAVING] [ORDER BY] [LIMIT]

其中SELECT和FROM为必须子句。

查询语句的执行顺序

子句顺序

子句内部顺序

优化指南

说明

FROM

从后往前、从右到左

执行顺序为从后往前, 所以数据量较少的表尽量放在后面

多表连接时,使用表的别名并把别名前缀于每个Column上。可以减少解析的时间并减少那些由Column 歧义引起的语法错误。

对FROM子句中前两个表执行笛卡尔积生成虚拟表vt1。

oracle 的解析器按照从右到左的顺序处理,FROM 子句中的表名,FROM 子句中写在最后的表(基础表 driving table)将被最先处理,即最后的表为驱动表,在FROM 子句中包含多个表的情况下,你应当选择记录条数最少的表作为基础表。如果有3 个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指被其他表所引用的表

ON

对vt1表应用ON筛选器生成虚拟表vt2

JOIN

如果是outer join 那么这一步就将添加外部行,left outer jion 就把左表在上一步中过滤掉的行添加进来,如果是right outer join 那么就将右表在上一步中过滤掉的行添加进来,这样生成虚拟表 vt3。

如果 from 子句中的表数目多于两个表,那么就将vt3和第三个表连接从而计算笛卡尔乘积,生成虚拟表,也就是重复1-3步骤,最终得到一个新的虚拟表 vt3。

WHERE

自下而上、从右到左

将能过滤掉最大数量记录的条件写在WHERE子句的最右

对vt3应用 WHERE 筛选器生成虚拟表vt4。

GROUP BY

从左往右分组

开始使用select中的别名,后面的语句中都可以使用

按GROUP BY子句中的列对vt4中的行分组生成vt5。

如果应用了group by,那么后面的所有步骤都只能得到vt5的列或者是聚合函数。

组函数?

WITH {CUBE | ROLLUP}?

把超组(supergroups)插入vt6,生成vt6 

having筛选器是第一个也是为唯一一个应用到已分组数据的筛选器。

使用聚组函数返回的是每组的汇总信息

HAVING

很耗资源,尽量少用,未涉及分组统计的过滤请使用WHERE

对vt6应用HAVING筛选器生成vt7

HAVING 只有在检索出所有记录之后才对结果集进行过滤,需要排序、总计等操作,因此很耗资源。

 (非Oracle中)ON、WHERE、HAVING三者皆用于过滤:

ON最先执行,用于多表联接过滤,速度最快,

WHERE然后执行,用于分组统计前过滤行(不能使用组函数),速度其次。

HAVING 最后执行,用于分组统计后过滤分组(常使用组函数),速度最慢。

on和where、HAVING的最大区别在于,在on执行后在下一步outer join中还可以把移除的行再次添加回来,而where、HAVING的移除是最终的。

SELECT

少用*号,尽量取字段名称。

ORACLE 在解析的过程中, 通过查询数据字典将*号依次转换成所有的列名, 消耗时间。

SQL语句用大写,因为Oracle总要把小写转大写再执行

选择指定的列生成vt8

DISTINCT

将重复的行从vt8中去除产生vt9。

事实上如果应用了group by子句那么distinct是多余的。

UNION

ORDER BY

从左到右排序,消耗资源。

将vt9的行按order by子句中的列列表排序生成一个游标vc10。

此时返回的一个游标,而不是虚拟表。sql是基于集合的理论的,集合不会预先对他的行排序,它只是成员的逻辑集合,成员的顺序是无关紧要的。对表进行排序的查询可以返回一个对象,这个对象包含特定的物理顺序的逻辑组织。这个对象就叫游标。正因为返回值是游标,那么使用order by 子句查询不能应用于表表达式。排序是很需要成本的,除非你必须要排序,否则最好不要指定order by,最后,在这一步中是第一个也是唯一一个可以使用select列表中别名的步骤。

LIMIT

取出指定行的记录,产生虚拟表VT11, 并将结果返回

TOP

从vc10的开始处选择指定数量或比例的行生成vt11 并返回调用者

    • DQL数据查询语言)

单表查询(简单查询)

MySQL

Oracle

基本查询(select)

所有字段

select * from student;

指定字段

select id,name,gender from student;

指定字段别名

select id [as] '编号', name [as] '姓名' from student;

添加常量列

select id, name, '一年级 ' AS '年级' from student;

合并列

+、-、*、/

select id,name,(servlet+jsp) [AS] '总成绩' from student;

注意:

  1. 非数值类型列合并,其值会被忽略
  2. 合并列有Null值的,结果也为Null。

+、-、*、/、||

select id,name,(servlet+jsp) [AS] '总成绩' from student;

select '工号为:' || empno || ' 的员工的姓名为:'|| ename from emp;

去除重复记录

select DISTINCT address from student;

或者使用函数写法:

select DISTINCT(address) from student;

条件查询(where)

注:MySql数据库字符集为..._general_ci时,查询条件匹配是对大小写不敏感的,如果需要对大小写敏感,可将字符集设为..._bin,或者在查询语句中的条件字段前加入关键字binary如:

SELECT * FROM 表 WHERE binary 字段 = ?;

比较条件

>  <  >=  <=  =  <>或!=  

between...and闭区间,等价于>=且<=  in(...)

select * from student where servlet>70;

select * from student where jsp>=75 AND jsp<=90;

select * from student where jsp BETWEEN 75 AND 90;

select * from student where gender<>'男'; (非标准写法:!=)

注意:使用大于、小于、不等于和不在过滤时会把null值也过滤。如不想过滤null值,可以如:

...where a <> 'A' or a is null;

...where ifnull(a,'default') <> 'A';

...where a <> 'A' or a is null;

...where nvl(a,'default') <> 'A';

判空条件

is null / is not null / ='' / <>''(MySQL中 ''不等于null)

select * from student where address IS NULL;

select * from student where address='';

--查询无地址的学生(包括null和空字符串):

select * from student where address IS NULL OR address='';

--查询有地址的学生(不包括null和空字符串):

select * from student where address IS NOT NULL AND address<>'';

is null / is not null(Oracle中''等于null)

注意:WHERE 1 = NULL、WHERE NOT(1 = NULL)、WHERE 1 NOT IN (1,NULL)、WHERE 1 NOT IN (2,NULL)、WHERE null in (null)、WHERE null = null都不返回任何数据(把null看作未知数来判断,无法判断的条件都为false)

模糊条件

like

-- % : 表示任意个字符

-- _ : 一个表示一个字符

escape:指定转义字符

select * from student where name LIKE '李%';

select * from student where name LIKE '李_';

select * from student where name LIKE '李__';

select * from student where name LIKE '%\_%'; --查找名字含“_”的学生。

select * from student where name LIKE '%/%%' escape '/'; --查找名字含“%”的学生。

注:“\”为默认的转义字符,在MySQL中,默认的“\”不要再重申escape '\',在Oracle中,默认的“\”可以再重申escape '\'

逻辑条件

and或&&  or或||  not()或! 优先级(执行顺序):NOT >AND >OR

select * from student where gender='男'AND address='北京';

select * from student where address=北京’ OR address='上海'; 

select * from student where not(age > 18);

分组查询(group by)

分组后统计(先分组(GROUP BY),后统计(COUNT(*)每组数量):

select gender,COUNT(*) from student GROUP BY gender;

分组后筛选(先分组后统计再筛选(having)):

select gender,COUNT(*) from student where grade='一年级' GROUP BY gender HAVING COUNT(*)>2;

注意:分组前条件用where,分组后条件用having。

使用分组查询的规则:出现在select列表中的字段,如果不是组函数,那么就必须出现在group by 子句中,否则就会出现错误:not a GROUP BY expression

在mysql 5.7.5之前的版本,ONLY_FULL_GROUP_BY默认不开启,即mysql对标准SQL的扩展可以生效:

    允许having子句、select子句、order by子句中的非聚合列不出现在group by子句中,此时mysql会对这些列随机选择值。效果等同于使用ANY_VALUE()。

    允许在having子句中使用select子句中的别名

查看是否开启了only_full_group_by规则校验:

全局查看:SELECT @@GLOBAL.sql_mode;

会话查看:SELECT @@SESSION.sql_mode;

如果查询结果中可以看到ONLY_FULL_GROUP_BY这个关键字,说明开启了规则校验,如果要关闭only_full_group_by规则校验,将上面的查询结果中删去ONLY_FULL_GROUP_BY后的值设回参数后重启mysql服务:

全局设置:set @@GLOBAL.sql_mode='<删去ONLY_FULL_GROUP_BY后的值>';

会话设置:set @@SESSION.sql_mode='<删去ONLY_FULL_GROUP_BY后的值>';

查询排序(order by)

asc顺序desc倒序

select * from student; --无order时默认按插入顺序排序

select * from student ORDER BY id [ASC]; -- 默认正序可不加ASC

select * from student ORDER BY id DESC;-- 反序

select * from student ORDER BY servlet [ASC],jsp DESC; -- 多个排序条件

分页查询

limit 起始行,查询几行-- 行号从0开始

嵌套子查询 --行号从1开始

select * from student LIMIT (当前页-1)*每页条数, 每页条数;

查询第1,2条记录(第1页的数据):

select * from student LIMIT 0,2;

查询第3,4条记录(第2页的数据):

select * from student LIMIT 2,2;

查询第5,6条记录(无记录不显示):

select * from student LIMIT 4,2;

开始索引:(当前页-1)*每页条数+1

结束索引:每页条数*页数

若不要排序,先取1到结束索引的行,再取开始索引到结束索引的行(二层嵌套)

SELECT *

FROM (SELECT ROWNUM R,EMP.*

      FROM EMP

      WHERE ROWNUM < 7)

WHERE R > 3;

若要排序,先排序再加伪列并分页(三层嵌套)

注:Oracle嵌套分页查询为什么要先判断小于结束索引再判断大于开始索引?

例子:select * from (select ta.*,rownum r from (select * from A) ta where rownum < 10) where r > 5

(其实两层就可以,不过,两层嵌套查询不会用到oracle的外层条件内推机制,效率慢了点)

先判断大于开始索引为什么不好使?

因为检索和操作rownum的时候游标的指向必须从1开始,不能跳过;

rownum是查询过后才按顺序排的,假如你的条件是rownum>1;那么返回数据的第一条(rownum是1)就不符合要求了,然后第二条数据变成了现在的第一条,结果这一条rownum又变成1了又不符合要求了,以此类推,就没有返回结果。

查询合并

第一个select语句的列或别名作为结果标题

并集

union all(并集不去重)、union(并集且去重)

select * from emp where deptno=10

union

select * from emp where deptno=20;

交集

intersect取出结果集中共有的记录

select ename,sal from emp where sal between 1000 and 2000

intersect

select ename,sal from emp where sal between 1500 and 2500;

减集

minus取出第一个查询有,第二个查询中没有的记录

select ename,sal from emp where sal between 1000 and 2000

minus

select ename,sal from emp where sal between 1500 and 2500;

运算符优先级

运算符类型

运算符

括号

()

一元运算符

+、-、NOT、!、~

二元运算符

*、/、%、DIV、MOD

加减运算符

+、-

比较运算符

IS[NOT]NULL, LIKE, [NOT]IN、=、>、<、>=、<=、<>、!=、<=>、[NOT] BETWEEN

逻辑运算符

AND、OR

OR的优先级最低,括号的优先级最高。

多表查询(连接查询)

交叉连接查询(笛卡尔积,产生笛卡尔积的原因是没有足够的连接条件)

select empName,deptName from employee,dept

内连接查询:只显示两表相同数据

隐式内连接查询:select empName,deptName from employee,dept where employee.deptId=dept.id;

显式内连接查询:select empName,deptName from employee [INNER] JOIN dept ON employee.deptId=dept.id;

又或使用别名:SELECT [e.]empName,[d.]deptName from employee [as] e [INNER] JOIN dept [as] d ON e.deptId=d.id;

左[外]连接查询:用左表数据匹配右表数据,左表数据完全显示,右表无匹配数据则显示null。

通用:select d.deptName,e.empName from dept d LEFT [OUTER] JOIN employee e ON d.id=e.deptId;

Oracle既可用上面通用写法,亦可用下面专用写法:

左外连接(+)号写在等号右边,右外连接(+)号写在等号左边。(+)号在哪张表,哪张表就可能要补null

--按部门统计员工的人数,要求显示部门号、部门名称、和部门人数,部门下没有人的也将显示

select d.deptno,d.dname,count(e.empno) from dept d,emp e where d.deptno=e.deptno(+) group by d.deptno,d.dname;

右[外]连接查询: 与左[外]连接查询相对。

select e.empName,d.deptName from employee e RIGHT [OUTER] JOIN dept d ON d.id=e.deptId;

全连接查询: full join。左右表数据相互匹配,两表数据完全显示,对应表无匹配数据则显示null。

自连接查询:虚拟出两张表,一般是该表的字段之间存在上下级关系

--查询员工及其上司

select e.empName,b.empName from employee e LEFT [OUTER] JOIN employee b ON e.bossId=b.id;

子查询(嵌套查询)

  1. 将子查询放入括号中,可以将子查询看作一张新表
  2. group by后不能使用子查询;select、from、where后面都可以使用子查询。

--select后面的子查询

select (select dname from dept where deptno=10),ename from emp where deptno=10;

--from后面的子查询

select * from (select ename,sal from emp);

--where后面的子查询;查询工资比10号部门员工中任意一个员工的工资低的员工信息

select * from emp where sal < (select min(sal) from emp where deptno=10);

  1. 标量子查询

标量子查询的结果为单个值

  1. 列子查询

列子查询返回的结果是一列,对列子查询常用的操作符有in、not in、any或some、all

-- 比某部门所有人工资都高的员工信息

select * from emp where salary > all (select salary from emp where dept_id = 1)

  1. 行子查询

列子查询返回的结果是一行

--与张三的职位和薪资相同的员工信息

select * from emp where (job, salary) = (select job, salary from emp where name = ('张三') limit 1)

  1. 表子查询

表子查询返回的结果为多行多列。

--与张三、李四的职位和薪资相同的员工信息

select * from emp where (job, salary) in (select job, salary from emp where name in ('张三', '李四'))

聚合函数(又称聚组函数、组函数)

 sum() avg() max() min() count()

select SUM(servlet) AS 'servlet的总成绩', SUM(jsp) AS 'jsp的总成绩' from student; --sum仅对数值起作用,否则会报错。

select AVG(servlet) AS 'servlet的平均分', AVG(jsp) AS 'jsp的平均分' from student;

select MAX(servlet) AS '最高分' from student;

select MIN(servlet) AS '最低分' from student;

select COUNT(*) AS '多少学生' from student;

select COUNT(id) from student; --效率略高,但不要使用含null值的列。

注意:null值不参与任何聚合函数的运算

数学函数

函数

返回类型

MySQL函数

Oracle函数

ABS (number2 )

绝对值

BIN (decimal_number )

十进制转二进制

向上取整

CEIL(number)

CEILING(number)

向下取整

FLOOR(number)

CONV(number2,from_base,to_base)

进制转换

FORMAT (number,decimal_places )

保留小数位数

HEX (DecimalNumber )

转十六进制

LEAST (number , number2 [,..])

求最小值

MOD(numerator ,denominator)

求余(取模)

RAND([seed])

RAND([seed])

四舍五入

round(x[,y])

返回对x四舍五入保留y位小数(若y大于0)或y位0(若y小于0)的值。

y默认为0。

若y为小数,先对y四舍五入。

round(x[,y])

【功能】返回四舍五入后的值

【参数】x,y,数字型表达式,如果y不为整数则截取y整数部分,如果y>0则四舍五入为y位小数,如果y小于0则四舍五入到小数点向左第y位。

【返回】数字

【示例】

select round(5555.6666,2.1),round(5555.6666,-2.6),round(5555.6666) from dual;

返回:   5555.67     ,    5600    ,    5556

trunc(x[,y])

数字

【功能】返回x按精度y截取后的值

【参数】x,y,数字型表达式,如果y不为整数则截取y整数部分,如果y>0则截取到y位小数,如果y小于0则截取到小数点向左第y位,小数前其它数据用0表示。

【示例】

select trunc(5555.66666,2.1),

trunc(5555.66666,-2.6),trunc(5555.033333)  from dual;

返回:5555.66                    5500               5555

字符串函数

功能

返回类型

MySQL函数

Oracle函数

CHARSET(str)

返回字串字符集

连接字串

CONCAT(string[,...])

INSTR (string ,substring )

返回substring在string中出现的位置,没有返回0

转换成大写

UPPER(string)或UCASE(string)

转换成小写

LOWER(string)或LCASE(string)

LEFT (string2 ,length )

从string2中的左边起取length个字符

LENGTH (string )

数值型

返回string长度。【说明】多字节符(汉字、全角符等),按1个字符计算

STRCMP (string1 ,string2 )

逐字符比较两字串大小,

去除空格

LTRIM(str):去除左端空格

RTRIM(str):去除右端空格

TRIM(str):去除两端空格

字符串填充

字符型

左填充:LPAD(str,n,pad)

右填充:RPAD(str,n,pad)

【说明】返回用pad填充str的左(右)边,直到长度为n时为止的结果,如果str长度大于n,则返回str的前n个字符

替换字符串

字符型

REPLACE(str ,search_str ,replace_str )

在str中用replace_str替换search_str

REPLACE(c1,c2[,c3])

将c1中的c2替换成c3,若不指定c3,则删除c1中的c2

截取字符串

字符型

SUBSTR(str,start[,length])

SUBSTRING(str,start[,length])

SUBSTR(str,start[,length])

【说明】下标从1开始。若不指定length,则从start截至末尾

日期时间函数

返回类型

MySQL函数

Oracle函数

返回追加后的日期(日期时间)。

ADDTIME(原date ,要追加的time_interval)

【示例】select addtime('16:58:30','01:01:01');

select addtime(time,'1 1-1 10:09:09') from student; --时间戳上增加,注意年后没有

DATE_ADD (date2 , INTERVAL d_value d_type)

在date2中加上日期或时间

【示例】select date_add(entry_date,INTERVAL 2 year) from student; --增加两年

DATE_SUB (date2 , INTERVAL d_value d_type)

在date2上减去一个时间

add_months(原日期,要追加的月数)

【示例】select sysdate,add_months(sysdate,3) from dual;

返回当前日期时间(时间戳)。

日期时间

CURRENT_TIMESTAMP ():返回当前时间戳

now():返回当前日期时间(now和括号之间不能有空格)

sysdate返回当前日期时间

【说明】:没有参数,没有括号

【示例】select sysdate from dual;

当前日期和时间

NOW()

当前日期

CURDATE()或CURRENT_DATE()

当前时间

CURTIME()或CURRENT_TIME()

DATE(时间戳)

返回时间戳的日期部分

计算日期差(月份差)

DATEDIFF (date1 ,date2)

两个日期差

months_between(d1,d2)

【功能】:返回月数差(数)。

【参数】:d1,d2 日期型

【返回】:数。如果d1>d2,则返回正数;如果d1<d2,则返回负

【示例】

select sysdate,

months_between(sysdate,to_date('2015-01-01','YYYY-MM-DD')) 2015元旦,

months_between(sysdate,to_date('2016-01-01','YYYY-MM-DD')) 2016元旦 from dual;

当前时间

YEAR(日期时间)

MONTH(日期时间)

DATE(日期时间)

返回年

返回月

返回日期

提取数字

extract(c1 from d1)

【功能】:日期/时间d1中,参数(c1)的值

【参数】:d1日期型(date)/日期时间型(timestamp),c1为字符型(参数)

【参数表】:c1对应的参数表详见示例

【返回】:字符

【示例】

select 

extract(YEAR from timestamp '2015-5-1 12:26:18 ' ) ,

extract(MONTH from timestamp '2015-5-1 12:26:18 ' ) ,

extract(DAY from timestamp '2015-1-5 12:26:18 ' ) ,

extract(hour from timestamp '2015-5-1 12:26:18 ' ) ,

extract(minute from timestamp '2015-5-1 12:26:18' ) ,

extract(second from timestamp '2015-5-1 12:26:18 ' )  

from dual;

返回:

2015 5 5 12 26 18

【示例】

select extract (YEAR from date '2015-5-1' ) from dual; 

返回:2015

【示例】

select sysdate 当前日期,

extract(YEAR from sysdate ) ,

extract(MONTH from sysdate ) ,

extract(DAY from sysdate ) 

from dual;

--如下语句也可获取年份、月份等

select to_number(to_char(sysdate,'yyyy')) from dual;

转换函数

Oracle

TO_CHAR(x[[,c2],C3])

【功能】将日期或数据转换为char数据类型

【参数】

       x是一个date或number数据类型。

       c2为格式参数

       c3为NLS设置参数

【返回】varchar2字符型

【示例】

select to_char(sysdate,'YYYY_MM_DD HH24:MI:SS') FROM dual;

--返回:2017_11_19 13:20:20

select to_char(1210.7, '$9,999.00') FROM dual;

--返回:$1,210.70

TO_DATE(X[,c2[,c3]])

【功能】将字符串X转化为日期型

【参数】c2,c3,字符型,参照to_char()

       x是字符串。

       c2为格式参数

       c3为NLS设置参数

【返回】字符串

如果x格式为日期型(date)格式时,则相同表达:date x

如果x格式为日期时间型(timestamp)格式时,则相同表达:timestamp x

【示例】

select to_date('201212','yyyymm'),

to_date('2012.12.20','yyyy.mm.dd'),

(date '2012-12-20') XXdate, 

to_date('2012-12-20 12:31:30','yyyy-mm-dd hh24:mi:ss'),

to_timestamp('2012-12-20 12:31:30','yyyy-mm-dd hh24:mi:ss'),

(timestamp '2012-12-20 12:31:30') XXtimestamp

from dual;

TO_NUMBER(X[[,c2],c3])

【功能】将字符串X转化为数字型

【参数】c2,c3,字符型

【返回】数字串

【示例】

select TO_NUMBER('201212') + 3,TO_NUMBER('450.05') + 1 from dual;

select '201212' + 3 from dual;

逻辑判断函数

功能

MySQL函数

Oracle函数

判真

IF(exp, value1, value2)

如果exp为true,返回value1,否则返回value2

判空

IFNULL(value1, value2)

NVL(value1, value2)

NVL2(exp, exp非空时返回值, exp为空时返回值)。

如果value1非NULL,返回此value1,否则返回value2

流程判断

CASE WHEN exp1 THEN res1 ... [ELSE default] END

若exp为真,返回对应res,否则返回default(有else default时)或null

CASE exp WHEN test1 THEN res1 ... [ELSE default] END

若exp等于test,返回对应res,否则返回default(有else default时)或null

decode(exp, test1, res1,...,...,default)

Oracle分析函数

分析函数中了解rank()/dense_rank()/row_number()的使用:分类并在类的内部排序

--查询部门的员工工种情况,并在部门内重新进行排序;PARTITION BY类似group by,根据ORDER BY排序字段的值重新由1开始排序。

--RANK 使用相同排序排名一样,后继数据空出排名;即有2个排序为1的,那么接下来的排序号则为3

select deptno,ename,job,rank() over(partition by deptno order by job) as myRank from emp e;

--DENSE_RANK使用,使用相同排序排名一样,后继数据不空出排名;即有2个排序为1的,那么接下来的排序号则为2

select deptno,ename,job,dense_rank() over(partition by deptno order by job) as myDenseRank from emp e;

--ROW_NUMBER使用,不管排名是否一样,都按顺序排名;即有2个排序为1的,那么排序号不会重现重复

select deptno,ename,job,row_number() over(partition by deptno order by job) as myRowNumber from emp e;--如果只想查看某部门的排序,后面加上where deptno=部门号

行转列、列转行

行(长表):

userid

subject

score

001

语文

85

001

数学

90

001

英语

95

002

语文

75

002

数学

80

002

英语

85

003

语文

60

003

数学

65

003

英语

70

003

体育

75

列(宽表):

userid

语文

数学

英语

体育

001

85

90

95

0

002

75

80

85

0

003

60

65

70

75

行转列:

SELECT userid,

SUM(CASE `subject` WHEN '语文' THEN score ELSE 0 END) as '语文',

SUM(CASE `subject` WHEN '数学' THEN score ELSE 0 END) as '数学',

SUM(CASE `subject` WHEN '英语' THEN score ELSE 0 END) as '英语',

SUM(CASE `subject` WHEN '体育' THEN score ELSE 0 END) as '体育'

FROM tb_score

GROUP BY userid

列转行:

SELECT userid, '语文' as subject, `语文` as score  

FROM tb_score  

WHERE `语文` IS NOT NULL  

UNION ALL

SELECT userid, '数学' as subject, `数学` as score  

FROM scoreWide  

WHERE `数学` IS NOT NULL  

UNION ALL

SELECT userid, '英语' as subject, `英语` as score  

FROM scoreWide  

WHERE `英语` IS NOT NULL   

UNION ALL

SELECT userid, '体育' as subject, `体育` as score  

FROM scoreWide  

WHERE `体育` IS NOT NULL   

    • 管理TCL事务控制语言

事务的提交比较简单;直接在执行DML语句后进行提交即可,如果不提交事务则刚刚通过DML语句进行修改的内容还未保存到数据库中,只在当前用户的连接会话中有效。要永久变更数据需要显示地执行提交、回滚或者退出当前会话(如退出sqlplus)。

方式一

方式二

-- 查看事务是否自动提交

select @@autocommit;

-- 修改事务为手动提交

set @@autocommit = 0;

-- 修改数据,修改暂存在当前会话缓存中

update user set age = 30 where name = '李四';

-- 查询当前会话缓存中数据(已修改未入库)

select * from user;

-- 手动回滚事务,撤回当前会话缓存中的修改

rollback;

-- 手动提交事务,将当前会话缓存中的修改提交到数据库

commit;

-- 修改事务为自动提交

set @@autocommit = 1;

-- 手动开启事务

begin; -- 或者start transaction;

-- 修改数据,修改暂存在当前会话缓存中

update user set age = 30 where name = '李四';

-- 设置一个保存点a

savepoint a;

-- 再次修改数据,修改暂存在当前会话缓存中

update user set age = 20 where name = '李四';

-- 查询当前会话缓存中数据(已修改未入库)

select * from user;

-- 手动回滚事务到保存点a,撤回当前会话缓存中保存点a后的修改

rollback to a;

-- 手动回滚事务,撤回当前会话缓存中的所有修改

rollback;

-- 手动提交事务,将当前会话缓存中的修改提交到数据库,所有保存点将丢弃

commit;

mysql备份恢复导入

备份(终端下):mysqldump --default-character-set=编码 -u 用户名 -p 数据库名 > sql文件完整路径

default-character-set=charset 指定导出数据时采用何种字符集,如果数据表不是采用默认的 latin1 字符集的话,那么导出时必须指定该选项,否则再次导入数据后将产生乱码问题。

模板:mysqldump --default-character-set=utf8 -uroot -p bankingsystem > E:\bankingsystem20180605.sql

恢复(终端下):mysql -u 用户名 -p密码 -- default-character-set=编码如utf8 数据库名 < sql文件完整路径

导入(mysql下):source sql文件完整路径 (要先登录)

Oracle数据备份

--全表备份

exp itcast/itcast@orcl file=d:\database\oracle_data\itcast.dmp full=y;

--指定表备份

exp itcast/itcast@orcl file=d:\database\oracle_data\itcast_emp_dept.dmp tables=(emp,dept);

【说明】full:完整导出数据库,一般使用system具有管理员权限的用户在命令行下进行操作。

Oracle数据恢复

--全表恢复

imp itcast/itcast@orcl ignore=y file=d:\database\oracle_data\itcast.dmp  full=y;

--指定表恢复

imp itcast/itcast@orcl ignore=y file=d:\database\oracle_data\itcast_emp_dept.dmp tables=(emp,dept);

【说明】ignore:忽略创建错误

    1. 架构

连接层:

客户端连接器如Native C API、JDBC、ODBC、.NET、PHP、Perl、Python、Ruby、Cobol

连接池:授权认证、连接限制、检查内存

服务层(Server):

连接器:管理连接、权限验证

查询缓存:命中则直接返回结果

分析器:词法分析、语法分析

优化器:执行计划生成,索引选择

执行器:操作引擎,返回结果

引擎层(可插拔存储引擎):

InnoDB

MyISAM

其他...

存储层:

系统文件

文件和日志:Redo、Undo、Data、Index、Binary、Error、Query、Slow

    1. 存储引擎

存储引擎就是存储数据、建立索引、更新/查询数据的实现方式。

存储引擎是基于表的(同库中不同表可选择不同存储引擎),所以又称为表类型(即存储和操作此表的类型)。

MySQL5.5版本开始的存储引擎默认为InnoDB

存储引擎相关语法:

查看当前数据库存储引擎信息:show engines;

建表时指定存储引擎:create table 表名(字段信息等) ENGINE=存储引擎类型 DEFAULT CHARSET=utf8mb4 COMMENT='注释';

修改表的引擎:alter table 表名 engine=存储引擎类型;

查看指定表的状态(含存储引擎信息):show table status like '表名';

主要存储引擎对比

InnoDB(5.5起默认):支持事务,行锁,外键,是聚集索引,不保存表记录数,5.7起支持全文索引,查询慢,崩溃恢复易。

MyISAM(5.5前默认):不支持事务,行锁,外键,非聚集索引,保存表记录数,支持全文索引,查询快,崩溃恢复难。

Memory:表数据存内存中,只能将这些表作为临时表或缓存使用,访问速度贼快,默认使用hash索引。

如何选择

  1. 若要支持事务,选择Innodb。
  2. 读多选MyISAM(不如用MongoDB),写多选InnoDB。
  3. 只做临时表或缓存选Memory(不如用Redis)

存储引擎

InnoDB

MyISAM

Memory

存储限制

64TB

事务

支持

-

-

锁机制

行锁

表锁

表锁

索引

支持B+tree索引,5.6版本以后支持全文索引

支持B+tree索引和全文索引

支持B+tree索引和Hash索引

空间使用

-

内存使用

中等

批量插入速度

外键

支持

-

-

表空间文件

文件配置参数:innodb_file_per_table

在MySQL8中innodb_file_per_table默认为ON,在该配置下:

文件名:表名.idb

可在cmd中用ibd2sdi命令查看二进制文件内容(JSON格式):ibd2sdi 表名.idb

存储内容:表结构(frm、sdi)、数据、索引

在MySQL早期版本中innodb_file_per_table默认为OFF,在该配置下:

文件名:表名.frm

存储内容:

一个表对应三个文件:

表名.MYD:存储数据

表名.MYI:存储索引

表名_序号.sdi:存储表结构信息(JSON格式文本文件)

表名.sdi:存储表结构信息

(数据存放在内存中)

逻辑存储结构

TableSpace:表空间

Segment:段

Extent:区(1M,包含64个Page)

Page:页(16K)

Row:行

在Row中:

Trx id:最后一次操作事务的id

Roll Pointer

col1、col2、...

索引是用来提高查询速度的有序数据结构。

索引的优点:提高查询速度(SELECT)。

索引的缺点:降低更新速度(INSERT、UPDATE和DELETE)。因为更新表时,MySQL不仅要保存数据,还要保存索引文件。

索引结构

MySQL的索引在引擎层实现,不同的引擎有不同的索引结构,主要的索引结构有:

索引结构

特点

支持的引擎

B+Tree索引

最常见的索引

大部分引擎都支持

Hash索引

支持精确查找(=、in)

不支持范围查找(between、>、<...)和排序

Memory引擎支持

InnoDB引擎具有自适应Hash功能

R-Tree索引

空间索引主要用于地理空间数据类型。

MyISAM引擎支持

Full-Text索引

全文索引是一种倒排索引,类似于Lucene、Solr、ES

InnoDB(5.6版本起)和MyISAM引擎支持

数据结构可视化演示:Data Structure Visualization

(面试重点)InnoDB引擎为什么选择B+树做索引:

二叉树做索引缺点:顺序插入时形成单向链表(树高增加),只有两个子节点导致树高增加。

红黑树做索引缺点:解决了单向链表问题,未解决只有两个子节点问题。

B树做索引缺点:B树非叶子节点也存储数据,导致一页(16K)中存储的键值和指针减少,树高增加。

Hash做索引缺点:不支持范围查找和排序。

B树(多路平衡查找树)特点

  1. 最大度数(max-degree,子节点个数)为n(n阶)的B树,每个节点最多存n-1个key和n个指针(key和指针间隔排列,两头为指针),每个指针指向一个子节点。
  2. 数据存储在每个节点的key下。

B+树特点:

  1. 所有key会出现在叶子节点并形成单向链表(非叶子节点上的key会在叶子节点重现)。
  2. 数据存储在叶子节点的key下,非叶子节点只存储key。

MySQL的B+树索引在B+树的基础上将叶子节点的单向链表升级为双向链表。

当B+树树高为3时,

InnoDB中指针占6字节,设主键的类型是bigint占8字节,一组就是14字节。一个非叶子节点(页)最多可存储16*1024/14=1170个指针(1169个key)。假设一条数据的大小是1KB,那么一个叶子结点可以存储16条数据,得出两层B+树可以存储1170*16=18720条数据。三层B+树可以存储1170*1170*16=21902400条数据。

索引分类

分类

含义

特点

关键字

主键索引

针对表中主键创建的索引

默认自动创建,只能有一个

PRIMARY

唯一索引

避免列值重复(可为NULL)

可以多个

UNIQUE

常规索引

快速定位数据

可以多个

全文索引

查找文本中的关键词

可以多个

FULLTEXT

InnoDB引擎的索引根据存储形式分类:

分类

含义

特点

聚集索引/聚簇索引/主键索引

Clustered Index

将数据与索引一起存储,叶子节点存储行数据。

有且只有一个。如果没有主键,就默认在第一个非空唯一键上建立聚集索引;如果又没有,就会创建一个隐藏的row-id作为聚集索引。

二级索引/非聚集索引/非聚簇索引/普通索引/辅助索引

Secondary Index

将数据与索引分开存储,叶子节点存储主键值。

可以多个

MySQL的MyISAM引擎的索引叶子节点存储记录指针。

最左匹配原则:where条件从联合索引最左列开始按顺序使用,直到某列不被使用或用作不含等值匹配的范围查询(>或<),该列之后的列将不被使用。(参考:https://zhuanlan.zhihu.com/p/573138586)

不含等值匹配的范围查询有:>、<、like前模糊;

含有等值匹配的范围查询有:>=、<=、between、like后模糊。

比如联合索引(name,age,gender):

where name = '张三' and age = 20 and gender = 0:可以使用联合索引中的name、age、gender

where age = 20 and gender = 0:无法使用联合索引,因为where条件跳过了最左边的name

where name = '张三' and gender = 0:可以使用联合索引中的name,无法使用gender,因为where条件跳过了gender前面的age

where name = '张三' and age > 20 and gender = 0:可以使用联合索引中的name、age,无法使用gender,因为前面的age范围查询不含等号

where name = '张三' and age >= 20 and gender = 0:可以使用联合索引中的name、age、gender,age范围查询含等号,不影响后面的gender

where name like '张%' and age = 20 and gender = 0:可以使用联合索引中的name、age、gender,name模糊查询含

考虑到存储空间的问题,还请根据业务需求,将查找频繁的数据进行靠左创建索引。

覆盖索引(Using index):

如果要返回的数据都可在使用的索引中取得(执行计划Extra中为 Using index),则称为覆盖索引,这时不用回表查询,直接返回结果,减少IO。

索引下推(Index Condition Pushdown,ICP)

通过访问索引元组并首先测试它们来确定是否读取完整的表行来读取表。通过这种方式,索引信息用于推迟(“下推”)读取整个表行

假设联合索引为(a, b),

查询语句为SELECT * FROM 表WHERE a='valueA' AND b LIKE '%valueB%';

该查询用到联合索引的a字段,

如果没有索引下推(5.6前),每次匹配到符合a的索引元组,会回表查询出b进行过滤(不使用索引元组中的b)。

有了索引下推后,每次匹配到符合a的索引元组,会先根据索引元组中的b进行过滤,从而减少回表次数。

当执行计划的Extra列值为Using index condition时,表示使用了索引下推。

无法下推的情形:

引用子查询的条件无法下推。

引用存储函数的条件无法下推。存储引擎无法调用存储函数。

触发条件无法下推。

(MySQL 8.0.30 及更高版本)条件无法下推到包含对系统变量的引用的派生表。

索引语法

创建索引

一般创建(必须指定索引名,关键字INDEX必需且不能用KEY替换,无法创建主键索引):

CREATE INDEX index_name ON 表名(列名)

CREATE UNIQUE INDEX index_name ON 表名(列名)

CREATE FULLTEXT INDEX index_name ON 表名(列名);

修改表结构添加索引可不指定索引名,可创建主键索引

ALTER TABLE `table_name` ADD PRIMARY KEY (`column`) # 主键索引名固定为“PRIMARY”,无法修改

ALTER TABLE `table_name` ADD INDEX|KEY [index_name] (`column`)

ALTER TABLE `table_name` ADD UNIQUE [INDEX|KEY] [index_name] (`column`)

ALTER TABLE `table_name` ADD FULLTEXT [INDEX|KEY] [index_name] (`column`)

创建表的时候直接指定(可不指定索引名,可创建主键索引)

CREATE TABLE user(

id BIGINT,

username VARCHAR(16) NOT NULL,

age TINYINT,

description VARCHAR(16),

introduction TEXT,

PRIMARY KEY (id),

INDEX|KEY [index_name] (列名),

UNIQUE [INDEX|KEY] [index_name] (列名)

FULLTEXT [INDEX|KEY] [index_name] (列名)

);

注:

  1. 创建唯一索引和常规索引时,列如果是CHAR,VARCHAR类型,可在列名后加上要索引的长度(sub_part,需小于等于字段长度);如果是BLOB和TEXT类型,则必须指定。例如:ALTER TABLE `user` ADD UNIQUE idx_introduction (introduction(10))
  2. 索引创建时,默认为升序,可指定为降序,例如:create index idx_user_age_phone on t_user (age asc, phone desc);
  3. 索引名建议为“idx_表名_列名”或“表名_列名_idx”

查看索引

SHOW INDEX FROM 表名;

删除索引

DROP INDEX index_name ON 表名;

ALTER TABLE user DROP PRIMARY KEY;

ALTER TABLE user DROP INDEX index_name;

    1. 性能分析

查看SQL执行频率:show global status like 'Com_______'

慢查询日志

查看开启情况:show variables like 'slow_query_log'

查看慢查询判定秒数:show variables like 'long_query_time'

临时开启慢查询日志

set GLOBAL slow_query_log = ON

set long_query_time = 2

永久开启慢查询日志:在MySQL配置文件(/etc/my.cnf)中开启之(重启MySQL生效):

# 开启MySQL慢查询日志

slow_query_log=1

# 设置慢查询判定秒数,超过则为慢查询

long_query_time=2

慢查询日志文件路径:/var/lib/mysql/<主机名>-slow.log

慢查询日志记录内容例子:

# Time: 2023-06-09T11:44:07.716968Z

# User@Host: root[root] @  [192.168.56.1]  Id:     5

# Query_time: 0.006333  Lock_time: 0.000109 Rows_sent: 200  Rows_examined: 1427

SET timestamp=1686311047;

/* ApplicationName=DBeaver 23.0.1 - SQLEditor <pms.sql> */ select a.*,b.* from pms_category a, pms_category b

profile详情

查看数据库是否支持profile详情:select @@have_profiling

查看profiling是否开启:select @@profiling

开启profiling:set profiling = 1

查看本会话中sql耗时:show profiles

设置profile条数(最大100条):set profiling_history_size = 100

查看具体sql执行详情:show profile [cpu] for query <query_id>

explain执行计划

语法

EXPLAIN|DESC SELECT ……

EXPLAIN EXTENDED SELECT ……;

SHOW WARNINGS

(mysql8不再支持)

先运行EXPLAIN EXTENDED将执行计划“反编译”

再运行SHOW WARNINGS可得到被MySQL优化器优化后的查询语句

EXPLAIN PARTITIONS SELECT ……

用于分区表的EXPLAIN

EXPLAIN列(红色加粗重点关注)

EXPLAIN列

含义

说明

id

子句操作顺序

id不同,执行顺序从大到小

id相同,执行顺序从上到下

select_type

select的类型

SIMPLE:简单查询,不含关联查询、子查询或UNION

PRIMARY:主查询,即最外层的查询

SUBQUERY:子查询

DERIVED:在FROM列表中包含的子查询被标记为DERIVED(衍生)

UNION:UNION中的第二个及以后的查询;若UNION包含在 FROM子句的子查询中,外层SELECT将被标记为:DERIVED

UNION RESULT:从UNION表获取结果的SELECT

table

子句查询的表

partitions

type

连接类型

效率从高到低:

null:查询时不用访问表或索引,如:select 1;

system:查询的表只有一行且命中,这是const类型的特殊情况

const:命中主键索引或唯一索引,比较值是一个常量。

eq_ref:关联查询命中主键索引或非空唯一索引,比较值是关联表对应列。

ref:非唯一索引扫描(包括唯一联合索引的非唯一前缀),返回匹配某个单独值的所有行。

range:索引范围查找(between、>、<、>=、<=、like前缀匹配、in?)

index:遍历索引树

index_merge:AND或OR连接的条件,对多个索引分别进行条件扫描,然后合并结果(intersects/unions/unions-of-intersections)。其中index intersect merge可以优化为复合索引

all:全表扫描

possible_keys

可能用到的索引

注:查询中若使用了覆盖索引,则该索引仅出现在key列表中

key

实际使用的索引

可用USE|FORCE INDEX(indexname)来强制使用某个索引

可用IGNORE INDEX(indexname)来强制忽略某个索引

key_len

使用的索引字节长度

索引字段的最大可能字节长度,并非实际使用字节长度,即key_len是根据表定义计算而得,key_len主要是为了让我们区分某个使用联合索引的查询具体用了几个索引列。

计算方式:

1)数字类型:tinyint=1,int=4,bigint=8。

2)字符串类型:

字符数 * 字符集单位字节长度(latin1=1,gbk=2,utf8=3,utf8mb4=4)

若为变长类型VARCHAR,还需要再加2字节来存储该变长列的实际长度

3)日期时间类型:

若不使用小数秒,YEAR=1,DATE=3,TIME=3,DATETIME=5,TIMESTAMP=4;

若使用小数秒,需再加上小数秒字节长度(1~2位=1,3~4位=2,5~6位=3)

  1. 若索引列允许NULL,还需要再加1字节

ref

匹配条件

显示索引的哪一列被使用了,即哪些列或常量被用于查找索引列上的值

如果可能的话,是一个常数

rows

预估扫描行数

预估找到所需的记录所需要读取的行数

filtered

返回行数占扫描行数的百分比

越大越好

Extra

额外信息

Distinct:一旦mysql找到了与行相联合匹配的行,就不再搜索了。

Not exists:mysql优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了。

Range checked for each Record(index map:#) :没有找到理想的索引,因此对从前面表中来的每一个行组合,mysql检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一。

Using index:使用了覆盖索引(Covering Index,包含所有满足查询需要的数据的索引)。

Using where:表示MySQL服务器在存储引擎受到记录后进行“后过滤”(Post-filter),如果查询未能使用索引,Using where的作用只是提醒我们MySQL将用where子句来过滤结果集

Using temporary:(需要优化)表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询。

Using filesort:(需要优化)MySQL中无法利用索引完成的排序操作称为“文件排序”。

Where used :使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题。

MySQL执行计划的局限

•EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况

•EXPLAIN不考虑各种Cache

•EXPLAIN不能显示MySQL在执行查询时所作的优化工作

•部分统计信息是估算的,并非精确值

•EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看执行计划

    1. SQL SQL优化面试重点

尽量走索引,避免全表扫描

索引的本质是平衡b+数,是为了方便查询的。

与B-Tree相比,B+Tree有以下不同点:

每个节点的指针上限为2d而不是2d+1;

内节点不存储data,只存储key;

叶子节点不存储指针;

B+Tree比B-Tree更适合实现外存储索引结构

并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,

如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。

索引并不是越多越好,提高查询效率的同时也降低了修改的效率。修改时可能重建索引。一个表的索引数最好不要超过6个。

建表优化

1)尽量使用数字型字段。若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。

这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

  1. 尽量使用varchar代替char。因为首先变长字段存储空间小,可以节省存储空间,其次在一个相对较小的字段内搜索效率显然要高些。

索引设计原则

  1. 对数据量大于100万且查询频繁的表建立索引
  2. 对常作为where、group by、order by操作的列建立索引
  3. 尽量选择区分度高的列建立索引
  4. 尽量建立唯一索引、联合索引
  5. varchar类型长度较大时,可考虑建立前缀索引
  6. 索引列尽量NOT NULL
  7. 要控制索引数量,索引太多影响增删改效率

from子句优化

1)把记录少的表放在FROM子句最后。

on/where子句优化

要义

不推荐

推荐

首先应考虑在 where 及 order by 涉及的列上建立索引。

尽量避免where子句中对字段进行 null 值判断

select id from t where num is null

可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:

select id from t where num=0

把可以过滤掉最大数量记录的条件应写在WHERE子句最后。

WHERE子句内部的执行顺序是自下而上、从右到左。

尽量避免在 where子句中使用!=或<>操作符

or、in筛选值若连续,改用between and,若不连续,改用union all

不连续:

select id from t where num = 10 or num = 20 or num = 30;

select id from t where num in (10,20,30);

连续

select id from t where num in (1,2,3)

不连续:

select id from t where num = 10

union all

select id from t where num = 20

union all

select id from t where num = 30:

连续:

select id from t where num between 1 and 3

用exists代替in;not exists代替 not in

not in 字句将执行一个内部的排序和合并,任何情况下,not in是最低效的,子查询中全表扫描

select num from a where num in(select num from b)

select num from a where exists(select 1 from b where num=a.num)

避免在索引列上使用NOT

因为Oracle服务器遇到NOT后,他就会停止目前的工作,转而执行全表扫描

用 >= 替代 >
 

低效:where num > 3

 --首先定位到num = 3的记录并且扫描到第一个DEPT大于3的记录

高效:WHERE DEPTNO >= 4

--直接跳到第一个num等于4的记录

索引列不要加函数运算

... where num/2=100

... where substring(name,1,3)='abc'

... where num=100*2

... where name like 'abc%'

索引列不要发生类型转换。例如字符串条件值忘加单引号导致条件两边类型不等从而索引列发生隐式类型转换

or分割的条件只要有一个不走索引,那么整体不走索引。解决方法:补全索引或改用union查询

有多条件查询时,尽量建立联合索引而非多个单列索引(MySQL只会选择使用一个单列索引)

联合索引要遵循最左匹配原则

模糊查询尽量遵循最左匹配原则

select id from t where name like '%abc%'

select id from t where name like 'abc%'

尽量使用覆盖索引避免回表查询(不要select *)

覆盖索引(using index):如果要返回的数据都可在使用的索引中取得(执行计划Extra中为 Using index),则称为覆盖索引,这时不用回表查询,直接返回结果,减少IO。

回表查询:通过普通索引查询主键,再通过聚集索引查询记录(执行计划Extra中Using index condition或null)。

当varchar类型长度很大时,可使用前缀索引。前缀长度(sub_part)可根据索引的选择性决定,索引的选择性=select count(distinct substr(索引列, 1, 前缀长度)/count(0) from 表。索引选择性越高则查询效率越高。唯一索引的选择性是1,是最高的选择性。

注意数据分布影响:

1)如果MySQL评估使用索引更慢,就不使用索引。例如:列值多null时is null判断走全表扫描,列值少null时is null判断走索引

当同一列有多个索引时,可使用SQL提示MySQL走或不走哪个索引:

use index(索引名)

ignore index(索引名)

force index(索引名)

不要写一些没有意义的查询

如需要生成一个空表结构

select col1,col2 into #t from t where 1=0

这类代码不会返回任何结果集,但是会消耗系统资源的

应改成这样:

create table #t(...)

关于mysql处理百万级以上的数据时如何提高其查询速度的方法

一、尽量使用索引,避免全表扫描

首先应考虑在 where 及 order by 涉及的列上建立索引

  1. 尽量避免在 where 子句中使用!=或<>操作符
    3、应尽量避免在 where 子句中对字段进行 null 值判断,可以在num上设置默认值0,确保表中num列没有null值,然后以=0代替is null。
    4、尽量避免在 where 子句中使用 or 来连接条件,如:
    select id from t where num=10 or num=20
    可以这样查询:
    select id from t where num=10
    union all
    select id from t where num=20
    5、下面的查询也将导致全表扫描:(不能前置百分号)
    select id from t where name like ‘%c%’
    若要提高效率,可以考虑全文检索。
    6、in 和 not in 也要慎用,否则会导致全表扫描,如:
    select id from t where num in(1,2,3)
    对于连续的数值,能用 between 就不要用 in 了:
    select id from t where num between 1 and 3
    7、如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
    select id from t where num=@num
    可以改为强制查询使用索引:
    select id from t with(index(索引名)) where num=@num
    8、应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
    select id from t where num/2=100
    应改为:
    select id from t where num=100*2
    9、应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
    select id from t where substring(name,1,3)=’abc’                    -name以abc开头的id
    select id from t where datediff(day,createdate,’2005-11-30′)=0     –’2005-11-30′生成的id
    应改为:
    select id from t where name like ‘abc%’
    select id from t where createdate>=’2005-11-30′ and createdate<’2005-12-1′
    10、不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
    11、在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使 用,并且应尽可能的让字段顺序与索引顺序相一致。
    12、不要写一些没有意义的查询,如需要生成一个空表结构:
    select col1,col2 into #t from t where 1=0
    这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
    create table #t(…)
    13、很多时候用 exists 代替 in 是一个好的选择:
    select num from a where num in(select num from b)
    用下面的语句替换:
    select num from a where exists(select 1 from b where num=a.num)

    14、并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段 sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。

    15、索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。

    16.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。

    17、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

    18、尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

    19、任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。

    20、尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。

    21、避免频繁创建和删除临时表,以减少系统表资源的消耗。

    22、临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使 用导出表。

    23、在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

    24、如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

    25、尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

    26、使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

    27、与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时 间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。

    28、在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。

    29、尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

    30、尽量避免大事务操作,提高系统并发能力。

select子句优化

  1. 避免使用*号。ORACLE解析时会将*依次转换成所有列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。

order by优化

Using fileSort :通过表的索引或者全表扫描,读取满足条件的数据行,然后再排序缓冲区sort buffer中,完成排序操作,所有不是通过索引直接返回结果的排序都叫filesort排序

Using index:通过有序索引顺序扫描,直接返回有序数据,不需要额外排序,效率高

    根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则

    尽量使用覆盖索引。查询结果不能通过索引直接返回数据时,会导致Using fileSort

    索引创建时,默认为升序,全部降序查询时,也会走索引

    多字段排序,一个升序,一个降序,需要注意联合索引创建时的规则(指定索引字段顺序)

    排序的联合索引也要满足最左前缀法则,否则导致Using fileSort

    如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小sort_buffer_size(默认256k)

group by优化

    分组操作时,可以通过索引提高效率

    分组操作时,索引的使用也是满足最左前缀法则的

    未走索引时,Extra提示Using temporary

limit优化

分页数越大时,分页效率越慢,因为limit操作会先遍历直到最大行的所有行,然后丢弃起始行之前的所有行。

优化方法:先使用覆盖索引分页查出指定的主键id列表,再自关联查询走聚集索引查出对应数据。(子查询不支持limit,所以用关联查询):

a)select a.目标字段 from 表 a join (select id from 表 where 筛选字段 order by 排序字段 limit 分页信息) b on a.id =b.id

b)筛选字段和排序字段建立复合索引

count()优化

不同引擎count的区别:

MyISAM 引擎把一个表的总行数存在了磁盘上,执行无条件count(*)时直接返回该数,效率很高;但如果是带条件的count或count(可null字段),MyISAM也慢。

InnoDB执行count(*)的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数。

不同计数方式区别:

    count(主键或非null字段):遍历整张表,每一行的该非空字段值出来,返回给服务层,服务层拿到之后计数加1

    count(可null字段):把每一行的字段值取出来,返回给服务层,服务层判断是否为null,不为null则计数加1

    count(数字):不取值,服务层对于返回的每一行,放指定数字进去,直接进行累加

    count(*):不会把字段取出来,做了专门优化,服务层直接按行进行累加

效率:count(*) 约等于 count(1) > count(主键id) > count(字段)

优化方式:

  1. 尽量使用count(*)或count(数字)
  2. (只适用于不带条件的count)如果数据量大导致count耗时,可使用redis存计数器,添加或删除数据的时候,计数器加一或减一

update优化

InnoDB 的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁。例如:

update user set name = '李四' where name = '张三';

如果name字段未建索引,执行期间会导致锁表。

关于临时表和游标

18.避免频繁创建和删除临时表,以减少系统表资源的消耗。

19.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。

20.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

21.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

22.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

23.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

24.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。

在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。

25.尽量避免大事务操作,提高系统并发能力。26.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

  1. 避免对大表进行无条件或无索引的的扫描
  2. UNION-ALL 替换UNION

当SQL语句需要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并, 然后在输出最终结果前进行排序. 如果用UNION ALL替代UNION, 这样排序就不是必要了. 效率会因此得到提高。

  1. 避免使用耗费资源的操作

带有DISTINCT,UNION,MINUS,INTERSECT的SQL语句会启动SQL引擎 执行耗费资源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序. 通常, 带有UNION, MINUS , INTERSECT的SQL语句都可以用其他方式重写。

最后;同样的操作有些时候可以在程序上处理的就程序上处理,毕竟在内存中的执行速度比在硬盘上执行要高非常多。

增删改数据的优化

  1. 清空表时用TRUNCATE替代DELETE
  2. 多用COMMIT以释放回滚点

insert优化

  1. 手动控制事务:避免频繁开启和提交事务
  2. 批量插入:一次批量建议不超过一千条
  3. 主键顺序插入:主键乱序插入导致页分裂
  4. load大批量插入:
    1. 客户端连接服务端时,加上参数--local-infile:mysql --local-infile -u用户名 -p密码
    2. 设置全局参数,开启本地导入文件数据开关:set global local_infile = 1
    3. 执行load导入:load data local infile '文件路径' into table '表名' fields terminated by '字段间分隔符' lines terminated by '换行符'

主键优化

在InnoDB中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(index organized table, IDT)

页分裂:如果向两个页间插入或更新数据将导致页溢出,会把第一个页中超出50%的数据,移动到新开辟的页中,再将新数据放入。并且调整页的顺序(更新链表指针),以及上层非叶子节点的指针。

页合并:当页中标记删除的记录达到Merge_threshold(默认页的50%),Innodb会开始寻找最靠近的页(前或后)看看是否将两个页合并以优化空间使用。

主键设计原则

满足业务需求下,尽量降低主键的长度

插入数据时,尽量选择顺序插入,选择自增主键,无序插入会导致页分裂

尽量不要使用uuid做主健或者是其他自然主键,如身份证号,可能会造成页分裂

业务操作时,避免对主键的修改

优化后的查询过程:

  1. 子查询中使用覆盖索引查出所有符合条件的主键id;
  2. 外层查询通过延迟关联,根据子句获得的主键id批量匹配出对应的数据。

覆盖索引:索引字段覆盖了所有要筛选和查询的字段,此时的索引就是覆盖索引。

Oracle使用plsql查看执行计划

写好一段SQL代码以后,可以通过查看SQL的执行计划,初步预测该SQL在运行时的性能好坏,尤其是在sql调优时,我们可以通过查看执行计划, 来分析sql性能问题,本文简单介绍怎么在plsql中查看SQL语句的执行计划。

方法/步骤

1. 打开PL/SQL Developer软件,请确保plsql能够成功连接到一个oracle数据库。

2. 在PL/SQL Developer中写好一段SQL代码,按F5,或者点击“执行执行计划”图标,PL/SQL Developer会自动打开执行计划窗口,显示该SQL的执行计划。

3. 可以看到窗口上方是sql语句,下方显示执行计划表格。表格的列主要包含描述、用户、对象、成本花费、IO开销等,表格,当然表格列还可以自定义。表格的行包含了查询逻辑的执行顺序和各个步骤信息。

4. 执行计划表格内容的执行顺序是:按照从左至右,从上至下的步骤执行,具体是指执行计划按照层次逐步缩进,从左至右看,缩进最多的那一步最先执行,如果缩进量相同,则按照从上而下的方法判断执行顺序。

5. 通过查看执行计划表格的cost列,即成本花费能够知道哪个步骤花费的成本高,通过查看执行计划表格的行中的objectname列,能够知道是否使用到表中的索引。

6. 本文先简单介绍在plsql中的使用方法,后续会逐步完成各种理论和技巧的使用方法,例如优化器、表连接访问方法、索引等

注意事项:oracle执行计划是关乎sql执行效率最重要的内容,本文是个简单介绍,更深入的使用技巧请继续关注我的其他文章

    1. 变量

在MySQL中变量分为三种类型:系统变量、用户变量、局部变量。

变量类型

说明

语法

系统变量

系统变量由MySQL服务器提供。

系统变量分为全局变量(GLOBAL)、会话变量(SESSION)。

查看

查看所有:SHOW [ [SESSION] | GLOBAL ] VARIABLES;

模糊查找:SHOW [ [SESSION] | GLOBAL ] VARIABLES LIKE '...';

精确查找:SELECT @@[[SESSION.]|GLOBAL.]系统变量名;

设置

方式一:SET [ [SESSION] | GLOBAL ] 系统变量名 = 新值;

方式二:SET @@[[SESSION.]|GLOBAL.]系统变量名 = 新值;

注意

  1. 如果没有指定SESSION/GLOBAL,默认是SESSION,会话变量。
  2. mysql服务重新启动之后,所设置的全局参数会失效,要想不失效,可以在/etc/my.cnf中配置。

用户变量

用户变量不用提前声明,作用域为当前连接。

赋值

方式一:SET @var_name := expr [, @var_name := expr] ... ;

方式二:SELECT @var_name := expr [, @var_name := expr] ... ;

方式三:SELECT 字段名 INTO @var_name FROM 表名;

使用

SELECT @var_name [, @var_name,...];

注意

  1. 赋值时,可以使用= ,也可以使用:= ,推荐使用:=。
  2. 用户定义的变量无需对其进行声明或初始化,只不过获取到的值为NULL。

局部变量

局部变量访问之前需要声明,可用作存储过程内的局部变量和输入参数,局部变量的范围是在其内声明的BEGIN ... END块。

声明

DECLARE 变量名 变量类型 [DEFAULT ... ] ;

赋值

方式一:SET 变量名 := 值; # 也可使用=,推荐使用:=

方式二:SELECT 字段名 INTO 变量名 FROM 表名 ... ;

使用

SELECT 变量名

MySQL大小写敏感参数lower_case_table_names

1 简介

在MySQL中,数据库对应数据目录中的目录。数据库中的每个表至少对应数据库目录中的一个文件(也可能是多个,取决于存储引擎)。因此,所使用操作系统的大小写敏感性决定了数据库名和表名的大小写敏感性。

在大多数Unix中数据库名和表名对大小写敏感,而在Windows中对大小写不敏感。一个显著的例外情况是Mac OS X,它基于Unix但使用默认文件系统类型(HFS+),对大小写不敏感。然而,Mac OS X也支持UFS卷,该卷对大小写敏感,就像Unix一样。

变量lower_case_file_system说明是否数据目录所在的文件系统对文件名的大小写敏感。ON说明对文件名的大小写不敏感,OFF表示敏感。

在Linux中,数据库库名表名对大小写敏感,与文件系统保持一致:

在Windows中,数据库库名表名对大小写不敏感,与文件系统保持一致:

mysql> show variables like 'lower%';

+------------------------+-------+

| Variable_name          | Value |

+------------------------+-------+

| lower_case_file_system | OFF   |

| lower_case_table_names | 0     |

+------------------------+-------+

2 rows in set (0.01 sec)

mysql>

mysql> show variables like 'lower%';

+------------------------+-------+

| Variable_name          | Value |

+------------------------+-------+

| lower_case_file_system | ON    |

| lower_case_table_names | 1     |

+------------------------+-------+

2 rows in set (0.07 sec)

mysql>

2 大小写区分规则
Linux下:

数据库名与表名是严格区分大小写的;

表的别名是严格区分大小写的;

列名与列的别名在所有的情况下均是忽略大小写的;

变量名也是严格区分大小写的;

Windows下:

都不区分大小写

Mac OS下(非UFS卷):

都不区分大小写

3 参数说明(lower_case_table_names)

参数值

系统默认

解释

0

Unix默认

使用CREATE TABLE或CREATE DATABASE语句指定的大小写字母在硬盘上保存表名和数据库名。名称比较对大小写敏感。在大小写不敏感的操作系统如windows或Mac OS x上我们不能将该参数设为0,如果在大小写不敏感的文件系统上将--lowercase-table-names强制设为0,并且使用不同的大小写访问MyISAM表名,可能会导致索引破坏。

1

Windows默认

表名在硬盘上以小写保存,名称比较对大小写不敏感。MySQL将所有表名转换为小写在存储和查找表上。该行为也适合数据库名和表的别名。该值为Windows的默认值。

2

Mac OS X默认

表名和数据库名在硬盘上使用CREATE TABLE或CREATE DATABASE语句指定的大小写字母进行保存,但MySQL将它们转换为小写在查找表上。名称比较对大小写不敏感,即按照大小写来保存,按照小写来比较。注释:只在对大小写不敏感的文件系统上适用! innodb表名用小写保存。

4 由大小写敏感转换为不敏感方法

如果原来所建立库及表都是对大小写敏感的,想要转换为对大小写不敏感,主要需要进行如下3步:

1.将数据库数据通过mysqldump导出。

2.在my.cnf中更改lower_case_tables_name = 1,并重启mysql数据库。

3.将导出的数据导入mysql数据库。

5 注意事项

1.为避免大小写引发的问题,一种推荐的命名规则是:在定义数据库、表、列的时候全部采用小写字母加下划线的方式,不使用任何大写字母

2.在任何系统中可以使用lower_case_tables_name=1。使用该选项的不利之处是当使用SHOW TABLES或SHOW DATABASES时,看不出名字原来是用大写还是小写。

3.在Unix中如果以前lower_case_tables_name = 0将lower_case_tables_name设置为1之前,重启mysqld之前,必须先将旧的数据库名和表名转换为小写。

    1. ySQL视图/存储过程/触发器

MySQL视图

介绍

视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。

通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。

注意:对视图增删改数据,就是对基表增删改数据。

作用

简单:经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件。

细化授权:数据库可以授权,但不能授权到数据库特定行和特定的列上。通过视图用户只能查询和修改他们所能见到的数据。

数据独立:视图可帮助用户屏蔽真实表结构变化带来的影响。

语法

创建视图

CREATE [OR REPLACE] VIEW 视图名称[(列名列表)] AS SELECT语句 [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]

查询视图

查看创建视图语句:SHOW CREATE VIEW 视图名称;

查看视图数据:SELECT * FROM 视图名称 ......;

修改视图

方式一:ALTER VIEW 视图名称[(列名列表)] AS SELECT语句 [ WITH [ [CASCADED] | LOCAL ] CHECK OPTION ]

方式二:CREATE OR REPLACE VIEW 视图名称[(列名列表)] AS SELECT语句 [ WITH [ [CASCADED] | LOCAL ] CHECK OPTION ]

删除视图

DROP VIEW [IF EXISTS] 视图名称 [,视图名称] ...

检查选项

当使用WITH CHECK OPTION子句创建视图时,MySQL会通过视图检查正在更改的每个行,例如插入,更新,删除,以使其符合视图的定义。 MySQL允许基于另一个视图创建视图,它还会检查依赖视图中的规则以保持一致性。为了确定检查的范围,mysql提供了两个选项: CASCADED 和LOCAL,默认值为 CASCADED 。

CASCADED级联:需要检查本视图以及所依赖视图,不论所依赖视图是否有检查选项。

LOCAL本地:需要检查本视图,是否检查所依赖视图由所依赖视图决定。

无检查选项:不需检查本视图,是否检查所依赖视图由所依赖视图决定。

更新限制

要使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系。如果视图包含以下任何一项,则该视图不可更新:

    聚合函数或窗口函数(SUM()、 MIN()、 MAX()、 COUNT()等)

    DISTINCT

    GROUP BY

    HAVING

    UNION 或者 UNION ALL

MySQL存储过程

介绍

存储过程是事先经过编译并存储在数据库中的一段SQL语句的集合,可以接收参数,也可以返回数据。

调用存储过程可以简化工作,减少网络交互,提高效率。但移植性差。

基本语法

创建

CREATE PROCEDURE 存储过程名称 ([ 参数列表 ])

BEGIN

  SQL语句;

END ;

调用

CALL 存储过程名称 ([ 参数列表 ]);

查看

SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA = 'xxx'; -- 查询指定数据库的存储过程及状态信息

SHOW CREATE PROCEDURE 存储过程名称; -- 查询某个存储过程的定义

删除

DROP PROCEDURE [ IF EXISTS ] 存储过程名称;

注意:

在命令行中,执行创建存储过程的SQL时,需要先通过关键字 delimiter 修改SQL语句的结束符(默认的分号与存储过程中的分号冲突),创建完再改回来,例如:

delimiter $$

CREATE PROCEDURE 存储过程名称 ([ 参数列表 ])

BEGIN

-- SQL语句

END$$

delimiter ;

CALL 存储过程名称 ([ 参数列表 ]);

Declare语句只允许出现在begin … end语句中而且必须出现在第一行

Declare的顺序也有要求,通常是先声明本地变量,再是游标,然后是条件和handler

带输入参数的存储过程

例:传入一个员工的id,查询员工信息

DELIMITER $

CREATE PROCEDURE pro_findById(IN eid INT)

BEGIN

select * from employee where id=eid;

END $

--调用

CALL pro_findById(4);

带输出参数的存储过程:

DELIMITER $

CREATE PROCEDURE pro_testOut(OUT str VARCHAR(20))

BEGIN

set str='helljava';

END $

--调用

CALL pro_testOut(@name);-- 定义一个会话变量name以接收存储过程的返回值

select @name;-- 查看变量值(返回值)

带输入输出参数的存储过程

DELIMITER $

CREATE PROCEDURE pro_testInOut(INOUT n INT)

BEGIN

   select n;

   set n =500;

END $

set @n=10;

--调用

CALL pro_testInOut(@n); --执行此步输出10

select @n; --执行此步输出500

带输入输出参数的存储过程(使用查询的结果赋值(INTO)给变量)

DELIMITER $

CREATE PROCEDURE pro_findById2(IN eid INT,OUT vname VARCHAR(20) )

BEGIN

select empName INTO vname from employee where id=eid;

END $

--调用

CALL pro_findById2(1,@name);

select @name;

带有判断条件的存储过程

输入一整数,若1,返回“星期一”,若2,返回“星期二”,余者返回“错误输入”:

DELIMITER $

CREATE PROCEDURE pro_testIf(IN num INT,OUT str VARCHAR(20))

BEGIN

IF num=1 THEN

set str='星期一';

ELSEIF num=2 THEN

set str='星期二';

ELSE

set str='输入错误';

END IF;

END $

--调用

CALL pro_testIf(4,@str);

select @str;

带有循环条件的存储过程

案例一:输入一整数求包括1到此整数之间所有整数的和:

DELIMITER $

CREATE PROCEDURE pro_testWhile(IN num INT,OUT result INT)

BEGIN

-- 定义一个局部变量

DECLARE i INT DEFAULT 1;

DECLARE vsum INT DEFAULT 0;

WHILE i<=num DO

      set vsum = vsum+i;

      set i=i+1;

END WHILE;

set result=vsum;

END $

--调用

CALL pro_testWhile(100,@result);

select @result;

案例二:批量插入测试数据:

DELIMITER $

CREATE PROCEDURE insert100 ()

BEGIN

DECLARE i INT DEFAULT 1;

WHILE i <= 100 DO

INSERT INTO student(student_number, NAME) VALUES (i, concat('twx', i));

SET i = i + 1;

END WHILE;

END $

练习:视频day16_06末尾;解答:day16_07开头。

MySQL触发器

例:当向员工表插入一条记录时,同时往日志表插入数据:

创建日志表:CREATE TABLE test_log(Id INT PRIMARY KEY AUTO_INCREMENT,content VARCHAR(100));

创建触发器(添加):CREATE TRIGGER tri_empAdd AFTER INSERT ON employee FOR EACH ROW INSERT INTO test_log(content) VALUES('员工表插入了一条记录');

创建触发器(修改):CREATE TRIGGER tri_empUpd AFTER update ON employee FOR EACH ROW INSERT INTO test_log(content) VALUES('员工表修改了一条记录');

创建触发器(删除):CREATE TRIGGER tri_empDel AFTER delete ON employee FOR EACH ROW INSERT INTO test_log(content) VALUES('员工表删除了一条记录');

增删改employee表:代码略。

查看触发器执行结果(查看日志表):select * from test_log;

删除触发器:DROP TRIGGER tri_empAdd; DROP TRIGGER tri_empUpd; DROP TRIGGER tri_empDel;

    1. ySQL锁机制

锁粒度

锁类型

具体锁

说明

触发SQL

解除SQL

全局锁

全局锁是对整个数据库实例加锁,加锁后整个实例处于只读状态,当前客户端的DML、DDL、已经更新操作的事务提交语句将失败,其他客户端的写将被阻塞。

其典型使用场景是做全库的逻辑备份,锁定所有表,保证数据一致性。

flush tables with read lock;

unlock tables;

表级锁

表锁

表共享读锁

所有客户端可读,当前事务不可写,其他事务写阻塞

lock tables 表名... read

unlock tables;

或客户端断开

表独占写锁

当前事务可读可写,其他事务读写阻塞

lock tables 表名... write

元数据锁

元数据共享锁

在表上有活动事务的时候,不可以对元数据进行写入操作。为了避免DML与DDL冲突,保证读写的正确性。

DML

事务提交

元数据排他锁

DDL

意向锁

意向共享锁(IS)

与表共享锁(read)兼容

与表排他锁(write)互斥

任意意向锁之间兼容(不是真正的锁,只是表达了加锁的意向)

select ... lock in share mode

意向排他锁(IX)

与表共享锁(read)互斥

与表排他锁(write)互斥

任意意向锁之间兼容(不是真正的锁,只是表达了加锁的意向)

DML、

select...for update

行级锁

行锁

行共享锁(S)

共享锁之间兼容,排它锁之间互斥,共享锁与排它锁之间互斥。

默认隔离级别RR下,InnoDB使用临键锁进行索引扫描,以防止幻读。

1)不在索引上的查询,升级为表锁。

2)在唯一索引上的等值查询,记录存在时,优化为行锁。

3)在唯一索引上的等值查询,记录不存在时,退化为间隙锁。

4)在普通索引上的等值查询,向右遍历时最后一个值不满足查询需求时,退化为间隙锁。

select ... lock in share mode

行排他锁(X)

DML、

select...for update

间隙锁

临键锁

概述

MySQL中的锁,按照锁的粒度分,分为以下三类:

全局锁:锁定数据库中的所有表。

表级锁:每次操作锁住整张表。粒度大,加锁快,无死锁,性能低(低并发,高冲突)

行级锁:每次操作锁住对应的行数据。粒度小,加锁慢,或死锁,性能高(高并发,低冲突)

又可分为共享锁和排它锁:

共享锁:又称为共享读锁,简称S锁,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。

比如事务T对数据对象A加上S锁,则事务T只能读A;其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这就保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。

排他锁:又称为独占写锁,简称X锁,排他锁就是不能与其他锁并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。

比如事物T对数据对象A加上X锁,则只允许T读取和修改A,其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。它防止任何其它事务获取资源上的锁,直到在事务的末尾将资源上的原始锁释放为止。

mysql InnoDB引擎默认的DML数据操作语句都会自动给涉及到的数据加上排他锁,DQL数据查询语句默认不会加任何锁类型,如果加排他锁可以使用select ...for update语句,加共享锁可以使用select ... lock in share mode语句。

全局锁

介绍

全局锁是对整个数据库实例加锁,加锁后整个实例处于只读状态,当前客户端的DML、DDL、已经更新操作的事务提交语句将失败,其他客户端的写将被阻塞。

其典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。

语法

加全局锁MySQL命令

flush tables with read lock;

数据备份cmd命令,新开cmd窗口执行

mysqldump -h主机地址本地可略 -u用户名 –p密码 库名 > 备份文件名.sql

释放锁(MySQL命令)

unlock tables;

特点

数据库中加全局锁,是一个比较重的操作,存在以下问题:

    如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆。

    如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟。

在InnoDB引擎中,我们可以在备份时加上参数 --single-transaction 参数来完成不加锁的一致性数据备份:

mysqldump --single-transaction -h主机地址本地可略 -u用户名 –p密码 库名 > 备份文件名.sql

表级锁

介绍

表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中。

对于表级锁,主要分为以下三类:

    表锁

    元数据锁(meta data lock,MDL)

    意向锁

表锁

分类:

    表共享读锁(read lock):所有客户端可读,当前客户端不可写,其他客户端写阻塞

    表独占写锁(write lock):当前客户端可读可写,其他客户端读写阻塞

语法:

    加锁:lock tables 表名... read/write。

    释放锁:unlock tables  # 或者客户端断开连接。

元数据锁

meta data lock , 元数据锁,简写MDL。

MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。为了避免DML与DDL冲突,保证读写的正确性。

这里的元数据,大家可以简单理解为就是一张表的表结构。 也就是说,某一张表涉及到未提交的事务时,是不能够修改这张表的表结构的。

在MySQL5.5中引入了MDL,当对一张表进行增删改查的时候,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)。

常见的SQL操作时,所添加的元数据锁:

对应SQL

锁类型

说明

lock tables 表名列表 read/write

SHARED_READ_ONLY / SHARED_NO_READ_WRITE

select 、select ... lock in share mode

SHARED_READ

与SHARED_WRITE兼容,与EXCLUSIVE互斥

insert 、update、delete、select ... for update

SHARED_WRITE

与SHARED_READ兼容,与EXCLUSIVE互斥

alter table ...

EXCLUSIVE

与其他的MDL都互斥

当执行SELECT、INSERT、UPDATE、DELETE等语句时,添加的是元数据共享锁(SHARED_READ / SHARED_WRITE),之间是兼容的。

当执行SELECT语句时,添加的是元数据共享锁(SHARED_READ),会阻塞元数据排他锁(EXCLUSIVE),之间是互斥的。

查看数据库中的元数据锁的加锁情况(MySQL8):

select object_type,object_schema,object_name,lock_type,lock_duration from performance_schema.metadata_locks;

意向锁

介绍

InnoDB引入了意向锁,使得加表锁时不用逐行检查是否加了互斥的行锁,提升了加表锁的效率。

有了意向锁后:

客户端一,在执行DML操作时,会对涉及的行加行锁,同时也会对该表加上意向锁。

而其他客户端,在对这张表加表锁的时候,会根据该表上所加的意向锁来判定是否可以成功加表锁,而不用逐行判断行锁情况了。

分类

    意向共享锁(IS):由语句select ... lock in share mode添加。与表共享锁(read)兼容,与表排他锁(write)互斥。

    意向排他锁(IX):由insert、update、delete、select...for update添加。与表共享锁(read)、表排他锁(write)都互斥。

意向锁之间不会互斥。

一旦事务提交了,意向共享锁、意向排他锁,都会自动释放。

可以通过以下SQL,查看意向锁及行锁的加锁情况:

select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;

行级锁

介绍

行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。

InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。

对于行级锁,主要分为以下三类:

    行锁(Record Lock):锁定单个行记录的锁,防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持。

    间隙锁(Gap Lock):锁定索引记录间隙(不含该记录),确保索引记录间隙不变,阻塞其他事务在这个间隙进行insert,防止产生幻读。在RR隔离级别下都支持。

    临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。

InnoDB实现了以下两种类型的行锁:

    共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁。

    排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。

共享锁之间兼容,排它锁之间互斥,共享锁与排它锁之间互斥。

常见的SQL语句,在执行时,所加的行锁如下:

SQL

行锁类型

INSERT ...、UPDATE ...、DELETE ...

加排他锁

SELECT(正常)

不加任何锁

SELECT ... LOCK IN SHARE MODE

加共享锁

SELECT ... FOR UPDATE

加排他锁

可以通过以下SQL,查看意向锁及行锁的加锁情况:

select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;

默认情况下,InnoDB在REPEATABLE READ事务隔离级别运行,InnoDB使用 next-key 锁进行搜索和索引扫描,以防止幻读。

  1. InnoDB的行锁是针对于索引加的锁,不通过索引条件检索、更新数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁。
  2. 在唯一索引上的等值匹配,对已存在的记录加锁时,优化为行锁。
  3. 在唯一索引上的等值匹配,给不存在的记录加锁时,优化为间隙锁。

2)在普通索引上的等值匹配,向右遍历时最后一个值不满足查询需求时,next-key lock退化为间隙锁。

示例:

我们知道InnoDB的B+树索引,叶子节点是有序的双向链表。假如,我们要根据这个二级索引查询值为18的数据,并加上共享锁,我们是只锁定18这一行就可以了吗? 并不是,因为是非唯一索引,这个结构中可能有多个18的存在,所以,在加锁时会继续往后找,找到一个不满足条件的值(当前案例中也就是29)。此时会对18加临键锁,并对29之前的间隙加锁。

3)唯一索引上的范围查询,会访问到不满足条件的第一个值为止。

示例:

查询的条件为id>=19,并添加共享锁。此时我们可以根据数据库表中现有的数据,将数据分为三个部分:

[19]、(19,25]、(25,+∞]

所以数据库数据在加锁是,就是将19加了行锁,25的临键锁(包含25及25之前的间隙),正无穷的临键锁(正无穷及之前的间隙)。

4)间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。

乐观锁

悲观锁是由数据库自己实现,要用的时候,我们直接调用数据库的相关语句就可以。

乐观锁不是数据库自带的,需要我们自己用程序实现。乐观锁的实现大部分都是基于版本控制实现的,还可以通过时间戳的方式,通过提前读取,事后对比的方式实现。

死锁

InnoDB引擎处理死锁的方式是将持有最少行级排它锁的事务进行回滚。

常见的三种避免死锁的方法:

不同事务访多表:相同顺序访问表

同一事务访多表:一次锁定所需表

易生死锁业务中:升级粒度表级锁

    1. ySQL InnoDB引擎

逻辑存储结构

层级

大小

说明

表空间

表空间是InnoDB存储引擎逻辑结构的最高层,如果用户启用了参数innodb_file_per_table(8.0版本默认开启),则每张表都会有一个表空间(xxx.ibd),一个mysql实例可以对应多个表空间,用于存储记录、索引等数据。

分为数据段(Leaf node segment)、索引段(Non-leaf node segment)、回滚段(Rollback segment),InnoDB是索引组织表,数据段就是B+树的叶子节点,索引段即为B+树的非叶子节点。段用来管理多个Extent(区)。

每区默认1M,含64页

区,表空间的单元结构,每个区的大小为。 默认情况下,InnoDB存储引擎页大小为16K,即一个区中一共有。

每页默认16KB

页是InnoDB存储引擎磁盘管理的最小单元。为了保证页的连续性,InnoDB 存储引擎每次从磁盘申请 4-5 个区。

行,InnoDB 存储引擎数据是按行进行存放的。在行中,默认有两个隐藏字段:

    Trx_id:每次对某条记录进行改动时,都会把对应的事务id赋值给trx_id隐藏列。

    Roll_pointer:每次对某条引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。

文件配置参数:innodb_file_per_table

在MySQL8中innodb_file_per_table默认为ON,在该配置下:

文件名:表名.idb

可在cmd中用ibd2sdi命令查看二进制文件内容(JSON格式):ibd2sdi 表名.idb

存储内容:表结构(frm、sdi)、数据、索引

在MySQL早期版本中innodb_file_per_table默认为OFF,在该配置下:

文件名:表名.frm

存储内容:

内存结构

内存结构主要分为四块:

Buffer Pool缓冲池

Buffer Pool缓冲池是主内存中的一个区域,里面缓存磁盘上经常操作的数据,在执行增删改查操作时,先操作缓冲池中的数据(若缓冲池没有数据,则从磁盘加载并缓存),然后再以一定频率刷新到磁盘,从而减少磁盘IO,加快处理速度。

缓存内容:索引页、数据页、undo页、插入缓存、自适应哈希索引、InnoDB锁信息等。

缓冲池以Page页为单位,底层采用链表数据结构管理Page。根据状态,将Page分为三种类型:

    free page:空闲page,未被使用。

    clean page:被使用page,数据没有被修改过。

    dirty page:脏页,被使用page,数据被修改过,也中数据与磁盘的数据产生了不一致。

在专用服务器上,通常将多达80%的物理内存分配给缓冲池。

参数:innodb_buffer_pool_size

Change Buffer更改缓冲区

Change Buffer更改缓冲区(针对非唯一索引),在执行DML语句时,如果这些数据Page没有在Buffer Pool中,不会直接操作磁盘,而会将数据变更存在更改缓冲区 Change Buffer中,在未来数据被读取时,再将数据合并恢复到Buffer Pool中,再将合并后的数据刷新到磁盘中。

Change Buffer的意义是什么呢?

与聚集索引不同,二级索引通常是非唯一的,并且以相对随机的顺序插入二级索引。同样,删除和更新可能会影响索引树中不相邻的二级索引页,如果每一次都操作磁盘,会造成大量的磁盘IO。有了ChangeBuffer之后,我们可以在缓冲池中进行合并处理,减少磁盘IO。

Adaptive Hash Index自适应hash索引

用于优化对Buffer Pool数据的查询。MySQL的innoDB没有直接支持hash索引,但是提供了这个自适应hash索引。

InnoDB会监控对表上各索引页的查询,如果观察到在特定的条件下hash索引可以提升速度,则建立hash索引,称之为自适应hash索引。

参数:adaptive_hash_index

Log Buffer日志缓冲区

用来保存要写入到磁盘中的log日志数据(redo log 、undo log),默认大小16MB,日志缓冲区的日志会定期刷新到磁盘中。如果需要更新、插入或删除许多行的事务,增加日志缓冲区的大小可以节省磁盘 I/O。

参数:

innodb_log_buffer_size:日志缓冲区大小

innodb_flush_log_at_trx_commit:日志刷新到磁盘时机,取值主要为以下三种:

​ 1:日志在每次事务提交时写入并刷新到磁盘,默认值。

​ 0:每秒将日志写入并刷新到磁盘一次。

​ 2:日志在每次事务提交后写入,并每秒刷新到磁盘一次。

磁盘结构

System Tablespace系统表空间

系统表空间是更改缓冲区的存储区域。如果表是在系统表空间而不是每个表文件或通用表空间中创建的,它也可能包含表和索引数据。(在5.x版本中还包含InnoDB数据字典、undolog等)

参数:innodb_data_file_path

系统表空间,默认的文件名叫 ibdata1。

File-Per-Table Tablespaces

如果开启了innodb_file_per_table开关,则每个表的文件表空间包含单个InnoDB表的数据和索引,并存储在文件系统上的单个数据文件中。

开关参数:innodb_file_per_table,该参数默认开启。也就是说,我们每创建一个表,都会产生一个表空间文件。

General Tablespaces通用表空间

需要通过 CREATE TABLESPACE 语法创建通用表空间,在创建表时,可以指定该表空间。

A. 创建表空间

CREATE TABLESPACE ts_name ADD DATAFILE 'file_name' ENGINE = engine_name;

B. 创建表时指定表空间

CREATE TABLE xxx(...) TABLESPACE ts_name;

Undo Tablespaces撤销表空间

MySQL实例在初始化时会自动创建两个默认的undo表空间(初始大小16M),用于存储 undo log日志。

Temporary Tablespaces临时表空间

InnoDB使用会话临时表空间和全局临时表空间,存储用户创建的临时表等数据。

Doublewrite Buffer Files双写缓冲区

innoDB引擎将数据页从Buffer Pool刷新到磁盘前,先将数据页写入双写缓冲区文件中,便于系统异常时恢复数据。

双写缓冲区对应文件后缀为dblwr

Redo Log重做日志

是用来实现事务的持久性。该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都会存到该日志中,用于在刷新脏页到磁盘发生错误时,进行数据恢复。

以循环方式写入重做日志文件,涉及两个文件:

-rw-r-----. 1 mysql mysql  50331648 10月  2 22:52 ib_logfile0

-rw-r-----. 1 mysql mysql  50331648 10月  2 22:52 ib_logfile1

后台线程

前面我们介绍了InnoDB的内存结构,以及磁盘结构,那么内存中我们所更新的数据,又是如何到磁盘中的呢? 此时,就涉及到一组后台线程,接下来,就来介绍一些InnoDB中涉及到的后台线程。

在InnoDB的后台线程中,分为4类,分别是:Master Thread 、IO Thread、Purge Thread、Page Cleaner Thread。

Master Thread核心后台线程

负责调度其他线程,还负责将缓冲池中的数据异步刷新到磁盘中,保持数据的一致性,还包括脏页的刷新、合并插入缓存、undo页的回收。

IO Thread

InnoDB大量使用了AIO来处理IO请求,可以极大提高性能,而IO Thread主要负责这些IO请求的回调。

线程类型

默认个数

职责

Read thread

4

负责读操作

Write thread

4

负责写操作

Log thread

1

负责将日志缓冲区刷新到磁盘

Insert buffer thread

1

负责将写缓冲区内容刷新到磁盘

可通过指令show engine innodb status查看到InnoDB的状态信息,其中就包含IO Thread信息。

Purge Thread

主要用于回收事务已经提交了的undo log,在事务提交之后,undo log可能不用了,就用它来回收。

Page Cleaner Thread

协助 Master Thread 刷新脏页到磁盘的线程,它可以减轻Master Thread的工作压力,减少阻塞。

什么是数据库事务

数据库事务多条SQL语句看做一个不可分割的整体,要么一起成功,要么一起失败

比如银行转账,一个SQL扣钱,一个SQL收钱,两者要么一起成功,要么一起失败

innoDB:支持事务。

MyISAM:不支持事务,但执行效率高。

事务4大特性(ACID)

特性

内容

原理

原子性(Atomicity)

事务是不可分割的最小操作单元,要么全部成功,要么全部失败

undo log提供回滚语句

一致性(Consistency)

事务完成时,必须使所有的数据都保持一致状态

undo log + redo log共同保证

隔离性(Isolation)

数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行

锁 + MVCC共同保证

持久性(Durability)

事务一旦提交或回滚,它对数据库中的数据的改变就是永久的

redo log重做失败的脏页刷新

事务隔离级别

并发事务问题

脏读(Dirty Read):一个事务读到另一个事务未提交的数据。

不可重复读(Non-Repeatable Read):一个事务再查询时读到另一个事务已提交的新数据(update/delete)。

幻读(Phantom Read):一个事务再查询(快照读)时读不到另一个事务已提交的新增数据(insert),但无法插入相同数据(主键冲突),或者更新时可能覆盖该数据(脏写)。

注:脏写问题解决之道:

  1. 通过乐观锁更新:表添加version字段,更新时while(更新未成功){更新对应version数据}
  2. SQL中更新:例如update user set num = num + 1;

事务隔离级别(Transaction Isolation Levels)是数据库管理系统(DBMS)中用来控制多个并发事务之间相互影响程度的一种机制。它指定了一个事务可能受到其他并发事务影响的程度,从而保证事务的一致性和可靠性。

以下是四种常见的事务隔离级别:

读未提交(Read Uncommitted):最低的隔离级别,一个事务可以读另一个事务未提交的数据脏读

读已提交(Read Committed):保证一个事务提交后才能被其他事务读取。避免了脏读,但是可能出现不可重复读。

可重复读(Repeatable Read):保证在同一事务中多次读取快照读同一数据时,其结果是一致的。避免了不可重复读,但是可能出现幻读。

序列化(Serializable):最高的隔离级别,强制事务串行执行,避免了所有并发问题,但是会导致性能下降。

不同的隔离级别具有不同的特点和适用场景。在实际应用中,需要根据具体情况选择合适的隔离级别。

隔离级别(从高到底)

防止脏读

防止不可重复读

防止幻读

serializable(序列化)

repeatable read(可重复读)

×

MySQL、InnoDB 默认

read committed(读已提交)

×

×

Sql Server、Oracle 默认

read uncommitted(读未提交)

×

×

×

MySQL默认为可重复读的原因互联网项目中mysql应该选什么事务隔离级别 - 知乎

Mysql早些时候,bin log日志格式只有statement这种,在RC的隔离级别,主从复制可能出现数据不一致的问题

如右图,在RC隔离级别下,主库执行两个事务,都提交后,主库test表会有一条为3的记录(因为没有间隙锁,行锁没有锁住同一条记录,所以先删后增),但在从库该表为空(因为binlog cache在事务提交时才会写入binlog文件中,所以日志记录为先增后删)。

解决方案有两种!

  1. 隔离级别设为可重复读(Repeatable Read),在该隔离级别下引入间隙锁。当Session 1执行delete语句时,会锁住间隙。那么,Ssession 2执行插入语句就会阻塞住!
  2. 将binglog的格式修改为ROW格式,此时是基于行的复制,自然就不会出现sql执行顺序不一样的问题!奈何这个格式在mysql5.1版本开始才引入。因此由于历史原因,mysql将默认的隔离级别设为可重复读(Repeatable Read),保证主从复制不出问题!

Session1

Session2

create table test(b int, primary key(b))engine=innodb

insert into test select 1

set tx_isolation = 'read-committed'

set tx_isolation='read-committed'

begin

begin

delete from test where b <= 6

insert into test select 3

commit

commit

如何选择事务隔离级别

如果是互联网高并发项目,建议选读已提交,性能好,此时日志格式应确保为ROW

如果是传统报表项目,建议选可重复读,并且报表查询加事务处理,防止不同时刻查出来的数据不一致。

查询事务隔离级别

全局,5.7及以前版本语法select @@global.tx_isolation

全局,5.7及以后版本语法select @@global.transaction_isolation

会话,5.7及以前版本语法select @@tx_isolation

会话,5.7及以后版本语法select @@transaction_isolation

设置事务隔离级别

set <global/session> transaction isolation level <read uncommitted/read committed/repeatable read/serializable>;

事务提交类型

提交数据有三种类型:显式提交、隐式提交及自动提交。

显式提交

用COMMIT命令直接完成的提交为显式提交。

自动提交

若把AUTOCOMMIT设置为ON,则在DML语句执行后,系统将自动进行提交。

隐式提交

当执行DDL语句或DCL语句或正常退出会话(没有明确发出commit或者rollback)时,之前的DML语句都会被提交,DDL语句或DCL语句本身也会提交。

隐式提交时注意事项:

执行DDL语句不管成功还是失败,前面的DML操作也会被提交。

为了避免隐式提交或者回滚,尽量保证一条或者几条DML操作完成后有显示的提交或者回滚,防止后续执行的DCL或者DDL自动提交前期的DML操作。

隐式提交的本质:执行DDL语句之前之后都会执行commit

第一个commit将当前session中未提交的事务隐式提交,以保证DDL语句失败时的回滚位置。

第二个commit将DDL操作提交

事务原理

原子性、一致性、持久性由InnoDB中的redo log和undo log来保证。

持久性由数据库的锁和MVCC来保证。

redo log

重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。当事务提交之后会把所有修改信息都存到该日志文件中,用于在刷新脏页到磁盘发生错误时,进行数据恢复用。

redo log由两部分组成:内存中的重做日志缓冲(redo log buffer)以及磁盘上的重做日志文件(redo log file)。

redo log buffer

MySQL对数据页的每次原子操作(Mini-Trasaction,mtr,比如向B+树中插入一条记录)会生成一组redo log日志存入redo log buffer,在进行恢复时,需要将一组redo日志作为一个不可分割的整体来处理。

redo log buffer以redo log block为单位,每个redo log block大小为512字节,分为log block header、log block body、log block trailer三部分,redo log日志存放在log block body中。全局参数buffer_free指明后续redo日志应该从log buffer中的哪个偏移量开始写入。

redo log file

文件组存放路径:由参数innodb_log_group_home_dir指定,默认值./,表示在数据库的数据目录下。MySQL的默认数据目录(var/lib/mysql)下默认有两个名为 ib_logfile0 和 ib_logfile1 的文件,log buffer中的日志默认情况下就是刷新到这两个磁盘文件中。

innodb_log_files_in_group:指明redo log file的个数,命名方式如:ib_logfile0,iblogfile1... iblogfilen。默认2个,最大100个。

innodb_log_file_size:单个redo log文件大小,默认值48M。最大值512G,注意最大值指的是整个redo log系列文件之和,即(innodb_log_files_in_group * innodb_log_file_size )不能大于最大值512G。

循环日志:当最后一个redo log文件写满后,会从首个redo log文件继续写入覆盖旧日志。

write pos、checkpoint:

write pos是日志刷新的最新位置,每当redo log日志刷新,write pos就后移。

checkpoint是数据页刷新的最新位置,每当数据页刷新,checkpoint就后移。

从write pos往后直到checkpoint的区域,是已刷新的脏页对应的日志,可以被新日志覆盖。

从checkpoint往后直到write pos的区域,是未刷新的脏页对应的日志。

redo log刷新机制

MySQL按照一定的刷新机制将redo log buffer写入os buffer,再调用fsync()写入redo log file

刷新机制由参数innodb_flush_log_at_trx_commit指定:

值为0:每秒刷新。MySQL挂了或操作系统宕机可能会有1秒数据的丢失。

值为1:默认值,每次事务提交时刷新。数据绝对不会丢失。

值为2:每次事务提交时写入os buffer,每秒调用fsync()将os buffer日志写入redo log file。MySQL挂了不会有任何数据丢失,但是操作系统宕机可能会有1秒数据的丢失。

redo log记录形式

redo log保证事务持久性原理:

在事务中写数据时,每执行一个原子写操作,先更新缓冲区的数据(产生脏页),再写redo log buffer记录。当提交事务时,先把redo log buffer记录写入redo log磁盘文件中,然后直接返回成功信息,至于缓存池中的更新数据(脏页)写入磁盘,是由另一个线程不定时进行。

如果刷新缓冲区的脏页到磁盘时发生错误,就可以借助于redo log进行数据恢复,这样就保证了事务的持久性。

为什么每次提交事务,要刷新redo log到磁盘中,而不是直接将buffer pool中的脏页刷新到磁盘?

原因一:因为redo log是顺序写磁盘,效率大于脏页数据的随机写磁盘。这种先写日志的方式,叫做WAL(Write-Ahead Logging)。

原因二:Innodb以页为单位进行磁盘交互,如果一个事务只修改一页中的的少量数据,提交事务时就将完整的数据页刷到磁盘,这样太浪费资源。

redo log与bin log的区别

用途不同:redo log用于崩溃恢复,bin log用于主从复制和数据恢复。

大小不同:redo log大小固定,bin log大小由参数max_binlog_size确定。

写入时机不同:bin log仅在事务提交前进行提交,而在事务进行的过程中,却不断有redo ertry被写入redo log中。

写入方式不同:redo log是循环写入和擦除,bin log是追加写入,超过文件大小则新建文件写入,不会覆盖已写文件。

实现方式不同:redo log是InnoDB引擎层实现,并非所有引擎都有;bin log由Server层实现,所有引擎都可以使用。

undo log

回滚日志,用于记录数据被修改前的信息,作用包含两个:提供回滚(保证事务的原子性)和MVCC(多版本并发控制)。

undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undolog中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。

Undo log销毁:undo log在事务执行时产生,事务提交时,并不会立即删除undo log,因为这些日志可能还用于MVCC。

Undo log存储:undo log采用段的方式进行管理和记录,存放在前面介绍的rollback segment回滚段中,内部包含1024个undo log segment。

MySQL多版本并发控制MVCC

MVCC(Multi-Version Concurrency Control)多版本并发控制,通常在数据库管理系统中用于实现并发事务的隔离性和一致性。

在MVCC中,每个事务都有一个唯一的时间戳,称为事务ID。当某个事务对数据进行更新时,系统会创建一个新的版本,并记录该版本对应的事务ID。这样,旧的事务可以继续读取旧版本的数据,而新的事务则读取新版本的数据,彼此不会产生冲突。

MVCC的优点包括:

避免了读-写冲突:不同事务读取不同版本的数据,避免了读取操作和写入操作之间的冲突,在一定程度上提高了并发性能。

读操作不阻塞写操作:由于读操作可以读取旧版本的数据,因此读操作不会被写操作所阻塞。

提供了可重复读隔离级别:由于每个事务读取的是自己的版本,因此可以获得一致性快照,实现了可重复读隔离级别。

快照读为MySQL实现MVCC提供了一个非阻塞读功能。MVCC的具体实现,还需要依赖于数据库记录中的三个隐藏字段、undo log日志、readView。

当前读

读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:select ... lock in share mode(共享锁),select ...for update、update、insert、delete(排他锁)都是一种当前读。

在测试中我们可以看到,即使是在默认的RR隔离级别下,事务A中依然可以读取到事务B最新提交的内容,因为在查询语句后面加上了 lock in share mode 共享锁,此时是当前读操作。当然,当我们加排他锁的时候,也是当前读操作。

快照读

简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。

    Read Committed:每次select,都生成一个快照读。

    Repeatable Read:开启事务后第一个select语句才是快照读的地方。

    Serializable:快照读会退化为当前读。

在测试中,我们看到即使事务B提交了数据,事务A中也查询不到。 原因就是因为普通的select是快照读,而在当前默认的RR隔离级别下,开启事务后第一个select语句才是快照读的地方,后面执行相同的select语句都是从快照中获取数据,可能不是当前的最新数据,这样也就保证了可重复读。

隐藏字段

当我们创建InnoDB表时,InnoDB会自动的给我们添加两个或三个隐藏字段(可在表对应的文件所在目录中运行命令查看:ibd2sdi 表名.ibd):

DB_TRX_ID 最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID,是自增的。

DB_ROLL_PTR 回滚指针,指向当前更新对应的undo log回滚日志(当前记录的上一个版本),新插入数据时该字段值为null。

DB_ROW_ID 隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。

undolog版本链

回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志。

当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除。

而update、delete的时候,产生的undo log日志不仅在回滚时需要,在快照读时也需要,不会立即被删除。

不同事务或相同事务对同一条记录进行修改,会导致该记录的undolog生成一条记录版本链表,链表头部是最新的旧记录,链表尾部是最早的旧记录。

readView读视图

ReadView(读视图)是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。

ReadView中包含了四个核心字段:

m_ids 当前活跃的事务ID集合

min_trx_id 最小活跃事务ID

max_trx_id 预分配事务ID,当前最大事务ID+1(因为事务ID是自增的)

creator_trx_id ReadView创建者的事务ID

而在readview中就规定了版本链数据的访问规则(trx_id 代表当前undolog版本链对应事务ID):

条件

是否可以访问

说明

trx_id == creator_trx_id

可以访问该版本

成立,说明数据是当前这个事务更改的

trx_id < min_trx_id

可以访问该版本

成立,说明数据已经提交

trx_id > max_trx_id

不可以访问该版本

成立,说明该事务是在ReadView生成后才开启

min_trx_id <= trx_id <= max_trx_id

如果trx_id不在m_ids中,可以访问该版本

成立,说明数据已经提交

不同的隔离级别,生成ReadView的时机不同:

    READ COMMITTED:在事务中每一次执行快照读时生成ReadView。

    REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView

MySQL事务操作命令和JDBC事务操作API

MySQL

JDBC

设置是否自动提交事务(关闭自动提交以开启事务/开启自动提交以关闭事务)

set autocommit =0 / 1

conn.setAutoCommit(false/true);

开启事务

start transaction;

conn.setAutoCommit(false);

提交事务,一旦提交事务不能回滚

commit;

conn.commit();

回滚事务。回滚到事务的起始点

rollback;

conn.rollback();//回滚后必须要提交conn.commit();

设置回滚点

savepoint sp;

Savepoint sp = conn.setSavepoint();

回滚至某点

rollback to sp;

conn.rollback(sp);//回滚后必须要提交conn.commit();

系统数据库

Mysql数据库安装完成后,自带了四个数据库,具体作用如下:

mysql 存储MySQL服务器正常运行所需要的各种信息 (时区、主从、用户、权限等)

information_schema 提供了访问数据库元数据的各种表和视图,包含数据库、表、字段类型及访问权限等

performance_schema 为MySQL服务器运行时状态提供了一个底层监控功能,主要用于收集数据库服务器性能参数

sys 包含了一系列方便 DBA 和开发人员利用 performance_schema性能数据库进行性能调优和诊断的视图

常用工具

mysql客户端工具

语法 :

mysql [options] [database]

选项 :

-u, --user=name #指定用户名

-p, --password[=name] #指定密码

-h, --host=name #指定服务器IP或域名

-P, --port=port #指定连接端口

-e, --execute=name #执行SQL语句并退出(该选项使得sql可直接在cmd中执行,尤其方便批处理脚本)。

示例:

mysql -uroot -proot -e "select * from 表" 库名;

mysqladmin管理工具

mysqladmin 是一个执行管理操作的客户端程序。可以用它来检查服务器的配置和当前状态、创建并删除数据库等。

语法:

mysqladmin [options] command ...

选项:

-u, --user=name       #指定用户名

-p, --password[=name] #指定密码

-h, --host=name       #指定服务器IP或域名

-P, --port=port       #指定连接端口

--help                #查看帮助文档

示例:

mysqladmin -uroot –p1234 drop 'test01'

mysqladmin -u root –p 123456 version

mysqlbinlog二进制日志查看工具

由于服务器生成的二进制日志文件以二进制格式保存,所以如果想要检查这些文本的文本格式,就会使用到mysqlbinlog 日志管理工具。

语法 :

mysqlbinlog [options] log-files1 log-files2 ...

选项 :

-d, --database=name 指定数据库名称,只列出指定的数据库相关操作。

-o, --offset=# 忽略掉日志中的前n行命令。

-v 将行事件(数据变更)重构为SQL语句(变量binlog_format为ROW时使用)

-vv 将行事件(数据变更)重构为SQL语句,并输出注释信息(变量binlog_format为ROW时使用)

-r,--result-file=name 将输出的文本格式日志输出到指定文件。

-s, --short-form 显示简单格式, 省略掉一些信息。

--start-datatime=date1 --stop-datetime=date2 指定日期间隔内的所有日志。

--start-position=pos1 --stop-position=pos2 指定位置间隔内的所有日志。

示例:

[root@frx01 ~]# mysqlbinlog -s /var/lib/mysql/binlog.000008

mysqlshow统计工具

mysqlshow客户端对象查找工具,用来很快地查找存在哪些数据库、数据库中的表、表中的列或者索引。

语法 :

mysqlshow [options] [db_name [table_name [col_name]]]

选项 :

--count 显示数据库及表的统计信息(数据库,表均可以不指定)

-i 显示指定数据库或者指定表的状态信息

示例:

# 查看每个数据库的表的数量及表中记录的数量

mysqlshow -uroot -p123456 --count

# 查看demo库的统计信息

mysqlshow -uroot -p123456 demo --count

# 查看demo库test表的统计信息

mysqlshow -uroot -p123456 demo test --count

# 查看test库test表id字段的统计信息

mysqlshow -uroot -p123456 demo test id --count

mysqldump数据备份工具

mysqldump 客户端工具用来备份数据库或在不同数据库之间进行数据迁移。备份内容包含创建表,及插入表的SQL语句。

语法 :

mysqldump [options] db_name [tables]

mysqldump [options] ,<--database/-B> db1 [db2 db3...]

mysqldump [options] <--all-databases/-A>

连接选项 :

-u, --user=name        指定用户名

-p, --password[=name]  指定密码

-h, --host=name        指定服务器ip或域名

-P, --port=#           指定连接端口

输出选项:

--add-drop-database    在每个数据库创建语句前加上 drop database 语句

--add-drop-table       在每个表创建语句前加上 drop table 语句 , (默认开启,不开启则--skip-add-drop-table)

-n, --no-create-db     不包含数据库的创建语句

-t, --no-create-info   不包含数据表的创建语句

-d --no-data           不包含数据

-T, --tab=name         自动生成两个文件:一个.sql文件,创建表结构的语句;一个.txt文件,数据文件

示例:

# 备份demo库

mysqldump -uroot -p123456 demo > demo.sql

# 备份demo库的表数据,不备份表结构(-t)

mysqldump -uroot -p123456 -t demo > demo2.sql

# 将demo数据库的test表的表结构与数据分开备份(-T),

mysqldump -uroot -p123456 -T /var/lib/mysql-files/ demo test

注:1)目标目录/var/lib/mysql-files/应为系统变量secure_file_priv指定的绝对目录,

2)目标目录下会生成*.sql表结构文件和*.txt表数据文件(表数据文件非insert形式)

mysql/source/load/mysqlimport数据导入工具

mysql(cmd命令)

mysql -u用户名 -p密码 < 数据.sql

sourcemysql命令

如果需要导入sql文件,可以使用mysql中的source 指令

语法 :

source 数据.sql

示例 :

mysql> source /var/lib/mysql-files/demo.sql

load(mysql命令)

a.客户端连接服务端时,加上参数--local-infile:mysql --local-infile -u用户名 -p密码

b.设置全局参数,开启本地导入文件数据开关:set global local_infile = 1

c.执行load导入:load data local infile '文件路径' into table '表名' fields terminated by '字段间分隔符' lines terminated by '换行符'

注:

  1. 如果指定LOCAL关键词,则表明从客户主机上按路径读取文件。如果没有指定,则文件在服务器上按路径读取文件。
  2. 列分隔符默认为定位符,行尾标记默认为换行符。
  3. FIELDS和LINES子句可选,但是如果两个同时被指定,FIELDS子句必须出现在LINES子句之前。
  4. FIELDS子句的选项有TERMINATED BY、[OPTIONALLY] ENCLOSED BY和ESCAPED BY。
  5. 如果文件中的列与表中的列不一致,则需要在FIELDS和LINES子句(若有)之后指定列的顺序为文件中的顺序,例如:
    LOAD DATA LOCAL INFILE 'user.txt' INTO TABLE user FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\r\n' (name, id);

mysqlimportcmd命令

mysqlimport 客户端提供了 LOAD DATA INFILEQL 语句的一个命令行接口。mysqlimport 的大多数选项直接对应 LOAD DATA INFILE 子句。

mysqlimport是客户端数据导入工具,用来导入mysqldump 加 -T 参数后导出的文本文件。

语法 :

mysqlimport [options] db_name textfile1 [textfile2...]

示例 :

$ mysqlimport -uroot -proot demo /var/lib/mysql-files/demo.txt

$ mysqlimport -uroot -proot --local \

--fields-terminated-by=":" --lines-terminated-by="\r\n" \

--columns=name,id demo /var/lib/mysql-files/demo.txt

注:目标目录/var/lib/mysql-files/应为系统变量secure_file_priv指定的绝对目录

    1. ySQL日志

错误日志

该日志是默认开启的,默认路径Linux下为/var/log/mysqld.log,Windows下为%MYSQL安装目录%/data/<计算机名>.err

查看日志位置:show variables like '%log_error%';

二进制日志

介绍

二进制日志(BINLOG)记录了所有的写操作(DDL和DML)。

作用:主从复制和数据恢复。

在版本8中,二进制日志默认开启,涉及到的参数如下:

show variables like '%log_bin%';

参数说明:

log_bin_basename:当前数据库服务器的binlog日志的基础名称(前缀)。

log_bin_index:binlog的索引文件,里面记录了当前服务器关联的binlog文件有哪些。

刷盘机制

由参数sync_binlog控制:

值为0:系统自行判断何时写入磁盘;

值为1:默认值,每次事务提交时写入磁盘;

值为N:每N个事务提交,才会将binlog写入磁盘。

日志格式

由参数binlog_format控制:

STATEMENT:记录SQL写操作语句。性能高,但是无法保证系统变量、函数、触发器、存储过程的结果一致性(如now()、user()、@@hostname)

ROW:(默认)记录每一行的数据变更。性能差,多行数据变更时产生大量日志,尤其是alter table时日志会暴涨。

MIXED:混合了STATEMENT和ROW两种格式,有函数时用ROW,没函数时用STATEMENT,但是无法识别系统变量

如果我们需要配置二进制日志的格式,只需要在 /etc/my.cnf 中配置binlog_format参数即可。

查看

由于日志是以二进制方式存储的,不能直接读取,需要通过二进制日志查询工具mysqlbinlog来查看,使用方法往上搜索“mysqlbinlog”关键词。

删除

对于比较繁忙的业务系统,每天生成的binlog数据巨大,如果长时间不清除,将会占用大量磁盘空间。可以通过以下几种方式清理日志:

reset master 删除全部 binlog 日志,删除之后,日志编号,将从 binlog.000001重新开始

purge master logs to 'binlog.*' 删除 * 编号之前的所有日志

purge master logs before 'yyyy-mm-dd hh24:mi:ss' 删除日志为 "yyyy-mm-dd hh24:mi:ss" 之前产生的所有日志

也可以在mysql8的配置文件中配置二进制日志的过期时间(默认为30天),设置了之后,二进制日志过期会自动删除。

show variables like '%binlog_expire_logs_seconds%';

事务日志

redo log

undo log

查询日志

查询日志中记录了客户端的所有操作语句,而二进制日志不包含查询数据的SQL语句。默认情况下,查询日志是未开启的。

查看参数:show variables like '%general%';

如果需要开启查询日志,可以修改MySQL的配置文件 /etc/my.cnf 文件,添加如下内容:

#是否开启查询日志,0代表关闭,1代表开启

general_log=1

#设置查询日志的文件名,默认名称为<host_name>.log,位于/var/lib/mysql/目录下

general_log_file=mysql_query.log

查询日志谨慎开启,所有的增删改查都会记录,长时间运行后,查询日志将会非常大。

慢查询日志

慢查询日志记录了所有执行时间大于long_query_time并且扫描记录数大于等于min_examined_row_limit的所有的SQL语句的日志,

long_query_time默认为10秒,最小为0,精度可到微秒。

慢查询日志默认未开启。如要开启,需要在MySQL的配置文件/etc/my.cnf中配置如下参数:

#慢查询日志

slow_query_log=1

#执行时间参数

long_query_time=2

默认情况下,不会记录管理语句,也不会记录不使用索引进行查找的查询。如要记录,增加以下参数:

#记录执行较慢的管理语句

log_slow_admin_statements=1

#记录执行较慢的未使用索引的语句

log_queries_not_using_indexes=1

配置完后需重启MySQL服务才可生效。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/410151
推荐阅读
相关标签
  

闽ICP备14008679号