赞
踩
- 防火墙:6是iptables,7是firewalld
- 启动服务的命令:6是service,7是systemctl
rpm -qa | grep -i mysql # -i 忽略大小写
systemctl status mysqld.service
1.关闭 mysql 服务
systemctl stop mysqld.service
2.查看当前 mysql 安装状况
rpm -qa | grep -i mysql
或
yum list installed | grep mysql
3.卸载上述命令查询出的已安装程序
find / -name mysql
rm -rf xxx
5.删除 my.cnf
rm -rf /etc/my.cnf
MySQL Community Server 社区版本
,开源免费,自由下载,但不提供官方技术支持,适用于大多数普通用户。
MySQL Enterprise Edition 企业版本
,需付费,不能在线下载,可以试用30天。提供了更多的功能和更完备的技术支持,更适合于对数据库的功能和可靠性要求较高的企业客户。
MySQL Cluster 集群版
,开源免费。用于架设集群服务器,可将几个MySQL Server封装成一个Server。需要在社区版或企业版的基础上使用。
MySQL Cluster CGE 高级集群版
,需付费。
1.下载地址
2.打开官网,点击DOWNLOADS
然后,点击 MySQL Community(GPL) Downloads
3.点击 MySQL Community Server
4.在General Availability(GA) Releases中选择适合的版本
如果安装Windows 系统下MySQL ,推荐下载 MSI安装程序 ;点击 Go to Download Page 进行下载即可
Windows下的MySQL安装有两种安装程序
mysql-installer-web-community-8.0.25.0.msi
下载程序大小:2.4M;安装时需要联网安装组件
mysql-installer-community-8.0.25.0.msi
下载程序大小:435.7M;安装时离线安装即可。推荐
。
5.Linux系统下安装MySQL的几种方式
5.1 Linux系统下安装软件的常用三种方式:
方式1:rpm命令
使用rpm命令安装扩展名为".rpm"的软件包。
.rpm包的一般格式:
方式2:yum命令
需联网,从 互联网
获取 的yum源,直接使用yum命令安装。
方式3:编译安装源码包
针对 tar.gz
这样的压缩格式,要用tar命令来解压;如果是其它压缩格式,就使用其它命令。
5.2Linux系统下安装MySQL,官方给出多种安装方式
安装方式 | 特点 |
---|---|
rpm | 安装简单,灵活性差,无法灵活选择版本、升级 |
rpm repository | 安装包极小,版本安装简单灵活,升级方便,需要联网安装 |
通用二进制包 | 安装比较复杂,灵活性高,平台通用性好 |
源码包 | 安装最复杂,时间长,参数设置灵活,性能好 |
Red Hat Enterprise Linux
https://downloads.mysql.com/archives/community/
直接点Download下载RPM Bundle全量包。包括了所有下面的组件。不需要一个一个下载了。1.检查/tmp临时目录权限(必不可少)
由于mysql安装过程中,会通过mysql用户在/tmp目录下新建tmp_db文件,所以请给/tmp较大的权限。执 行 :
chmod -R 777 /tmp
2.安装前,检查依赖
rpm -qa|grep libaio
rpm -qa|grep net-tools
rpm -qa|grep net-tools
1.将安装程序拷贝到/opt目录下
在mysql的安装文件目录下执行:(必须按照顺序执行)
rpm -ivh mysql-community-common-8.0.25-1.el7.x86_64.rpm
rpm -ivh mysql-community-client-plugins-8.0.25-1.el7.x86_64.rpm
rpm -ivh mysql-community-libs-8.0.25-1.el7.x86_64.rpm
rpm -ivh mysql-community-client-8.0.25-1.el7.x86_64.rpm
rpm -ivh mysql-community-server-8.0.25-1.el7.x86_64.rpm
rpm
是Redhat Package Manage缩写,通过RPM的管理,用户可以把源代码包装成以rpm为扩展名的文件形式,易于安装-i
,–install 安装软件包-v
, --verbose 提供更多的详细信息输出-h
, --hash 软件包安装的时候列出哈希标记 (和 -v 一起使用效果更好),展示进度条一个命令:
yum remove mysql-libs
解决,清除之前安装过的依赖即可
**
执行如下命令,如果成功表示安装mysql成功。类似java -version如果打出版本等信息
mysql --version
或
mysqladmin --version
执行如下命令,查看是否安装成功。需要增加 -i 不用去区分大小写,否则搜索不到。
rpm -qa|grep -i mysql
为了保证数据库目录与文件的所有者为 mysql 登录用户,如果你是以 root 身份运行 mysql 服务,需要执行下面的命令初始化:
mysqld --initialize --user=mysql
说明: --initialize 选项默认以“安全”模式来初始化,则会为 root 用户生成一个密码并将 该密码标记为过期
,登录后你需要设置一个新的密码。生成的 临时密码
会往日志中记录一份。
查看密码:
cat /var/log/mysqld.log
root@localhost: 后面就是初始化的密码
#加不加.service后缀都可以
启动:systemctl start mysqld.service
关闭:systemctl stop mysqld.service
重启:systemctl restart mysqld.service
查看状态:systemctl status mysqld.service
mysqld 这个可执行文件就代表着 MySQL 服务器程序,运行这个可执行文件就可以直接启动一个服务器进程。
查看进程:
ps -ef | grep -i mysql
systemctl list-unit-files|grep mysqld.service
默认是enabled。
systemctl enable mysqld.service
systemctl disable mysqld.service
通过
mysql -hlocalhost -P3306 -uroot -p
进行登录,在Enter password:录入初始化密码
ALTER USER ‘root’@‘localhost’ IDENTIFIED BY ‘new_password’;
在用SQLyog或Navicat中配置远程连接Mysql数据库时遇到如下报错信息,这是由于Mysql配置了不支持远 程连接引起的。
telnet ip地址 端口号
注:telnet不能使用说明windows没有设置(百度)
方式一:关闭防火墙
CentOS6 :service iptables stop
CentOS7:
systemctl start firewalld.service
systemctl status firewalld.service
systemctl stop firewalld.service
#设置开机启用防火墙
systemctl enable firewalld.service
#设置开机禁用防火墙
systemctl disable firewalld.service
方式二:开放端口
查看开放的端口号: firewall-cmd --list-all
设置开放的端口号:
firewall-cmd --add-service=http --permanent
firewall-cmd --add-port=3306/tcp --permanent
重启防火墙:
firewall-cmd --reload
use mysql;
select Host,User from user;
修改Host为通配符%
Host列指定了允许用户登录所使用的IP,比如user=root Host=192.168.1.1。这里的意思就是说root用户只能通过192.168.1.1的客户端去访问。 user=root Host=localhost,表示只能通过本机客户端去访问。而 % 是个 通配符 ,如果Host=192.168.1.%,那么就表示只要是IP地址前缀为“192.168.1.”的客户端都可以连接。如果 Host=% ,表示所有IP都有连接权限。
注意:在生产环境下不能为了省事将host设置为%,这样做会存在安全问题,具体的设置可以根据生产 环境的IP进行设置。
update user set host = '%' where user ='root';
Host设置了“%”后便可以允许远程访问。
Host修改完成后记得执行flush privileges使配置立即生效:
flush privileges;
测试:
如果是MySQL5.7
版本,接下来就可以使用SQLyog或者Navicat成功连接至MySQL了。
如果是MySQL8
版本,连接时还会出现如下问题
配置新连接报错:错误号码 2058,分析是 mysql 密码加密方法变了。
解决方法
:Linux下 mysql -u root -p 登录你的 mysql 数据库,然后 执行这条SQL:
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'abc123';
然后在重新配置SQLyog的连接,则可连接成功了,OK。
在MySQL 8.0版本之前,默认字符集为
atin1
,utf8字符集指向的是utf8mb3
。网站开发人员在数据库设计的时候往往会将编码修改为utf8字符集。如果遗忘修改默认的编码,就会出现乱码的问题。从MySQL 8.0开始,数据库的默认编码将改为utf8mb4
,从而避免上述乱码的问题。
MySQL有4个级别的字符集和比较规则,分别是:
show variables like ‘character%’;
#或者
show variables like ‘%char%’;
character_set_server: 服 务 器 级 别 的 字 符 集
character_set_database: 当 前 数 据 库 的 字 符 集
character_set_client: 服 务 器 解 码 请 求 时 使 用 的 字 符 集
character_set_connection:服务器处理请求时会把请求字符串从character_set_client转为character_set_connection
character_set_results:服务器向客户端返回数据时使用的字符集
在MySQL5.7或之前的版本中,在文件最后加上中文字符集配置:(重启)
vim /etc/my.cnf
character_set_server=utf8
我们在创建和修改数据库的时候可以指定该数据库的字符集和比较规则,具体语法如下:
CREATE DATABASE
数据库名
[[DEFAULT
]CHARACTER
SET
字符集名称]
[[DEFAULT
]COLLATE
比较规则名称];
ALTER
DATABASE
数据库名
[[DEFAULT
]CHARACTER
SET
字符集名称]
[[DEFAULT
]COLLATE
比较规则名称];
示例如下:
alter database dbtest1 character set ‘utf8’;
注:但是原库、原表的设定不会发生变化,参数修改只对新建的数据库生效。
我们也可以在创建和修改表的时候指定表的字符集和比较规则,语法如下:
CREATE TABLE
表名 (列的信息)
[[DEFAULT
]CHARACTER
SET
字符集名称]
[[DEFAULT
]COLLATE
比较规则名称];
ALTER
TABLE
表名
[[DEFAULT
]CHARACTER
SET
字符集名称]
[[DEFAULT
]COLLATE
比较规则名称];
示例如下:
修改已创建 数据表 的字符集
alter table t_emp convert to character set ‘utf8’;
注意:但是原有的数据如果是用非’utf8’编码的话,数据本身编码不会发生改变。已有数据需要导 出或删除,然后重新插入。
对于存储字符串的列,同一个表中的不同的列也可以有不同的字符集和比较规则。我们在创建和修改列 定义的时候可以指定该列的字符集和比较规则,语法如下:
CREATE
TABLE
表名(
列名 字符串类型 [CHARACTER
SET
字符集名称] [COLLATE
比较规则名称],
其他列…
);
我们介绍的这4个级别字符集和比较规则的联系如下:
utf8 字符集表示 一个字符 需要使用 1~4 个字节,但是我们常用的一些字符使用1~3个字节就可以表示了。而字符集表示一个字符所用的最大字节长度,在某些方面会影响系统的存储和性能,所以设计MySQL的设计者偷偷的定义了两个概念:
MySQL版本一共支持41种字符集,其中的Default collation 列表示这种字符集中一种默认的比较规则,里面包含着该比较规则主要作用于哪种语言,比如utf8_polish_ci 表示以波兰语的规则比较, utf8_spanish_ci 是以西班牙语的规则比较, utf8_general_ci 是一种通用的比较规则。
后缀表示该比较规则是否区分语言中的重音、大小写。具体如下:
后缀 | 英文释义 | 描述 |
---|---|---|
_ai | accent insensitive | 不区分重音 |
_as | accent sensitive | 区分重音 |
_ci | case insensitive | 不区分大小写 |
_cs | case sensitive | 区分大小写 |
_bin | binary | 以二进制方式比较 |
#查看GBK字符集的比较规则
SHOW
COLLATION
LIKE
‘gbk%’;#查看UTF-8字符集的比较规则
SHOW
COLLATION
LIKE
‘utf8%’;
#查看服务器的字符集和比较规则
SHOW
VARIABLES
LIKE
‘%_server’;#查看数据库的字符集和比较规则
SHOW
VARIABLES
LIKE
‘%_database’;#查看具体数据库的字符集
SHOW
CREATE
DATABASE
dbtest1;#修改具体数据库的字符集
ALTER
DATABASE
dbtest1DEFAULT
CHARACTER
SET
‘utf8’COLLATE
‘utf8_general_ci’;
#查看表的字符集
show
create
table
employees;
#查看表的比较规则
show
table
status
from
atguigudblike
‘employees’;
在 SQL 中,关键字和函数名是不用区分字母大小写的,比如 SELECT、WHERE、ORDER、GROUP BY 等关键字,以及 ABS、MOD、ROUND、MAX 等函数名。
不过在 SQL 中,你还是要确定大小写的规范,因为在 Linux 和 Windows 环境下,你可能会遇到不同的大小写问题。 windows系统默认大小写不敏感 ,但是 linux系统是大小写敏感的 。
通过如下命令查看:
SHOW VARIABLES LIKE
‘%lower_case_table_names%’
lower_case_table_names参数值的设置:
默认为0,大小写敏感 。
设置1,大小写不敏感。创建的表,数据库都是以小写形式存放在磁盘上,对于sql语句都是转 换为小写对表和数据库进行查找。
设置2,创建的表和数据库依据语句上格式存放,凡是查找都是转换为小写进行。
MySQL在Linux下数据库名、表名、列名、别名大小写规则是这样的: 1、数据库名、表名、表的别名、变量名是严格区分大小写的 2、关键字、函数名称在 SQL 中不区分大小写; 3、列名(或字段名)与列的别名(或字段别名)在所有的情况下均是忽略大小写的; MySQL在Windows的环境下全部不区分大小写 |
当想设置为大小写不敏感时,要在 my.cnf 这个配置文件 [mysqld] 中加入
lower_case_table_names=1 ,然后重启服务器。
此参数适用于MySQL5.7
。在MySQL 8下禁止
在重新启动 MySQL 服务时将lower_case_table_names 设置成不同于初始化 MySQL 服务时设置的lower_case_table_names 值。如果非要将MySQL8设置为大小写不敏感,具体步骤为:1、停止MySQL服务
2、删除数据目录,即删除 /var/lib/mysql 目录
3、在MySQL配置文件( /etc/my.cnf )中添加 lower_case_table_names=1
4、启动MySQL服务
MySQL数据库文件的存放路径:/var/lib/mysql/
mysql> show variables like ‘datadir’;
±--------------±----------------+
| Variable_name | Value |
±--------------±----------------+
| datadir | /var/lib/mysql/ |
±--------------±----------------+
1 row in set (0.04 sec)
从结果中可以看出,在我的计算机上MySQL的数据目录就是 /var/lib/mysql/ 。
相关命令目录:/usr/bin(mysqladmin、mysqlbinlog、mysqldump等命令)和/usr/sbin。
配置文件目录:/usr/share/mysql-8.0(命令及配置文件),/etc/mysql(如my.cnf)
查看一下在我的计算机上当前有哪些数据库:
mysql> SHOW DATABASES;
可以看到有4个数据库是属于MySQL自带的系统数据库。
举例: 数据库a , 表b 。
1、如果表b采用 InnoDB ,data\a中会产生1个或者2个文件:
2、如果表b采用 MyISAM ,data\a中会产生3个文件:
启动MySQL服务后,可以通过mysql命令来登录MySQL服务器,命令如下:
mysql –h hostname|hostIP –P port –u username –p DatabaseName –e “SQL语句”
举例:
mysql -uroot -p -hlocalhost -P3306 mysql -e “select host,user from user”
下面详细介绍命令中的参数:
CREATE USER语句的基本语法形式如下:
CREATE
USER
用户名 [IDENTIFIED BY ‘密码’][,用户名 [IDENTIFIED
BY
‘密码’]];
举例:
CREATE USER zhang3 IDENTIFIED BY ‘123123’; # 默认host是 %
CREATE USER 'zhang3 '@‘localhost’ IDENTIFIED BY ‘123123’;
修改用户名:
UPDATE
mysql.userSET
USER
=‘li4’WHERE
USER
=‘wang5’;
FLUSH
PRIVILEGES
;
方式1:使用DROP方式删除(推荐)
使用DROP USER语句来删除用户时,必须用于DROP USER权限。DROP USER语句的基本语法形式如下:
DROP
USER
user[,user]…;
举例:
DROP USER li4 ; # 默认删除host为%的用户
DROP USER ‘kangshifu’@‘localhost’;
方式2:使用DELETE方式删除
DELETE
FROM
mysql.userWHERE
Host
=’hostname’AND
User
=’username’;
FLUSH
PRIVILEGES
;
举例:DELETE FROM mysql.user WHERE Host=‘localhost’ AND User=‘Emily’;
FLUSH PRIVILEGES;
注意:不推荐通过 DELETE FROM USER u WHERE USER='li4' 进行删除,系统会有残留信息保留。而drop user命令会删除用户以及对应的权限,执行命令后你会发现mysql.user表和mysql.db表的相应记录都消失了。 |
旧的写法如下 :
#修改
当前
用户的密码:(MySQL5.7测试有效)
SET
PASSWORD
=PASSWORD
(‘123456’);
这里介绍 推荐的写法 :
ALTER USER
命令来修改当前用户密码 基本语法如下:
ALTER
USER
USER()
IDENTIFIED
BY
new_password’;
SET
语句来修改当前用户密码 基本语法如下:
SET
PASSWORD
=‘new_password’;
该语句会自动将密码加密后再赋给当前用户。
ALTER
语句来修改普通用户的密码 ** 基本语法如下:
ALTER
USER
user
[IDENTIFIED
BY
‘新密码’] [,user[IDENTIFIED BY ‘新密码’]]…;
举例:
ALTER USER ‘lisi’ IDENTIFIED BY ‘1234’
SET
命令来修改普通用户的密码 基本语法如下:
SET
PASSWORD
FOR
‘username’@
‘hostname’=‘new_password’;
UPDATE
语句修改普通用户的密码(不推荐
)UPDATE MySQL.user SET authentication_string=PASSWORD(“123456”) WHERE User = “username” AND Host = “hostname”;
1.密码过期策略
ALTER
USER
user
PASSWORD
EXPIRE;
练习:
ALTER USER ‘kangshifu’@‘localhost’ PASSWORD EXPIRE;
方式①:使用SQL语句更改该变量的值并持久化
·SET· PERSIST default_password_lifetime = 180; # 建立全局策略,设置密码每隔180天过期
方式②:配置文件my.cnf中进行维护
[mysqld]
default_password_lifetime=180 #建立全局策略,设置密码每隔180天过期
手动设置指定时间过期方式2:单独设置
每个账号既可延用全局密码过期策略,也可单独设置策略。在 CREATE USER 和 ALTER USER 语句上加入 PASSWORD EXPIRE 选项可实现单独设置策略。下面是一些语句示例。
#设置kangshifu账号密码每90天过期:
CREATE
USER
‘kangshifu’@‘localhost’PASSWORD
EXPIREINTERVAL
90 DAY;
ALTER
USER
‘kangshifu’@‘localhost’PASSWORD
EXPIREINTERVAL
90 DAY;
#设置密码永不过期:
CREATE
USER
‘kangshifu’@‘localhost’PASSWORD
EXPIRE NEVER;
ALTER
USER
‘kangshifu’@‘localhost’PASSWORD
EXPIRE >NEVER;
#延用全局密码过期策略:
CREATE
USER
‘kangshifu’@‘localhost’PASSWORD
EXPIREDEFAULT
;
ALTER
USER
‘kangshifu’@‘localhost’PASSWORD
EXPIRE >DEFAULT
;
2.密码重用策略
手动设置密码重用方式1:全局
方式①:使用SQL
SET
PERSIST password_history = 6; #设置不能选> 择最近使用过的6个密码
SET
PERSIST password_reuse_interval = 365; #设置不能选择最近一年内的密码
方式②:my.cnf配置文件
[mysqld]
password_history=6 #设置不能选择最近使用过的6个密码
password_reuse_interval=365 #设置不能选择最近一年内的密码
手动设置密码重用方式2:单独设置
#不能使用最近5个密码:
CREATE
USER
‘kangshifu’@‘localhost’PASSWORD
HISTORY 5;
ALTER
USER
‘kangshifu’@‘localhost’PASSWORD
HISTORY 5;
#不能使用最近365天内的密码:
CREATE
USER
‘kangshifu’@‘localhost’PASSWORD
REUSEINTERVAL
365 DAY;
ALTER
USER
‘kangshifu’@‘localhost’PASSWORD
REUSEINTERVAL
365 DAY;
#既不能使用最近5个密码,也不能使用365天内的密码
CREATE
USER
‘kangshifu’@‘localhost’
PASSWORD
HISTORY 5
PASSWORD
REUSEINTERVAL
365 DAY;
ALTER
USER
‘kangshifu’@‘localhost’
PASSWORD
HISTORY 5
PASSWORD
REUSEINTERVAL
365 DAY;
MySQL到底都有哪些权限呢?
mysql>
show privileges
;
权限控制主要是出于安全因素,因此需要遵循以下几个 经验原则 :
给用户授权的方式有 2 种,分别是通过把 角色赋予用户给用户授权 和 直接用户授权。用户是数据库的
使用者,我们可以通过给用户授予访问数据库中资源的权限,来控制使用者对数据库的访问,消除安全 隐患。
授权命令:
#该权限如果发现没有该用户,则会直接新建一个用户。
GRANT
权限1,权限2,…权限nON
数据库名称.表名称 TO 用户名@用户地址 [IDENTIFIEDBY
‘密码口令’];
举例:
给li4用户用本地命令行方式,授予atguigudb这个库下的所有表的插删改查的权限。
GRANT SELECT,INSERT,DELETE,UPDATE ON
atguigudb.*TO
li4@localhost ;
授予通过网络方式登录的joe用户 ,对所有库所有表的全部权限,密码设为123。注意这里唯独不包括grant的权限
GRANT ALL PRIVILEGES ON
*.*TO
joe@’%’ IDENTIFIED BY ‘123’;
ALL PRIVILEGES
:表示所有权限,你也可以使用SELECT,UPDATE 等权限。
ON
: 用来指定权限针对那些库和表
*.*
:前面的 *
用来指定数据库名,后面的 *
用来指定表名。这里的表示所有的表。
TO
: 表示将权限赋予某个用户
li4@localhost
:表示li4用户,@后面接限制的主机,可以是IP、IP段、域名以及%,%表示任何地方。注意:这里的百分号不包括本地,以前碰到过给某个用户设置了%允许任何地方登陆,但是在本地登陆不了,这个和版本有关系,遇到这个问题再加一个localhost的用户就可以了。
IDENTIFIED BY
:指定用户的登录密码
WITH GRANT OPTION
这个选项即可,表示该用户可以将自己拥有的权限授权给别人。经常有人在创建操作用户的时候不指定 WITH GRANT OPTION 选项导致后来该用户不能使用grant 命令创建用户或者给其他用户授权权限叠加
,比如你先给一个用户添加一个select权限,然后又给用户添加一个insert 权限,那么就该给用户就同时拥有了SELECT和INSERT 权限注意:不推荐通过 我们在开发应用的时候,经常会遇到一种需求,就是要根据用户的不同,对数据进行横向和纵向的 分组。 ● 所谓横向的分组,就是指用户可以接触到的数据的范围,比如可以看到哪些表的数据; ● 所谓纵向的分组,就是指用户对接触到的数据能访问到什么程度,比如能看、能改,甚至是 删除。 |
● 查看当前用户权限
SHOW GRANTS
;
#或
SHOW GRANTS FOR CURRENT_USER
;
#或
SHOW GRANTS FOR CURRENT_USER()
;
● 查看某用户的全局权限
SHOW
GRANTS
FOR
‘user’@‘主机地址’ ;
收回权限就是取消已经赋予用户的某些权限。收回用户不必要的权限可以在一定程度上保证系统的安全 性。 MySQL中使用 REVOKE 语句 取消用户的某些权限。使用REVOKE收回权限之后,用户账户的记录将从db、host、tables_priv和columns_priv表中删除,但是用户账户记录仍然在user表中保存(删除user表中 的账户记录使用DROP USER语句)。
注意:在将用户账户从user表删除之前,应该收回相应用户的所有权限。
REVOKE
权限1,权限2,…权限n ON 数据库名称.表名称 FROM 用户名@用户地址;
举例:
#收回全库全表的所有权限
REVOKE ALL PRIVILEGES ON
*.*FROM
joe@’%’;
#收回mysql库下的所有表的插删改查权限
REVOKE SELECT,INSERT,UPDATE,DELETE ON
mysql.*FROM
joe@localhost;
注意:须用户重新登录后才能生效
总结 有一些程序员喜欢使用ROOT超级用户来访问数据库,完全把 权限控制 放在 应用层面实现 |
USER
表user表是MySQL中最重要的一个权限表, 记录用户账号和权限信息 ,有49个字段。如下图:
这些字段可以分成4类,分别是范围列(或用户列)、权限列、安全列和资源控制列。
1.范围列(或用户列)
%
表示所有远程通过 TCP方式的连接
IP 地址
如 (192.168.1.2、127.0.0.1) 通过制定ip地址进行的TCP方式的连接
机器名
通过制定网络中的机器名进行的TCP方式的连接
::1
IPv6的本地ip地址,等同于IPv4的 127.0.0.1
localhost
本地方式通过命令行方式的连接 ,比如mysql -u xxx -p xxx 方式的连接。
● 所有密码串通过 password(明文字符串) 生成的密文字符串。MySQL 8.0 在用户管理方面增加了
角色管理,默认的密码加密方式也做了调整,由之前的 SHA1 改为了 SHA2 ,不可逆 。同时
加上 MySQL 5.7 的禁用用户和用户过期的功能,MySQL 在用户管理方面的功能和安全性都较之
前版本大大的增强了
● mysql 5.7 及之后版本的密码保存到 authentication_string 字段中不再使用password 字
段。
2.权限列
3.安全列
安全列只有6个字段,其中两个是ssl相关的(ssl_type、ssl_cipher),用于 加密 ;两个是x509相关的(x509_issuer、x509_subject),用于标识用户 ;另外两个Plugin字段用于 验证用户身份 的插件,该字段不能为空。如果该字段为空,服务器就使用内建授权验证机制验证用户身份。
4.资源控制列
资源控制列的字段用来 限制用户使用的资源 ,包含4个字段,分别为:
①max_questions,用户每小时允许执行的查询操作次数; ②max_updates,用户每小时允许执行的更新操作次数;
③max_connections,用户每小时允许执行的连接操作次数; ④max_user_connections,用户允许同时建立的连接次数。
#查看字段
DESC
mysql.user;
#查看用户, 以列的方式显示数据:
SELECT * FROM mysql.user \G;
#查询特定字段:
SELECT host,user,authentication_string,select_priv,insert_priv,drop_priv FROM mysql.user;
db
表使用DESCRIBE查看db表的基本结构:
DESCRIBE
mysql.db;
tables_priv
表和columns_priv
表tables_priv表用来对表设置操作权限,columns_priv表用来对表的某一列设置权限 。tables_priv表和columns_priv表的结构分别如图:
desc
mysql.tables_priv;
desc
mysql.columns_priv;
tables_priv表有8个字段,分别是Host、Db、User、Table_name、Grantor、Timestamp、Table_priv和 Column_priv,各个字段说明如下:
procs_priv
表procs_priv表可以对 存储过程和存储函数设置操作权限 ,表结构如图:
desc
mysql.procs_priv;
1连接核实阶段
当用户试图连接MySQL服务器时,服务器基于用户的身份以及用户是否能提供正确的密码验证身份来确 定接受或者拒绝连接。即客户端用户会在连接请求中提供用户名、主机地址、用户密码,MySQL服务器 接收到用户请求后,会使用user表中的host、user和authentication_string这3个字段匹配客户端提供信 息。
服务器只有在user表记录的Host和User字段匹配客户端主机名和用户名,并且提供正确的密码时才接受 连接。如果连接核实没有通过,服务器就完全拒绝访问;否则,服务器接受连接,然后进入阶段2等待 用户请求。
2请求核实阶段
一旦建立了连接,服务器就进入了访问控制的阶段2,也就是请求核实阶段。对此连接上进来的每个请 求,服务器检查该请求要执行什么操作、是否有足够的权限来执行它,这正是需要授权表中的权限列发 挥作用的地方。这些权限可以来自user、db、table_priv和column_priv表。
确认权限时,MySQL首先 检查user表 ,如果指定的权限没有在user表中被授予,那么MySQL就会继续检查db表 ,db表是下一安全层级,其中的权限限定于数据库层级,在该层级的SELECT权限允许用户查看指定数据库的所有表中的数据;如果在该层级没有找到限定的权限,则MySQL继续 检查tables_priv表 以及 columns_priv表 ,如果所有权限表都检查完毕,但还是没有找到允许的权限操作,MySQL将 返回错误信息 ,用户请求的操作不能执行,操作失败。
提示: MySQL通过向下层级的顺序(从user表到columns_priv表)检查权限表,但并不是所有的权限都要执行该过程。例如,一个用户登录到MySQL服务器之后只执行对MySQL的管理操作,此时只 涉及管理权限,因此MySQL只检查user表。另外,如果请求的权限操作不被允许,MySQL也不会继 续检查下一层级的表。 |
引入角色的目的是 方便管理拥有相同权限的用户 。恰当的权限设定,可以确保数据的安全性,这是至关重要的。
创建角色使用 CREATE ROLE
语句,语法如下:
CREATE
ROLE ‘role_name’[@‘host_name’] [,‘role_name’[@‘host_name’]]…
角色名称的命名规则和用户名类似。如果 host_name省略,默认为%,role_name不可省略 不可为空。
举例
练习:我们现在需要创建一个经理的角色,就可以用下面的代码:
CREATE
ROLE ‘manager’@‘localhost’;
创建角色之后,默认这个角色是没有任何权限的,我们需要给角色授权。给角色授权的语法结构是:
GRANT privileges ON table_name TO
‘role_name’[@‘host_name’];
上述语句中privileges代表权限的名称,多个权限以逗号隔开。可使用SHOW语句查询权限名称,图11-43 列出了部分权限列表。
SHOW PRIVILEGES
\G;
举例:
练习1:我们现在想给经理角色授予商品信息表、盘点表和应付账款表的只读权限,就可以用下面的代码 来实现:
GRANT SELECT ON
demo.settlementTO
‘manager’;
GRANT SELECT ON
demo.goodsmasterTO
‘manager’;
GRANT SELECT ON
demo.invcountTO
‘manager’;
赋予角色权限之后,我们可以通过 SHOW GRANTS 语句,来查看权限是否创建成功了:
SHOW GRANTS FOR
‘manager’;
±------------------------------------------------------+
| Grants for manager@% |
±------------------------------------------------------+
| GRANT USAGE ON . TOmanager
@%
|
| GRANT SELECT ONdemo
.goodsmaster
TOmanager
@%
|
| GRANT SELECT ONdemo
.invcount
TOmanager
@%
|
| GRANT SELECT ONdemo
.settlement
TOmanager
@%
|
±------------------------------------------------------+
只要你创建了一个角色,系统就会自动给你一个“ USAGE ” 权限,意思是 连接登录数据库的权限 。代码的最后三行代表了我们给角色“manager”赋予的权限,也就是对商品信息表、盘点表和应付账款表的只读权 限。
结果显示,库管角色拥有商品信息表的只读权限和盘点表的增删改查权限。
角色授权后,可以对角色的权限进行维护,对权限进行添加或撤销。添加权限使用GRANT语句,与角色 授权相同。撤销角色或角色权限使用REVOKE语句。
修改了角色的权限,会影响拥有该角色的账户的权限。
撤销角色权限的SQL语法如下:
REVOKE privileges ON
tablenameFROM
‘rolename’;
举例
练习1:撤销school_write角色的权限。 (
1)使用如下语句撤销school_write角色的权限。
REVOKE INSERT, UPDATE, DELETE ON
school.*FROM
‘school_write’;
(2)撤销后使用SHOW语句查看school_write对应的权限,语句如下。
SHOW GRANTS FOR
‘school_write’;
当我们需要对业务重新整合的时候,可能就需要对之前创建的角色进行清理,删除一些不会再使用的角 色。删除角色的操作很简单,你只要掌握语法结构就行了。
DROP
ROLE role [,role2]…
注意, 如果你删除了角色,那么用户也就失去了通过这个角色所获得的所有权限。
举例
练习:执行如下SQL删除角色school_read。
DROP
ROLE ‘school_read’;
角色创建并授权后,要赋给用户并处于 激活状态 才能发挥作用。给用户添加角色可使用GRANT语句,语法形式如下:
GRANT
role [,role2,…]TO user
[,user2,…];
在上述语句中,role代表角色,user代表用户。可将多个角色同时赋予多个用户,用逗号隔开即可。
举例
练习:给kangshifu用户添加角色school_read权限。
(1)使用GRANT语句给kangshifu添加school_read权限,SQL语句如下。
GRANT
‘school_read’TO
‘kangshifu’@‘localhost’;
(2)添加完成后使用SHOW语句查看是否添加成功,SQL语句如下。
SHOW GRANTS FOR
‘kangshifu’@‘localhost’
(3)使用kangshifu用户登录,然后查询当前角色,如果角色未激活,结果将显示NONE。SQL语句如下。
SELECT
CURRENT_ROLE();
方式1:使用set default role 命令激活角色
举例:
SET DEFAULT
ROLEALL TO
‘kangshifu’@‘localhost’;
使用 SET DEFAULT ROLE
为下面4个用户默认激活所有已拥有的角色如下:
SET DEFAULT ROLE ALL TO
‘dev1’@‘localhost’,
‘read_user1’@‘localhost’,
‘read_user2’@‘localhost’,
‘rw_user1’@‘localhost’;
方式2:将activate_all_roles_on_login设置为ON
mysql>
show variables like
‘activate_all_roles_on_login’;
±----------------------------±------+
| Variable_name | Value |
±----------------------------±------+
| activate_all_roles_on_login | OFF |
±----------------------------±------+
1 row in set (0.00 sec)
SET GLOBAL
activate_all_roles_on_login=ON;
这条 SQL 语句的意思是,对 所有角色永久激活 。运行这条语句之后,用户才真正拥有了赋予角色的所有权限。
撤销用户角色的SQL语法如下:
REVOKE
roleFROM user
;
举例
练习:撤销kangshifu用户的school_read角色。
(1)撤销的SQL语句如下:
REVOKE
‘school_read’FROM
‘kangshifu’@‘localhost’;
(2)撤销后,执行如下查询语句,查看kangshifu用户的角色信息
SHOW GRANTS FOR
‘kangshifu’@‘localhost’;
执行发现,用户kangshifu之前的school_read角色已被撤销。
强制角色是给每个创建账户得默认角色,不需要手动设置,强制角色无法被 REVOKE
或者 DROP
方式1:服务启动前设置
[mysqld]
mandatory_roles=‘role1,role2@localhost,r3@%.atguigu.com’
方式2:运行时设置
SET PERSIST mandatory_roles = ‘role1,role2@localhost,r3@%.example.com’; #系统重启后仍然有效
SET GLOBAL mandatory_roles = ‘role1,role2@localhost,r3@%.example.com’; #系统重启后失效
那服务器进程对客户端进程发送的请求做了什么处理,才能产生最后的处理结果呢?这里以查询请求为 例展示:
系统(客户端)访问 MySQL 服务器前,做的第一件事就是 TCP 建立
经过三次握手建立连接成功后, MySQL 服务器对 TCP 传输过来的账号密码做身份认证、权限获取。
TCP连接连接收到请求后,必须要分配给一个线程专门与这个客户端的交互。所以还会有个线程池,去走后面的流程。每一个连接从线程池中获取线程,省去了创建和销毁线程的开销。
● 接收用户的SQL命令,并且返回用户需要查询的结果。比如SELECT … FROM就是调用SQL Interface
● MySQL支持DML(数据操作语言)、DDL(数据定义语言)、存储过程、视图、触发器、自定义函数等多种SQL语言接口
● 在解析器中对 SQL 语句进行语法分析、语义分析。将SQL语句分解成数据结构,并将这个结构传递到后续步骤,以后SQL语句的传递和处理就是基于这个结构的。如果在分解构成中遇到错误,那么就说明这个SQL语句是不合理的。
● 在SQL命令传递到解析器的时候会被解析器验证和解析,并为其创建 语法树 ,并根据数据字典丰富查询语法树,会 验证该客户端是否具有执行该查询的权限 。创建好语法树后,MySQL还会对SQl查询进行语法上的优化,进行查询重写。
● SQL语句在语法解析之后、查询之前会使用查询优化器确定 SQL 语句的执行路径,生成一个执行计划
● 这个执行计划表明应该 使用哪些索引 进行查询(全表检索还是使用索引检索),表之间的连接顺序如何,最后会按照执行计划中的步骤调用存储引擎提供的方法来真正的执行查询,并将 查询结果返回给用户。
● 它使用 “ 选取-投影-连接 ”策略进行查询。例如:
SELECT id,name FROM student WHERE gender = ‘女’;
这个SELECT查询先根据WHERE语句进行 选取 ,而不是将表全部查询出来以后再进行gender过滤。 这个SELECT查询先根据id和name进行属性 投影 ,而不是将属性全部取出以后再进行过滤,将这两个查询条件 连接 起来生成最终查询结果。
● MySQL内部维持着一些Cache和Buffer,比如Query Cache用来缓存一条SELECT语句的执行结
果,如果能够在其中找到对应的查询结果,那么就不必再进行查询解析、优化和执行的整个过 程了,直接将结果反馈给客户端。
● 这个缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,key缓存,权限缓存等 。
● 这个查询缓存可以在 不同客户端之间共享 。
● 从MySQL 5.7.20开始,不推荐使用查询缓存,并在 MySQL 8.0中删除 。
插件式存储引擎层( Storage Engines), 真正的负责了MySQL中数据的存储和提取,对物理服务器级别维护的底层数据执行操作 ,服务器通过API与存储引擎进行通信。不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取。
MySQL 8.0.25默认支持的存储引擎如下:
所有的数据,数据库、表的定义,表的每一行的内容,索引,都是存在 文件系统 上,以 文件 的方式存在的,并完成与存储引擎的交互。当然有些存储引擎比如InnoDB,也支持不使用文件系统直接管理裸设备,但现代文件系统的实现使得这样做没有必要了。在文件系统之下,可以使用本地磁盘,可以使用 DAS、NAS、SAN等各种存储系统。
MySQL架构图本节开篇所示。下面为了熟悉SQL执行流程方便,我们可以简化如下:
简化为三层结构: 1.连接层:客户端和服务器端建立连接,客户端发送 SQL 至服务器端; 2.SQL 层(服务层):对 SQL 语句进行查询处理;与数据库文件的存储方式无关; 3.存储引擎层:与数据库文件打交道,负责数据的存储和读取。 |
MySQL的查询流程:
SELECT employee_id,last_name FROM employees WHERE employee_id = 101
查询缓存是提前把查询结果缓存起来,这样下次不需要执行就可以直接拿到结果。需要说明的是,在MySQL 中的查询缓存,不是缓存查询计划,而是查询对应的结果。这就意味着查询匹配的 鲁棒性大大降低 ,只有 相同的查询操作才会命中查询缓存 。两个查询请求在任何字符上的不同(例如:空格、注释、大小写),都会导致缓存不会命中。因此 MySQL 的 查询缓存命中率不高 。
同时,如果查询请求中包含某些系统函数、用户自定义变量和函数、一些系统表,如 mysql 、information_schema、 performance_schema 数据库中的表,那这个请求就不会被缓存。以某些系统函数举例,可能同样的函数的两次调用会产生不一样的结果,比如函数 NOW ,每次调用都会产生最新的当前时间,如果在一个查询请求中调用了这个函数,那即使查询请求的文本信息都一样,那不同时间的两次 查询也应该得到不同的结果,如果在第一次查询时就缓存了,那第二次查询的时候直接使用第一次查询 的结果就是错误的!
此外,既然是缓存,那就有它 缓存失效的时候 。MySQL的缓存系统会监测涉及到的每张表,只要该表的
结构或者数据被修改,如对该表使用了 INSERT 、UPDATE 、DELETE、TRUNCATE、ALTER、TABLE 、 DROP 、TABLE 或 DROP DATABASE 语句,那使用该表的所有高速缓存查询都将变为无效并从高速缓存中删除!对于更新压力大的数据库 来说,查询缓存的命中率会非常低。
2.解析器 :在解析器中对 SQL 语句进行语法分析、语义分析。
分析器先做“ 词法分析 ”。你输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是什么,代表什么。 MySQL 从你输入的"select"这个关键字识别出来,这是一个查询语
句。它也要把字符串“T”识别成“表名 T”,把字符串“ID”识别成“列 ID”。
接着,要做“ 语法分析 ”。根据词法分析的结果,语法分析器(比如:Bison)会根据语法规则,判断你输入的这个 SQL 语句是否 满足 MySQL 语法 。
select department_id,job_id,avg(salary) from employees group by department_id;
如果SQL语句正确,则会生成一个这样的语法树:
3.优化器 :在优化器中会确定 SQL 语句的执行路径,比如是根据 全表检索 ,还是根据 索引检索 等。举例:如下语句是执行两个表的 join:
select
*from
test1join
test2using
(ID)
where
test1.name=‘zhangwei’and
test2.name=‘mysql高级课程’;
方案1:可以先从表 test1 里面取出 name='zhangwei'的记录的 ID 值,再根据 ID 值关联到表 test2,再判断 test2 里面 name的值是否等于 'mysql高级课程'。 方案2:可以先从表 test2 里面取出 name='mysql高级课程' 的记录的 ID 值,再根据 ID 值关联到 test1, 再判断 test1 里面 name的值是否等于 zhangwei。 这两种执行方法的逻辑结果是一样的,但是执行的效率会有不同,而优化器的作用就是决定选择使用哪一个方案。优化 器阶段完成后,这个语句的执行方案就确定下来了,然后进入执行器阶段。 |
4.执行器:
截止到现在,还没有真正去读写真实的表,仅仅只是产出了一个执行计划。于是就进入了 执行器阶段 。
在执行之前需要判断该用户是否 具备权限 。如果没有,就会返回权限错误。如果具备权限,就执行 SQL
查询并返回结果。在 MySQL8.0 以下的版本,如果设置了查询缓存,这时会将查询结果进行缓存。
select * from test where id=1;
调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是1,如果不是则跳过,如果是则将这行存在结果集中; 调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。 执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。 |
1.确认profiling 是否开启
mysql>
select
@@profiling;
#profiling=0 代表关闭,我们需要把 profiling 打开,即设置为 1
mysql>show variables like
‘profiling’;
mysql> set profiling=1;
2.多次执行相同SQL查询
然后我们执行一个 SQL 查询(你可以执行任何一个 SQL 查询):
mysql>
select
*from
employees;
3.查看profiles
查看当前会话所产生的所有 profiles:
mysql>
show profiles;
# 显示最近的几次查询
4.查看profile
mysql>
show profile
;
当然你也可以查询指定的 Query ID,比如:
mysql>show profile for query
7;
查询 SQL 的执行时间结果和上面是一样的。此外,还可以查询更丰富的内容:
mysql>
show profile
cpu,block iofor query
6;
上述操作在MySQL5.7中测试,发现前后两次相同的sql语句,执行的查询过程仍然是相同的。不是会使用缓存吗?这里我们需要 显式开启查询缓存模式 。在MySQL5.7中如下设置:
1.配置文件中开启查询缓存
在 /etc/my.cnf 中新增一行:
query_cache_type=1
2.重启mysql服务
systemctl restart mysqld
3.开启查询执行计划
由于重启过服务,需要重新执行如下指令,开启profiling。
mysql> set profiling=1;
4.执行语句两次:
mysql> select * from locations;
mysql> select * from locations;
5.查看profiles
6.查看profile
显示执行计划,查看两条SQL程序的执行步骤:
mysql>
show profile for query
1[2];
结论不言而喻。执行编号2时,比执行编号1时少了很多信息,从截图中可以看出查询语句直接从缓存中 获取数据。
随着Mysql版本的更新换代,其优化器也在不断的升级,优化器会分析不同执行顺序产生的性能消耗不同而动态调整执行顺序。
需求:查询每个部门年龄高于20岁的人数且高于20岁人数不能少于2人,显示人数最多的第一名部门信息下面是经常出现的查询顺序:
Oracle 中采用了 共享池
来判断 SQL 语句是否存在缓存和执行计划,通过这一步骤我们可以知道应该采用硬解析还是软解析。
我们先来看下 SQL 在 Oracle 中的执行过程:
从上面这张图中可以看出,SQL 语句在 Oracle 中经历了以下的几个步骤。
1.语法检查:检查 SQL 拼写是否正确,如果不正确,Oracle 会报语法错误。
2.语义检查:检查 SQL 中的访问对象是否存在。比如我们在写 SELECT 语句的时候,列名写错了,系统就会提示错误。语法检查和语义检查的作用是保证 SQL 语句没有错误。
3.权限检查:看用户是否具备访问该数据的权限。
4.共享池检查:共享池(Shared Pool)是一块内存池,最主要的作用是缓存 SQL 语句和该语句的执行计划。Oracle 通过检查共享池是否存在 SQL 语句的执行计划,来判断进行软解析,还是硬解析。那软解析和硬解析又该怎么理解呢?
在共享池中,Oracle 首先对 SQL 语句进行 Hash 运算 ,然后根据 Hash 值在库缓存(Library Cache)中查找,如果 存在 SQL 语句的执行计划 ,就直接拿来执行,直接进入“执行器”的环节,这就是 软解析 。
如果没有找到 SQL 语句和执行计划,Oracle 就需要创建解析树进行解析,生成执行计划,进入“优化器” 这个步骤,这就是 硬解析
5.优化器:优化器中就是要进行硬解析,也就是决定怎么做,比如创建解析树,生成执行计划。
6.执行器:当有了解析树和执行计划之后,就知道了 SQL 该怎么被执行,这样就可以在执行器中执行语句了。
共享池是 Oracle 中的术语,包括了库缓存,数据字典缓冲区等。我们上面已经讲到了库缓存区,它主要缓存 SQL 语句和执行计划。而 数据字典缓冲区 存储的是 Oracle 中的对象定义,比如表、视图、索引等对象。当对 SQL 语句进行解析的时候,如果需要相关的数据,会从数据字典缓冲区中提取。
库缓存 这一个步骤,决定了 SQL 语句是否需要进行硬解析。为了提升 SQL 的执行效率,我们应该尽量避免硬解析,因为在 SQL 的执行过程中,创建解析树,生成执行计划是很消耗资源的。
你可能会问,如何避免硬解析,尽量使用软解析呢?在 Oracle 中, 绑定变量 是它的一大特色。绑定变量就是在 SQL 语句中使用变量,通过不同的变量取值来改变 SQL 的执行结果。这样做的好处是能 提升软解析的可能性 ,不足之处在于可能会导致生成的执行计划不够优化,因此是否需要绑定变量还需要视情况而定。举个例子,我们可以使用下面的查询语句:
SQL> select * from player where player_id = 10001;
你也可以使用绑定变量,如:
SQL> select * from player where player_id = :player_id;
这两个查询语句的效率在 Oracle 中是完全不同的。如果你在查询 player_id = 10001 之后,还会查询10002、10003 之类的数据,那么每一次查询都会创建一个新的查询解析。而第二种方式使用了绑定变量,那么在第一次查询之后,在共享池中就会存在这类查询的执行计划,也就是软解析。
因此,我们可以通过使用绑定变量来减少硬解析,减少 Oracle 的解析工作量。但是这种方式也有缺点, 使用动态 SQL 的方式,因为参数不同,会导致 SQL 的执行效率不同,同时 SQL 优化也会比较困难。
Oracle的架构图:
小结:
Oracle 和 MySQL 在进行 SQL 的查询上面有软件实现层面的差异。Oracle 提出了共享池的概念,通过共享池来判断是进行软解析,还是硬解析。
InnoDB 存储引擎是以页为单位来管理存储空间的,我们进行的增删改查操作其实本质上都是在访问页 面(包括读页面、写页面、创建新页面等操作)。而磁盘 I/O 需要消耗的时间很多,而在内存中进行操作,效率则会高很多,为了能让数据表或者索引中的数据随时被我们所用,DBMS 会申请 占用内存来作为数据缓冲池 ,在真正访问页面之前,需要把在磁盘上的页缓存到内存中的 Buffer Pool 之后才可以访问。
这样做的好处是可以让磁盘活动最小化,从而 减少与磁盘直接进行 I/O 的时间 。要知道,这种策略对提升 SQL 语句的查询性能来说至关重要。如果索引的数据在缓冲池里,那么访问的成本就会降低很多。
缓冲池和查询缓存是一个东西吗?不是。
1.缓冲池(Buffer Pool)
首先我们需要了解在 InnoDB 存储引擎中,缓冲池都包括了哪些。
在 InnoDB 存储引擎中有一部分数据会放到内存中,缓冲池则占了这部分内存的大部分,它用来存储各种数据的缓存,如下图所示:
从图中,你能看到 InnoDB 缓冲池包括了数据页、索引页、插入缓冲、锁信息、自适应 Hash 和数据字典信息等。
缓存池的重要性:
缓存原则:
“ 位置 * 频次 ” 这个原则,可以帮我们对 I/O 访问效率进行优化。
首先,位置决定效率,提供缓冲池就是为了在内存中可以直接访问数据。
其次,频次决定优先级顺序。因为缓冲池的大小是有限的,比如磁盘有 200G,但是内存只有 16G,缓冲池大小只有 1G,就无法将所有数据都加载到缓冲池里,这时就涉及到优先级顺序,会优先对使用频次高的热数据进行加载
缓冲池的预读特性:
了解了缓冲池的作用之后,我们还需了解缓冲池的另一个特性:预读
缓冲池的作用就是提升I/O效率,而我们进行读取数据的时候存在一个“局部性原理”,也就是说我们使用了一些数据,大概率还会使用它周围的一些数据 ,因此采用 “预读”的机制提前加载,可以减少未来可能得磁盘I/O操作。
2.查询缓存
那么什么是查询缓存呢?
查询缓存是提前把 查询结果缓存 起来,这样下次不需要执行就可以直接拿到结果。需要说明的是,在MySQL 中的查询缓存,不是缓存查询计划,而是查询对应的结果。因为命中条件苛刻,而且只要数据表发生变化,查询缓存就会失效,因此命中率低。
缓冲池管理器会尽量将经常使用的数据保存起来,在数据库进行页面读操作的时候,首先会判断该页面 是否在缓冲池中,如果存在就直接读取,如果不存在,就会通过内存或磁盘将页面存放到缓冲池中再进 行读取。
缓存在数据库中的结构和作用如下图所示:
如果我们执行 SQL 语句的时候更新了缓存池中的数据,那么这些数据会马上同步到磁盘上吗?
如果你使用的是 InnoDB 存储引擎,可以通过查看 innodb_buffer_pool_size 变量来查看缓冲池的大
小。命令如下:
show variables like
‘innodb_buffer_pool_size’;
你能看到此时 InnoDB 的缓冲池大小只有 134217728/1024/1024=128MB。我们可以修改缓冲池大小,比如改为256MB,方法如下:
set global innodb_buffer_pool_size = 268435456;
或者:
vim /etc/my.cnf
[server]
innodb_buffer_pool_size = 268435456
[server]
innodb_buffer_pool_instances = 26
这样就表明我们要创建2个 Buffer Pool 实例。
我们看下如何查看缓冲池的个数,使用命令:
show variables like
‘innodb_buffer_pool_instances’;
那每个 Buffer Pool 实例实际占多少内存空间呢?其实使用这个公式算出来的:
innodb_buffer_pool_size/innodb_buffer_pool_instances
不过也不是说Buffer Pool 实例创建的越多越好,分别 管理各个 Buffer Pool 也是需要性能呢开销的,InnoDB规定: 当innodb_buffer_pool_size 的值小于1G的时候设置多个实例是无效的,InnoDB会默认把innodb_buffer_pool_instances的值修改为1.而我们鼓励在Buffer Pool 大于或等于1G的时候设置多个Buffer Pool 实例. |
查看mysql提供什么存储引擎:
show engines;
show engines \G;
显式如下:
*************************** 1. row *************************** Engine: InnoDB Support: DEFAULT Comment: Supports transactions, row-level locking, and foreign keys Transactions: YES XA: YES Savepoints: YES *************************** 2. row *************************** Engine: MRG_MYISAM Support: YES Comment: Collection of identical MyISAM tables Transactions: NO XA: NO Savepoints: NO *************************** 3. row *************************** Engine: MEMORY Support: YES Comment: Hash based, stored in memory, useful for temporary tables Transactions: NO XA: NO Savepoints: NO *************************** 4. row *************************** Engine: BLACKHOLE Support: YES Comment: /dev/null storage engine (anything you write to it disappears) Transactions: NO XA: NO Savepoints: NO *************************** 5. row *************************** Engine: MyISAM Support: YES Comment: MyISAM storage engine Transactions: NO XA: NO Savepoints: NO *************************** 6. row *************************** Engine: CSV Support: YES Comment: CSV storage engine Transactions: NO XA: NO Savepoints: NO *************************** 7. row *************************** Engine: ARCHIVE Support: YES Comment: Archive storage engine Transactions: NO XA: NO Savepoints: NO *************************** 8. row *************************** Engine: PERFORMANCE_SCHEMA Support: YES Comment: Performance Schema Transactions: NO XA: NO Savepoints: NO *************************** 9. row *************************** Engine: FEDERATED Support: NO Comment: Federated MySQL storage engine Transactions: NULL XA: NULL Savepoints: NULL
show variables like
‘%storage_engine%’;
#或
SELECT
@@default_storage_engine;
SET DEFAULT_STORAGE_ENGINE=MyISAM;
#或者
修改 my.cnf 文件:
default-storage-engine=MyISAM
#重启服务
systemctl restart mysqld.service
CREATE TABLE
表名(
建表语句;
)ENGINE
= 存储引擎名称;
如果表已经建好了,我们也可以使用下边这个语句来修改表的存储引擎:
ALTER TABLE
表名ENGINE
= 存储引擎名称;
举例:
比如我们修改一下 engine_demo_table 表的存储引擎:
ALTER TABLE
engine_demo_tableENGINE
= InnoDB;
这时我们再查看一下 engine_demo_table 的表结构:
mysql>
SHOW CREATE TABLE
engine_demo_table\G
*************************** 1. row *************************** Table: engine_demo_table
Create Table: CREATE TABLEengine_demo_table
(
i
int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.01 sec)
具备外键支持功能的事务存储引擎
主要的非事务处理存储引擎
用于数据存档
特征 | 支持 |
---|---|
B树索引 | × |
备份/时间点恢复 (在服务器中实现,而不是在存储引擎中) | √ |
集群数据库支持 | × |
聚集索引 | × |
压缩数据 | √ |
数据缓存 | × |
加密数据(加密功能在服务器中实现) | √ |
外键支持 | × |
全文检索索引 | × |
地理空间数据类型支持 | √ |
地理空间索引支持 | × |
哈希索引 | × |
索引缓存 | × |
锁粒度 | 行锁 |
MVCC | × |
存储限制 | 没有任何限制 |
交易 | × |
更新数据字典的统计信 | √ |
丢弃写操作,读操作会返回空内容
存储数据时,以逗号分隔各个数据项
使用案例如下:
mysql>
CREATE TABLE
test (i INTNOT
NULL, c CHAR(10)NOT
NULL)ENGINE
= CSV;
创建CSV表还会创建相应的 元文件 ,用于 存储表的状态 和 表中存在的行数 。此文件的名称与表的名称相
同,后缀为 。如图所示
如果检查 test.CSV 通过执行上述语句创建的数据库目录中的文件,其内容使用Notepad++打开如下:
“1”,“record one”
“2”,“record two”
置于内存的表
Memory采用的逻辑介质是 内存 , 响应速度很快 ,但是当mysqld守护进程崩溃的时候数据会丢失 。另外,要求存储的数据是数据长度不变的格式,比如,Blob和Text类型的数据不可用(长度不固定的)。
使用Memory存储引擎的场景:
1.目标数据比较小 ,而且非常 频繁的进行访问 ,在内存中存放数据,如果太大的数据会造成内存溢出
出 。可以通过参数 max_heap_table_size 控制Memory表的大小,限制Memory表的最大的大小。
2.如果 数据是临时的 ,而且 必须立即可用 得到,那么就可以放在内存中。
3.存储在Memory表中的数据如果突然间 丢失的话也没有太大的关系
访问远程表
Federated引擎是访问其他MySQL服务器的一个 代理 ,尽管该引擎看起来提供了一种很好的
器的灵活性 ,但也经常带来问题,因此 默认是禁用的 。
管理多个MyISAM表构成的表集合
MySQL集群专用存储引擎
也叫做 NDB Cluster 存储引擎,主要用于 MySQL Cluster 分布式集群 环境,类似于 Oracle 的 RAC 集群。
特点 | MyISAM | InnoDB | MEMORY | MERGE | NDB |
---|---|---|---|---|---|
存储限制 | 有 | 64TB | 有 | 没有 | 有 |
事务安全 | 支持 | ||||
锁机制 | 表锁,即使操作一条记录也会锁住整个 表,不适合高并发的操作 | 行锁,操作时只锁某一行,不对其它行有影响,适合高并发的操作 | 表锁 | 表锁 | 行锁 |
B树索引 | 支持 | 支持 | 支持 | 支持 | 支持 |
哈希索引 | 支持 | 支持 | |||
全文索引 | 支持 | ||||
集群索引 | 支持 | ||||
数据缓存 | 支持 | 支持 | 支持 | ||
索引缓存 | 只缓存索引,不缓存真实数据 | 不仅缓存索引还要缓存真实数据,对内存要求较高,而且内存大小对性能有决定性的影响 | 支持 | 支持 | 支持 |
数据可压缩 | 支持 | ||||
空间使用 | 低 | 高 | N/A | 低 | 低 |
内存使用 | 低 | 高 | 中等 | 低 | 高 |
批量插入的速度 | 高 | 低 | 高 | 高 | 高 |
支持外键 | 支持 |
很多人对 InnoDB 和 MyISAM 的取舍存在疑问,到底选择哪个比较好呢?
MySQL5.5之前的默认存储引擎是MyISAM,5.5之后改为了InnoDB。
对比项 | MyISAM | InnoDB |
---|---|---|
外键 | 不支持 | 支持 |
事务 | 不支持 | 支持 |
行表锁 | 表锁,即使操作一条记录也会锁住整个表,不适合高并发的操作 | 行锁,操作时只锁某一行,不对其它行有影响,适合高并发的操作 |
缓存 | 只缓存索引,不缓存真实数据 | 不仅缓存索引还要缓存真实数据,对内存要求较高,而且内存大小对性能有决定性的影响 |
自带系统表使用 | Y | N |
关注点 | 性能:节省资源、消耗少、简单业务 | 事务:并发写、事务、更大资源 |
默认安装 | Y | Y |
默认使用 | N | Y |
MyISAM的索引方式都是“非聚簇”的,与InnoDB包含1个聚簇索引是不同的。小结两种引擎中索引的区别:
聚簇索引
进行一次查找就能找到对应的记录,而在MyISAM
中却需要进行一次 回表
操作,意味着MyISAM中建立的索引相当于全部都是 二级索引
。分离的
,索引文件仅保存数据记录的地址。主键的值
,而MyISAM索引记录的是 地址
。换句话说,快速
的,因为是拿着地址偏移量直接到文件中取数据的,反观InnoDB是通过获取主键之后再去聚簇索引里找记录,虽然说也不慢,但还是比不上直接用地址去访问。必须有主键 ( MyISAM可以没有 )
。如果没有显式指定,则MySQL系统会自动选择一个可以非空且唯一标识数据记录的列作为主键。如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整型。InnoDB存储引擎在实际应用中拥有诸多优势,比如操作便利、提高了数据库的性能、维护成本低等。如果由于硬件或软件的原因导致服务器崩溃,那么在重启服务器之后不需要进行额外的操作。InnoDB崩溃恢复功能自动将之前提交的内容定型,然后撤销没有提交的进程,重启之后继续从崩溃点开始执行。
InnoDB存储引擎在主内存中维护缓冲池,高频率使用的数据将在内存中直接被处理。这种缓存方式应用于多种信息,加速了处理进程。
在专用服务器上,物理内存中高达80%的部分被应用于缓冲池。如果需要将数据插入不同的表中,可以设置外键加强数据的完整性。更新或者删除数据,关联数据将会被自动更新或删除。如果试图将数据插入从表,但在主表中没有对应的数据,插入的数据将被自动移除。如果磁盘或内存中的数据出现崩溃,在使用脏数据之前,校验和机制会发出警告。当每个表的主键都设置合理时,与这些列有关的操作会被自动优化。插入、更新和删除操作通过做改变缓冲自动机制进行优化。InnoDB不仅支持当前读写,也会缓冲改变的数据到数据流磁盘 。
InnoDB的性能优势不只存在于长时运行查询的大型表。在同一列多次被查询时,自适应哈希索引会提高查询的速度。使用InnoDB可以压缩表和相关的索引,可以 在不影响性能和可用性的情况下创建或删除索 引 。对于大型文本和BLOB数据,使用动态行形式,这种存储布局更高效。通过查询 INFORMATION_SCHEMA库中的表可以监控存储引擎的内部工作。在同一个语句中,InnoDB表可以与其他存储引擎表混用。即使有些操作系统限制文件大小为2GB,InnoDB仍然可以处理。 当处理大数据量时,InnoDB兼顾CPU,以达到最大性能 。
ACID模型是一系列数据库设计规则,这些规则着重强调可靠性,而可靠性对于商业数据和任务关键型应用非常重要。MySQL包含类似InnoDB存储引擎的组件,与ACID模型紧密相连,这样出现意外时,数据不会崩溃,结果不会失真。如果依赖ACID模型,可以不使用一致性检查和崩溃恢复机制。如果拥有额外的软件保护,极可靠的硬件或者应用可以容忍一小部分的数据丢失和不一致,可以将MySQL设置调整为只依赖部分ACID特性,以达到更高的性能。下面讲解InnoDB存储引擎与ACID模型相同作用的四个方面。
1. 原子方面 ACID的原子方面主要涉及InnoDB事务,与MySQL相关的特性主要包括:
2. 一致性方面 2.ACID模型的一致性主要涉及保护数据不崩溃的内部InnoDB处理过程,与MySQL相关的特性主要包括:
3. 隔离方面 隔离是应用于事务的级别,与MySQL相关的特性主要包括:
4.耐久性方面 4.ACID模型的耐久性主要涉及与硬件配置相互影响的MySQL软件特性。由于硬件复杂多样化,耐久性方面没有具体的规则可循。与MySQL相关的特性有:
假如给数据使用 二叉树 这样的数据结构进行存储,如下图所示
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。
索引的本质: 索引是数据结构。你可以简单理解为“排好序的快速查找数据结构”,满足特定查找算法。这些数据结构以某种方式指向数据, 这样就可以在这些数据结构的基础上实现 高级查找算法 。
提示: 索引可以提高查询的速度,但是会影响记录的速度,最好的办法是先删除表中的索引,然后插入数据,插入完成后再创建索引 |
我们在根据某个搜索条件查找一些记录时为什么要遍历所有的数据页呢?因为各个页中的记录并没有规律,我们并不知道我们的搜索条件匹配哪些页中的记录,所以不得不依次遍历所有的数据页。所以如果我们 想快速的定位到需要查找的记录在哪些数据页 中该咋办?我们可以为快速定位记录所在的数据页而 建立一个目录 ,建这个目录必须完成下边这些事:
3.2.1 迭代1次:目录项纪录的页
我们把前边使用到的目录项放到数据页中的样子就是这样:
从图中可以看出来,我们新分配了一个编号为30的页来专门存储目录项记录。这里再次强调 目录项记录 和普通的 用户记录 的不同点:
1
,其他别的记录的 min_rec_mask 值都是 0
。3.2.2 迭代2次:多个目录项纪录的页
从图中可以看出,我们插入了一条主键值为320的用户记录之后需要两个新的数据页:
现在因为存储目录项记录的页不止一个,所以如果我们想根据主键值查找一条用户记录大致需要3个步骤,以查找主键值为 20 的记录为例:
3.2.3 迭代3次:目录项记录页的目录页
如图,我们生成了一个存储更高级目录项的 页33 ,这个页中的两条记录分别代表页30和页32,如果用
户记录的主键值在 [1, 320) 之间,则到页30中查找更详细的目录项记录,如果主键值 不小于320 的话,就到页32中查找更详细的目录项记录。
我们可以用下边这个图来描述它:
这个数据结构,它的名称是 B+Tree
。
一个B+树的节点其实可以分成好多层,规定最下边的那层,也就是存放我们用户记录的那层为第 0 层,之后依次往上加。之前我们做了一个非常极端的假设:存放用户记录的页 最多存放3条记录 ,存放目录项记录的页 最多存放4条记录 。其实真实环境中一个页存放的记录数量是非常大的,假设所有存放用户记录的叶子节点代表的数据页可以存放 100条用户记录 ,所有存放目录项记录的内节点代表的数据页可以存放 1000条目录项记录 ,那么:
1
层,也就是只有1个用于存放用户记录的节点,最多能存放 100
条记录。2
层,最多能存放 1000×100=10,0000
条记录。3
层,最多能存放 1000×1000×100=1,0000,0000
条记录。4
层,最多能存放 1000×1000×1000×100=1000,0000,0000
条记录。相当多的记录!!!索引按照物理实现方式,索引可以分为 2 种:聚簇(聚集)和非聚簇(非聚集)索引。我们也把非聚集索引称为二级索引或者辅助索引。
1.聚簇索引
特点:
优点:
缺点:
2. 二级索引(辅助索引、非聚簇索引)
理解:聚簇索引中保存都是用户的真实数据
,而非聚簇索引保存的仅仅是该列的值 + 主键的值
,查找时需要回表
概念:回表 我们根据这个以c2列大小排序的B+树只能确定我们要查找记录的主键值,所以如果我们想根据c2列的值查找到完整的用户记录的话,仍然需要到 聚簇索引 中再查一遍,这个过程称为 回表 。也就是根据c2列的值查询一条完整的用户记录需要使用到 2
棵B+树!
问题:为什么我们还需要一次 回表 操作呢?直接把完整的用户记录放到叶子节点不OK吗?
小结: 聚簇索引与非聚簇索引的原理不同,在使用上也有一些区别: 1.聚簇索引的叶子节点存储的就是我们的数据记录,非聚簇索引的叶子节点存储的是数据位置。 非聚簇索引不会影响数据表的物理存储顺序。 2.一个表只能有一个聚簇索引,因为只能有一种排序存储的方式,但可以有多个非聚簇索引,也就是多个索引目录提供数据检索 3.使用聚簇索引的时候,数据的查询效率高,但如果对数据进行插入、删除、更新等操作,效率会比非聚簇索引低 |
3.联合索引(非聚簇索引)
我们也可以同时以多个列的大小作为排序规则,也就是同时为多个列建立索引,比方说我们想让B+树按照 c2和c3列 的大小进行排序,这个包含两层含义:
注意一点,以c2和c3列的大小为排序规则建立的B+树称为 联合索引 ,本质上也是一个二级索引。它的意思与分别为c2和c3列分别建立索引的表述是不同的,不同点如下:
1.根页面位置万年不动
我们前边介绍B+树索引的时候,为了大家理解上的方便,先把存储用户记录的叶子节点都画出来,然后接着画存储目录项记录的内节点,实际上B+树的形成过程是这样的:
这个过程特别注意的是:一个B+树索引的根节点自诞生之日起,便不会再移动,这样只要我们对某个表建立一个索引,那么它的根节点的页号便会被记录到某个地方,然后凡事InnoDB存储引擎需要用到这个索引的时候,都会从那个固定的地方去除根节点的页号,从而来访问这个索引。
2.内节点中目录记录的唯一性
我们知道B+树索引的内节点中目录项记录的内容是索引列 + 页号
的搭配,但是这个搭配对于二级索引来说有点不严谨,耳机索引还需要主键的列
3.一个页面最少存储2条记录
一个B+树只需要很少的层级就可以轻松存储数亿条记录,查询速度相当不错。这是因为B+树本质上就是一个大的多层级目录,每经过一个目录时都会过滤掉许多无效的子目录,直到最后访问到存储真实数据的目录,那如果一个大的目录中只存放一个子目录是个啥效果呢?那就是目录层级非常非常多,而且最后那个存放真实的目录中只能存放一条数据。
B+树索引适用存储引擎如表所示:
索引 | MyISAM | InnoDB | Memory |
---|---|---|---|
B-Tree索引 | 支持 | 支持 | 支持 |
即使多个存储引擎支持同一种类型的索引,但是他们的实现原理也是不同的。Innodb和MyISAM默认的索引是Btree索引;而Memory默认的索引是Hash索引。
MyISAM引擎使用 B+Tree
作为索引结构,叶子节点的data域存放的是 数据记录的地址
。
增、删、改
操作时,都需要去修改各个B+树索引。而且我们讲过,B+树每层节点都是按照索引列的值 从小到大的顺序排序
而组成了 双向链表
。不论是叶子节点中的记录,还是内节点中的记录(也就是不论是用户记录还是目录项记录)都是按照索引列的值从小到大的顺序而形成了一个单向链表。而增、删、改操作可能会对节点和记录的排序造成破坏,所以存储引擎需要额外的时间进行一些 记录移位 , 页面分裂 、 页面回收
等操作来维护好节点和记录的排序。如果我们建了许多索引,每个索引对应的B+树都要进行相关的维护操作,会给性能拖后腿。就是把表全部加载一遍
上图中哈希函数h有可能将两个不同的关键字映射到相同的位置,这叫做 碰撞 ,在数据库中一般采用 链接法 来解决。在链接法中,将散列到同一槽位的元素放在一个链表中,如下图所示:
Hash结构效率高,那为什么索引结构要设计成树型呢?
原因1:Hash索引仅能满足(=)(<>)和 IN 查询。如果进行范围查询 ,哈希型的索引,时间复杂度会退化为 O(n);而树形的“有序”特性,依然能够保持O(log2N)的高效率。
原因2:Hash索引还有一个缺陷,数据的存储是没有顺序的,在Order By的情况下,使用Hash索引还需要对数据重新排序
原因3:对于联合索引的情况,Hash值是将联合索引键合并后一起来计算的,无法对单独的一个键或者几个索引键进行查询
原因4:对于等值查询来说,通常Hash索引的效率更高,不过也存在一种情况,就是 索引列的重复值如果很多,效率就会降低。这是应为遇到Hash冲突是,需要遍历桶中的行指针来进行比较,找到查询的关键字,非常耗时,所以,Hash索引通常不会用到 重复值多的列上,比如列为性别,年龄的情况等。
Hash索引适用存储引擎如表所示:
索引 / 存储引擎 | MyISAM | InnoDB | Memory |
---|---|---|---|
HASH索引 | 不支持 | 不支持 | 支持 |
Hash索引的适用性:
采用自适应 Hash 索引目的是方便根据 SQL 的查询条件加速定位到叶子节点,特别是当 B+ 树比较深的时候,通过自适应 Hash 索引可以明显提高数据的检索效率。
我们可以通过 innodb_adaptive_hash_index 变量来查看是否开启了自适应 Hash,比如:
mysql> show variables like ‘%adaptive_hash_index’;
如果我们利用二叉树作为索引结构,那么磁盘的IO次数和索引树的高度是相关的。
为了解决上面二叉查找树退化成链表的问题,人们提出了 平衡二叉树 ,又称为AVL树(有别于AVL算法),他在二叉搜索树的基础上增加了约束,具有以下性质:
它是一颗空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一颗二叉平衡树。
这里说一下,常见的平衡二叉树有很多种,包括了
平衡二叉搜索树、红黑树、数堆、伸展树。平衡二叉 搜索树是最早提出来的自平衡二叉搜索树,当我们提到平衡二叉树时一般指的就是平衡二叉搜索树。事实上,第一棵树就属于平衡二叉搜索树,搜索时间复杂度就是O(log2n)
每访问一次节点就需要进行一次磁盘I/O操作,对于上面的树来说,我们需要进行5次I/O操作。虽然平衡二叉树的效率高,但是树的深度也同样高,这就意味着磁盘I/O操作次数多,会影响整个数据查询的效率。
针对同样的数据,我们把二叉树改成 M 叉树(M > 2)呢?当M=3时,同样的31个节点可以由下面的三叉树 进行存储:
B 树的结构如下图所示:
个 M 阶的 B 树(M>2)有以下的特性:
小结: 1.B树在插入和删除节点的时候会导致树的不平衡,就通过自动调整节点的位置来保持树的自平衡。 2.关键字集合分布在整颗树中,即叶子节点和非叶子节点都存放数据。搜索有可能在非叶子节点结束 3.其搜索性能等价于在关键字全集内做一次二分查找 |
见 目录 B+Tree
B+ 树和 B 树的差异:
1.有 k 个孩子的节点就有 k 个关键字。也就是孩子数量 = 关键字数,而 B 树中,孩子数量 = 关键字数
+1。
2.非叶子节点的关键字也会同时存在在子节点中,并且是在子节点中所有关键字的最大(或最小)。
3.非叶子节点仅用于索引,不保存数据记录,跟记录有关的信息都放在叶子节点中。而 B 树中,非叶子节点既保存索引,也保存数据记录 。
4.所有关键字都在叶子节点出现,叶子节点构成一个有序链表,而且叶子节点本身按照关键字的大小从小到大顺序链接。
R-Tree在MySQL很少使用,仅支持 geometry数据类型 ,支持该类型的存储引擎只有myisam、bdb、 innodb、ndb、archive几种。举个R树在现实领域中能够解决的例子:查找20英里以内所有的餐厅。如果没有R树你会怎么解决?一般情况下我们会把餐厅的坐标(x,y)分为两个字段存放在数据库中,一个字段记录经度,另一个字段记录纬度。这样的话我们就需要遍历所有的餐厅获取其位置信息,然后计算是否满足要求。如果一个地区有100家餐厅的话,我们就要进行100次位置计算操作了,如果应用到谷歌、百度地图这种超大数据库中,这种方法便必定不可行了。R树就很好的 解决了这种高维空间搜索问题 。它把B树的思想很好的扩展到了多维空间,采用了B树分割空间的思想,并在添加、删除操作时采用合并、分解结点的方法,保证树的平衡性。因此,R树就是一棵用来 存储高维数据的平衡树 。相对于B-Tree,R-Tree 的优势在于范围查找。
索引 / 存储引擎 | MyISAM | InnoDB | Memory |
---|---|---|---|
R-Tree索引 | 支持 | 支持 | 不支持 |
通过命令查看索引
方式1:
SHOW CREATE TABLE
book;
方式2:
SHOW INDEX FROM
book;
MySQL的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。
MySQL 支持多种方法在单个或多个列上创建索引;在创建表的定义语句 CREATE_TABLE 中指明索引列,使用 ALTER_TABLE 语句在存在的表上创建索引,或者使用 CREATE_INDEX 语句在已存在的表上添加索引
1. 创建表的时候创建索引
举例:
CREATE TABLE dept(
dept_id INT PRIMARY KEY AUTO_INCREMENT,
dept_name VARCHAR(20)
);
CREATE TABLE emp(
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(20) UNIQUE, dept_id INT,
CONSTRAINT emp_dept_id_fk FOREIGN KEY(dept_id) REFERENCES dept(dept_id)
);
但是,如果显式创建表时创建索引的话,基本语法格式如下:
CREATE TABLE table_name [col_name data_type]
[UNIQUE | FULLTEXT | SPATIAL] [INDEX | KEY] [index_name] (col_name [length]) [ASC | DESC]
UNIQUE 、 FULLTEXT 和 SPATIAL 为可选参数,分别表示唯一索引、全文索引和空间索引;
INDEX 与 KEY 为同义词,两者的作用相同,用来指定创建索引;
index_name 指定索引的名称,为可选参数,如果不指定,那么MySQL默认col_name为索引名;
col_name 为需要创建索引的字段列,该列必须从数据表中定义的多个列中选择;
length 为可选参数,表示索引的长度,只有字符串类型的字段才能指定索引长度;
ASC 或 DESC 指定升序或者降序的索引值存储。
在book表中的year_publication字段上建立普通索引,SQL语句如下:
CREATE TABLE book(
book_id INT ,
book_name VARCHAR(100),
authors VARCHAR(100),
info VARCHAR(100) ,
comment VARCHAR(100),
year_publication YEAR,
INDEX(year_publication)
);
CREATE TABLE test1( id INT NOT NULL,
name varchar(30) NOT NULL,
UNIQUE INDEX uk_idx_id(id)
设定为主键后数据库会自动建立索引,innodb为聚簇索引,语法:
CREATE TABLE student (
id INT(10) UNSIGNED AUTO_INCREMENT ,
student_no VARCHAR(200),
student_name VARCHAR(200),
PRIMARY KEY(id)
);
ALTER TABLE student drop PRIMARY KEY ;
举例:
CREATE TABLE test2(
id INT NOT NULL,
name CHAR(50) NULL,
INDEX single_idx_name(name(20))
举例:创建表test3,在表中的id、name和age字段上建立组合索引,SQL语句如下:
CREATE TABLE test3(
id INT(11) NOT NULL,
name CHAR(30) NOT NULL,
age INT(11) NOT NULL,
info VARCHAR(255),
INDEX multi_idx(id,name,age)
举例1:创建表test4,在表中的info字段上建立全文索引,SQL语句如下:
CREATE TABLE test4( id INT NOT NULL,
name CHAR(30) NOT NULL,
age INT NOT NULL,
info VARCHAR(255),
FULLTEXT INDEX futxt_idx_info(info)
在MySQL5.7及之后版本中可以不指定最后的ENGINE了,因为在此版本中InnoDB支持全文索引。
全文索引用match+against方式查询:
SELECT * FROM papers WHERE MATCH(title,content) AGAINST (‘查询字符串’);
空间索引创建中,要求空间类型的字段必须为 非空 。
举例:创建表test5,在空间类型为GEOMETRY的字段上创建空间索引,SQL语句如下:
CREATE TABLE test5(
geo GEOMETRY NOT NULL,
SPATIAL INDEX spa_idx_geo(geo)
在已经存在的表中创建索引可以使用 ALTER TABLE 语句或者CREATE INDEX 语句。
ALTER TABLE table_name ADD [UNIQUE | FULLTEXT | SPATIAL] [INDEX | KEY]
[index_name] (col_name[length],...) [ASC | DESC]
2.使用CREATE INDEX创建索引 CREATE INDEX语句可以在已经存在的表上添加索引,在MySQL中, CREATE INDEX被映射到一个ALTER TABLE语句上,基本语法结构为:
CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name
ON table_name (col_name[length],...) [ASC | DESC]
删除索引
使用ALTER TABLE删除索引 ALTER TABLE删除索引的基本语法格式如下:
ALTER TABLE table_name DROP INDEX index_name;
使用DROP INDEX语句删除索引 DROP INDEX删除索引的基本语法格式如下:
DROP INDEX index_name ON table_name;
提示 删除表中的列时,如果要删除的列为索引的组成部分,则该列也会从索引中删除。如果组成索引的所有列都被删除,则整个索引将被删除。
举例:分别在MySQL 5.7版本和MySQL 8.0版本中创建数据表ts1,结果如下:
CREATE TABLE ts1(
a int,
b int,
index idx_a_b(a,b desc));
从MySQL 8.x开始支持 隐藏索引(invisible indexes) ,只需要将待删除的索引设置为隐藏索引,使查询优化器不再使用这个索引(即使使用force index(强制使用索引),优化器也不会使用该索引),确认将索引设置为隐藏索引后系统不受任何响应,就可以彻底删除索引。 这种通过先将索引设置为隐藏索引,再删除索引的方式就是软删除 。
1.创建表时直接创建
CREATE TABLE tablename(
propname1 type1[CONSTRAINT1], propname2 type2[CONSTRAINT2],
……
propnamen typen,
INDEX [indexname](propname1 [(length)]) INVISIBLE
);
上述语句比普通索引多了一个关键字INVISIBLE,用来标记索引为不可见索引。
2.在已经存在的表上创建
CREATE INDEX indexname
ON tablename(propname[(length)]) INVISIBLE;
3.通过ALTER TABLE语句创建
ALTER TABLE tablename
ADD INDEX indexname (propname [(length)]) INVISIBLE;
4.切换索引可见状态
ALTER TABLE tablename ALTER INDEX index_name INVISIBLE; #切换成隐藏索引
ALTER TABLE tablename ALTER INDEX index_name VISIBLE; #切换成非隐藏索引
注意 当索引被隐藏时,它的内容仍然是和正常索引一样实时更新的。如果一个索引需要长期被隐藏,那么可以将其删除,因为索引的存在会影响插入、更新和删除的性能。
首先,
连接表的数量尽量不要超过 3 张
其次,对 WHERE 条件创建索引
最后,对用于连接的字段创建索引
,并且该字段在多张表中的类型必须一致
count(distinct left
(列名, 索引长度))/count(*)
举例:
select count(distinct left(address,10)) / count(*) as sub10, – 截取前10个字符的选择度
在MySQL中,可以使用 SHOW STATUS
语句查询一些MySQL数据库服务器的 性能参数 、 执行频率
。
SHOW STATUS语句语法如下:
SHOW [GLOBAL|SESSION] STATUS LIKE
‘参数’;
使用场景:它对于比较开销是非常有用的,特别是我们有好几种查询方式可选的时候。
SHOW STATUS LIKE 'last_query_cost';
1.开启slow_query_log
mysql >
set global
slow_query_log=‘ON’;
你能看到这时慢查询分析已经开启,同时文件保存在 /var/lib/mysql/atguigu02-slow.log 文件中。
2.修改long_query_time阈值
show variables like
‘%long_query_time%’;
这里如果我们想把时间缩短,比如设置为 1 秒,可以这样设置:
#测试发现:设置global的方式对当前session的long_query_time失效。对新连接的客户端有效。所以可以一并执行下述语句
mysql >set global
long_query_time = 1;
mysql>show global variables like
‘%long_query_time%’;mysql>
set
long_query_time=1;
mysql>show variables like
‘%long_query_time%’;
查询当前系统中有多少条慢查询记录
SHOW GLOBAL STATUS LIKE
‘%Slow_queries%’;
在生产环境中,如果要手工分析日志,查找、分析SQL,显然是个体力活,MySQL提供了日志分析工具 mysqldumpslow。
mysqldumpslow --help
mysqldumpslow 命令的具体参数如下:
举例:
我们想要按照查询时间排序,查看前五条 SQL 语句,这样写即可:
mysqldumpslow -s t -t 5 /var/lib/mysql/atguigu01-slow.log
工作常用参考:
#得到返回记录集最多的10个SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log
#得到访问次数最多的10个SQL
mysqldumpslow -s c -t 10 /var/lib/mysql/atguigu-slow.log
#得到按照时间排序的前10条里面含有左连接的查询语句
mysqldumpslow -s t -t 10 -g “left join” /var/lib/mysql/atguigu-slow.log
#另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现爆屏情况
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log | more
MySQL服务器停止慢查询日志功能有两种方法:
方式1:永久性方式
[mysqld]
slow_query_log=OFF
或者,把slow_query_log一项注释掉 或 删除
[mysqld]
#slow_query_log =OFF
重启MySQL服务,执行如下语句查询慢日志功能。
SHOW VARIABLES LIKE
‘%slow%’; #查询慢查询日志所在目录
SHOW VARIABLES LIKE
‘%long_query_time%’; #查询超时时长
方式2:临时性方式
使用SET语句来设置。
(1)停止MySQL慢查询日志功能,具体SQL语句如下。
SET GLOBAL
slow_query_log=off;
(2)重启MySQL服务,使用SHOW语句查询慢查询日志功能信息,具体SQL语句如下
SHOW VARIABLES LIKE
‘%slow%’;
#以及
SHOW VARIABLES LIKE
‘%long_query_time%’;
mysqladmin -uroot -p flush-logs slow
提示: 慢查询日志都是使用 mysqladmin flush-logs 命令来删除重建的。使用时一定要注意,一旦执行了这个命令,查询日志都只存在新的日志文件中,如果需要旧的查询日志,就必须事先备份 |
mysql >
show variables like
‘profiling’;
#开启 show profile:
mysql >set
profiling = ‘ON’;
#查看下当前会话都有哪些 profiles
mysql >show profiles;
#查看最近一次查询的开销
mysql >show profile;
使用示例:
show profile
cpu,block iofor query
2;
show profile的常用查询参数:
列名 | 描述 | 作用 |
---|---|---|
id | 在一个大的查询语句中每个SELECT关键字都对应一个 唯一的id | EXPLAIN语句输出的每条记录都对应着某个单表的访问方法 |
select_type | SELECT关键字对应的那个查询的类型 | id如果相同,可以认为是一组,从上往下顺序执行 在所有组中,id值越大,优先级越高,越先执行 关注点:id号每个号码,表示一趟独立的查询, 一个sql的查询趟数越少越好 |
table | 表名 | |
partitions | 匹配的分区信息 | (可略) |
type ☆ | 针对单表的访问方法 | 结果值从最好到最坏依次是: system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL 其中比较重要的几个提取出来(见上图中的蓝色)。SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,最好是 consts级别。(阿里巴巴开发手册要求) |
possible_keys | 可能用到的索引 | |
key | 实际上使用的索引 | |
key_len ☆ | 实际使用到的索引长度,主要针对联合索引,值越大越好 | |
ref | 当使用索引列等值查询时,与索引列进行等值匹配的对象信息 | |
rows ☆ | 预估的需要读取的记录条数 | |
filtered | 某个表经过搜索条件过滤后剩余记录条数的百分比 | |
Extra ☆ | 一些额外的信息 | Impossible WHERE Using where No matching min/max row Using index Using index condition Using join buffer (Block Nested Loop) Not exists |
小结 ● EXPLAIN不考虑各种Cache ● EXPLAIN不能显示MySQL在执行查询时所作的优化工作 ● EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况部分统计信息是估算的,并非精确值 |
EXPLAIN可以输出四种格式:
传统格式
,JSON格式
,TREE格式
以及可视化输出
1.传统格式
传统格式简单明了,输出是一个表格形式,概要说明查询计划。
2.JSON格式
JSON格式:在EXPLAIN单词和真正的查询语句中间加上 FORMAT=JSON 。
举例:
EXPLAIN
FORMAT=JSON SELECT …
"cost_info": {
"read_cost": "968.80",
"eval_cost": "193.76",
"prefix_cost": "3197.16",
"data_read_per_join": "1M"
3.TREE格式
TREE格式是8.0.16版本之后引入的新格式,主要根据查询的
各个部分之间的关系
和各部分的执行顺序
来描述如何查询。
举例:
EXPLAIN
FORMAT=treeSELECT
*FROM
s1INNER JOIN
s2ON
s1.key1 = s2.key2WHERE
s1.common_field = ‘a’\G
4.可视化输出
可视化输出,可以通过MySQL
Workbench可视化查看MySQL的执行计划。通过点击Workbench的放大镜图标,即可生成可视化的查询计划。
上图按从左到右的连接顺序显示表。红色框表示 全表扫描 ,而绿色框表示使用 索引查找
。对于每个表,显示使用的索引。还要注意的是,每个表格的框上方是每个表访问所发现的行数的估计值以及访问该表的成本。
mysql> EXPLAIN SELECT s1.key1, s2.key1 FROM s1 LEFT JOIN s2 ON s1.key1 = s2.key1 WHERE s2.common_field IS NOT NULL;
mysql> SHOW WARNINGS\G
*************************** 1. row *************************** Level: Note
Code: 1003
Message: /* select#1 */ select `atguigu`.`s1`.`key1` AS `key1`,`atguigu`.`s2`.`key1` AS `key1` from `atguigu`.`s1` join `atguigu`.`s2` where ((`atguigu`.`s1`.`key1` =
`atguigu`.`s2`.`key1`) and (`atguigu`.`s2`.`common_field` is not null))
1 row in set (0.00 sec)
SET optimizer_trace="enabled=on",end_markers_in_json=on;
### 设置内存大小
set optimizer_trace_max_mem_size=1000000;
开启后,可分析如下语句:
SELECT
INSERT
REPLACE
UPDATE
DELETE
EXPLAIN
SET
DECLARE
CASE
IF
RETURN
CALL
*************************** 1. row *************************** //第1部分:查询语句 QUERY: select * from student where id < 10 //第2部分:QUERY字段对应语句的跟踪信息 TRACE: { "steps": [ { "join_preparation": { //预备工作 "select#": 1, "steps": [ { "expanded_query": "/* select#1 */ select `student`.`id` AS `id`,`student`.`stuno` AS `stuno`,`student`.`name` AS `name`,`student`.`age` AS `age`,`student`.`classId` AS `classId` from `student` where (`student`.`id` < 10)" } ] /* steps */ } /* join_preparation */ }, { "join_optimization": { //进行优化 "select#": 1, "steps": [ { "condition_processing": { //条件处理 "condition": "WHERE", "original_condition": "(`student`.`id` < 10)", "steps": [ { "transformation": "equality_propagation", "resulting_condition": "(`student`.`id` < 10)" }, { "transformation": "constant_propagation", "resulting_condition": "(`student`.`id` < 10)" }, { "transformation": "trivial_condition_removal", "resulting_condition": "(`student`.`id` < 10)" } ] /* steps */ } /* condition_processing */ }, { "substitute_generated_columns": { //替换生成的列 } /* substitute_generated_columns */ }, { "table_dependencies": [ //表的依赖关系 { "table": "`student`", "row_may_be_null": false, "map_bit": 0, "depends_on_map_bits": [ ] /* depends_on_map_bits */ } ] /* table_dependencies */ }, { "ref_optimizer_key_uses": [ //使用键 ] /* ref_optimizer_key_uses */ }, { "rows_estimation": [ //行判断 { "table": "`student`", "range_analysis": { "table_scan": { "rows": 3973767, "cost": 408558 } /* table_scan */, //扫描表 "potential_range_indexes": [ //潜在的范围索引 { "index": "PRIMARY", "usable": true, "key_parts": [ "id" ] /* key_parts */ } ] /* potential_range_indexes */, "setup_range_conditions": [ //设置范围条件 ] /* setup_range_conditions */, "group_index_range": { "chosen": false, "cause": "not_group_by_or_distinct" } /* group_index_range */, "skip_scan_range": { "potential_skip_scan_indexes": [ { "index": "PRIMARY", "usable": false, "cause": "query_references_nonkey_column" } ] /* potential_skip_scan_indexes */ } /* skip_scan_range */, "analyzing_range_alternatives": { //分析范围选项 "range_scan_alternatives": [ { "index": "PRIMARY", "ranges": [ "id < 10" ] /* ranges */, "index_dives_for_eq_ranges": true, "rowid_ordered": true, "using_mrr": false, "index_only": false, "rows": 9, "cost": 1.91986, "chosen": true } ] /* range_scan_alternatives */, "analyzing_roworder_intersect": { "usable": false, "cause": "too_few_roworder_scans" } /* analyzing_roworder_intersect */ } /* analyzing_range_alternatives */, "chosen_range_access_summary": { //选择范围访问摘要 "range_access_plan": { "type": "range_scan", "index": "PRIMARY", "rows": 9, "ranges": [ "id < 10" ] /* ranges */ } /* range_access_plan */, "rows_for_plan": 9, "cost_for_plan": 1.91986, "chosen": true } /* chosen_range_access_summary */ } /* range_analysis */ } ] /* rows_estimation */ }, { "considered_execution_plans": [ //考虑执行计划 { "plan_prefix": [ ] /* plan_prefix */, "table": "`student`", "best_access_path": { //最佳访问路径 "considered_access_paths": [ { "rows_to_scan": 9, "access_type": "range", "range_details": { "used_index": "PRIMARY" } /* range_details */, "resulting_rows": 9, "cost": 2.81986, "chosen": true } ] /* considered_access_paths */ } /* best_access_path */, "condition_filtering_pct": 100, //行过滤百分比 "rows_for_plan": 9, "cost_for_plan": 2.81986, "chosen": true } ] /* considered_execution_plans */ }, { "attaching_conditions_to_tables": { //将条件附加到表上 "original_condition": "(`student`.`id` < 10)", "attached_conditions_computation": [ ] /* attached_conditions_computation */, "attached_conditions_summary": [ //附加条件概要 { "table": "`student`", "attached": "(`student`.`id` < 10)" } ] /* attached_conditions_summary */ } /* attaching_conditions_to_tables */ }, { "finalizing_table_conditions": [ { "table": "`student`", "original_table_condition": "(`student`.`id` < 10)", "final_table_condition ": "(`student`.`id` < 10)" } ] /* finalizing_table_conditions */ }, { "refine_plan": [ //精简计划 { "table": "`student`" } ] /* refine_plan */ } ] /* steps */ } /* join_optimization */ }, { "join_execution": { //执行 "select#": 1, "steps": [ ] /* steps */ } /* join_execution */ } ] /* steps */ } //第3部分:跟踪信息过长时,被截断的跟踪信息的字节数。 MISSING_BYTES_BEYOND_MAX_MEM_SIZE: 0 //丢失的超出最大容量的字节 //第4部分:执行跟踪语句的用户是否有查看对象的权限。当不具有权限时,该列信息为1且TRACE字段为空,一般在调用带有SQL SECURITY DEFINER的视图或者是存储过程的情况下,会出现此问题。 INSUFFICIENT_PRIVILEGES: 0 //缺失权限 1 row in set (0.00 sec)
1.
主机相关
:以host_summary开头,主要汇总了IO延迟的信息。
2.Innodb相关
:以innodb开头,汇总了innodb buffer信息和事务等待innodb锁的信息。
3.I/o相关
:以io开头,汇总了等待I/O、I/O使用量情况。
4.内存使用情况
:以memory开头,从主机、线程、事件等角度展示内存的使用情况
5.连接与会话信息
:processlist和session相关视图,总结了会话相关信息。
6.表相关
:以schema_table开头的视图,展示了表的统计信息。
7.索引信息
:统计了索引的使用情况,包含冗余索引和未使用的索引情况。
8.语句相关
:以statement开头,包含执行全表扫描、使用临时表、排序等的语句信息。
9.用户相关
:以user开头的视图,统计了用户使用的文件I/O、执行语句统计信息。
10.等待事件相关信息
:以wait开头,展示等待事件的延迟情况。
索引情况
#1. 查询冗余索引
select * from sys.schema_redundant_indexes;
#2. 查询未使用过的索引
select * from sys.schema_unused_indexes;
#3. 查询索引的使用情况
select index_name,rows_selected,rows_inserted,rows_updated,rows_deleted from sys.schema_index_statistics where table_schema='dbname' ;
表相关
# 1. 查询表的访问量
select table_schema,table_name,sum(io_read_requests+io_write_requests) as io from sys.schema_table_statistics group by table_schema,table_name order by io desc;
# 2. 查询占用bufferpool较多的表
select object_schema,object_name,allocated,data from sys.innodb_buffer_stats_by_table order by allocated limit 10;
# 3. 查看表的全表扫描情况
select * from sys.statements_with_full_table_scans where db='dbname';
语句相关
#1. 监控SQL执行的频率
select db,exec_count,query from sys.statement_analysis order by exec_count desc;
#2. 监控使用了排序的SQL
select db,exec_count,first_seen,last_seen,query from sys.statements_with_sorting limit 1;
#3. 监控使用了临时表或者磁盘临时表的SQL
select db,exec_count,tmp_tables,tmp_disk_tables,query
from sys.statement_analysis where tmp_tables>0 or tmp_disk_tables >0 order by (tmp_tables+tmp_disk_tables) desc;
IO相关
#1. 查看消耗磁盘IO的文件
select file,avg_read,avg_write,avg_read+avg_write as avg_io
from sys.io_global_by_file_by_bytes order by avg_read limit 10;
Innodb 相关
#1. 行锁阻塞情况
select * from sys.innodb_lock_waits;
风险提示: 通过sys库查询时,MySQL会 消耗大量的资源 去收集相关信息,严重的可能会导致业务请求被阻塞,从而引起故障。建议生产上 不要频繁 的去查询 sys 或者 performance_schema、information_schema来完成监控、巡检等工作。 |
大方向分为物理查询优化
和 逻辑查询优化
索引
和 表连接方式
等技术来进行优化,这里重点需要掌握索引的使用等价变换
提升查询效率,直白一点就是说,换一种查询写法执行效率可能要更高MySQL 中 提高性能
的一个最有效的方式就是对数据表 设计合理的索引
。索引提供了高效访问数据的方法,并且加快了查询的速度,因此索引对查询的速度有着至关重要的影响。
快速地定位
表中的某条记录,从而提高数据库查询的速度,提高数据库的性能。扫描表中的所有记录
。在数据量大的情况下,这样查询的速度会很慢。大多数情况下(默认)采用 B+树
来构建索引。只是空间列类型的索引使用 R-树
,并且MEMORY 表还支持 hash索引
。
其实。用不用索引都是优化器说了算。优化器是基于什么的优化器?基于cost开销
,它不是基于规则
,也不是基于语义
。怎么样开销小就怎么来。另外,SQL语句是否使用索引,跟数据库版本、数据量、数据选择都有关系
对经常查询的字段上建立合适的索引:
例如查询以下字段
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND classId = 4;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND classId = 4 AND NAME = 'ABCD';
建立相应的索引:
CREATE INDEX idx_age ON student(age);
CREATE INDEX idx_age_classId ON student(age,classId);
CREATE INDEX idx_age_classId_name ON student(age,classId,NAME);
如果此时再插入一条主键值为 9 的记录,那它插入的位置就如下图:
可这个数据页已经满了,再插进来咋办呢?我们需要把当前 页面分裂 成两个页面,把本页中的一些记录移动到新创建的这个页中。页面分裂和记录移位意味着什么?意味着:性能损耗 !所以如果我们想尽量避免这样无谓的性能损耗,最好让插入的记录的 主键值依次递增 ,这样就不会发生这样的性能损耗了。所以我们建议:让主键具有 AUTO_INCREMENT ,让存储引擎自己为表生成主键,而不是我们手动插入 。
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name LIKE 'abc%';
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE LEFT(student.name,3) = 'abc';
创建索引
CREATE INDEX idx_name ON student(NAME);
下列哪个sql语句可以用到索引。(假设name字段上设置有索引)
# 未使用到索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE name=123;
# 使用到索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE name='123';
ALTER TABLE student DROP INDEX idx_name; ALTER TABLE student DROP INDEX idx_age;
ALTER TABLE student DROP INDEX idx_age_classid;
EXPLAIN SELECT SQL_NO_CACHE * FROM student
WHERE student.age=30 AND student.classId>20 AND student.name = 'abc' ;
create index idx_age_name_classid on student(age,name,classid);
将范围查询条件放置语句最后:
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age=30 AND student.name = 'abc' AND student.classId>20 ;
结论:最好在设计数据表的时候就将 字段设置为NOT NULL 约束 比如你可以将INT类型设置为0。将字符类型的默认值设置值为空字符串(“”)应用层面实现 |
# 未使用到索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 10 OR classid = 100;
#使用到索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 10 OR name = 'Abel';
— 未完待续
— 常用的
w
●
总结 有一些程序员喜欢使用ROOT超级用户来访问数据库,完全把 权限控制 放在 应用层面实现 |
[mysqld]
default_password_lifetime=180 #建立全局策略,设置密码每隔180天过期
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。