MySQL调优

MySQL调优

在自己学习的过程中整理了一些笔记,先抛出一些问题引出MySQL调优的一些问题,后面为围绕性能监控、索引优化、事务来展开说明。

问题

基础知识

  • 了解mysql的三层架构
    • client层
    • server层:连接器、分析器、优化器、执行器
    • 存储引擎:几种存储引擎?各个存储引擎有什么优劣?
  • utf8字符集支持中文字节数几个,实际开发中为什么不用utf-8字符集,反之用什么?

性能监控

  • 了解 show profile和show profiles分析SQL过程
  • 了解performance schema系统数据库的各个表功能
  • 使用performance schema来进行mysql和SQL服务监控
  • 使用show processlist 来监控数据库连接
  • 什么是执行计划explain?和它的各个属性?
  • 项目中执行每条sql都要记录执行计划,其中使用事务。
  • 精通explain中的type的各个访问类型,和执行效率?(重点)

总结:搭建BS架构系统实现mysql服务和性能监控,包含以上点。

MySQL安装

#centos8安装mysql8
#下载rpm包
wget -i -c http://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
#yum安装
yum -y install mysql80-community-release-el7-3.noarch.rpm
#安装mysql server
yum -y install mysql-community-server
#启动mysql服务
systemctl start mysqld.service
#查看mysql服务状态
systemctl status mysqld.service
#查询mysql初始密码
grep "password" /var/log/mysqld.log
#进入mysql
mysql -uroot -p '输入刚才查询的密码'
#修改密码
ALTER USER 'root'@'localhost' IDENTIFIED BY 'new password';
#开启远程访问
user mysql;
update user set host='%' where user='root';
flush privileges;

索引优化

实验

索引的作用?

在库中的一个测试表添加100000条记录,查询条件根据某一个字段查询表中所需字段信息

在该查询条件字段时添加和不添加索引来比较查询时间,得出什么结论?

  • 索引的优点?
  • 索引有哪几类?
  • MyISAM和InnoDB存储引擎有什么异同和优缺点?
  • 了解二叉树、AVL树、红黑树、B树、B+树、mysql索引中为什么使用B+树?
  • hash索引和B+树索引有什么区别,各有什么优缺点?
  • 什么是回表?
  • 什么是覆盖索引?
  • 什么是索引的最左匹配原则?
  • 什么是索引下推?
  • 索引有哪些匹配方式?
  • 索引的优化策略有哪些?

具体索引的优化规则查看附件资料或书中信息

目的及要求

在执行SQL时执行计划的规则至少达到ref或range级别,最好达到const级别

在资料中一些案例自己最好手动敲一遍,熟能生巧

sakila数据库说明

ZIP格式:http://downloads.mysql.com/docs/sakila-db.zip

tar格式 http://downloads.mysql.com/docs/sakila-db.tar.gz

官方文档 http://dev.mysql.com/doc/sakila/en/index.html

解压后得到三个文件:

  1. sakila-schema.sql 文件包含创建Sakila数据库的结构:表、视图、存储过程和触发器

  2. sakila-data.sql文件包含:使用 INSERT语句填充数据及在初始数据加载后,必须创建的触发器的定义

  3. sakila.mwb文件是一个MySQL Workbench数据模型,可以在MySQL的工作台打开查看数据库结构。

--登录mysql
mysql -uroot -p password
--导入表的结构数据
source /root/sakila-schema.sql
--导入表的数据
source /root/sakila-data.sql

梳理表中名称和字段

1、actor:演员表,演员表列出了所有演员的信息。演员表和电影表之间是多对多的关系,通过film_actor表建立关系

​ actor_id:代理主键,用于唯一标识表中的每个演员

​ first_name: 演员的名字

​ last_name: 演员的姓氏

​ last_update: 该行已创建或最近更新的时间

2、address:地址表,地址表包含客户、员工和商店的地址信息。地址表的主键出现在顾客、 员工、和存储表的外键

​ address_id: 代理主键用于唯一标识表中的每个地址

​ address: 地址的第一行

​ address2: 一个可选的第二行地址

​ district: 该地区的所属地区,这可以是国家,省,县等

​ city_id: 指向城市表的外键

​ postal_code: 邮政编码

​ phone: 地址的电话号码

​ last_update: 该行已创建或最近更新的时间

3、category:分类表,类别表列出了可以分配到一个电影类别。分类和电影是多对多的关系,通过表film_category建立关系

​ category_id: 代理主键用于唯一标识表中的每个类别

​ name: 类别名称

​ last_update: 该行已创建或最近更新的时间

4、city:城市表,城市表包含的城市名单。城市表使用外键来标示国家;在地址表中被作为外键来使用。

​ city_id: 代理主键用于唯一标识表中的每个城市

​ city: 城市的名字

​ country_id: 外键,用于标示城市所属的国家

​ last_update: 该行已创建或最近更新的时间

5、country:国家表,国家表中包含的国家名单。国家表是指在城市表的外键 。

​ country_id: 代理主键用于唯一标识表中的每个国家

​ country: 国家的名称

​ last_update: 该行已创建或最近更新的时间

6、customer:客户表,客户表包含了所有客户的列表 。 客户表在支付表和租金表被作为外键使用;客户表使用外键来表示地址和存储。

​ customer_id: 代理主键用于唯一标识表中的每个客户

​ store_id: 一个外键,确定客户所属的store。

​ first_name: 客户的名字

​ last_name: 客户的姓氏

​ email: 客户的电子邮件地址

​ address_id: 使用在地址 表的外键来确定客户的地址

​ active: 表示客户是否是活跃的客户

​ create_date: 顾客被添加到系统中的日期。使用 INSERT 触发器自动设置。

​ last_update: 该行已创建或最近更新的时间

7、film:电影表,电影表是一个可能在商店库存的所有影片名单。每部影片的拷贝的实际库存信息保存在库存表。电影表指使用外键来标示语言表;在film_category、film_actor和库存表中作为外键使用。

​ film_id: 代理主键用于唯一标识表中的每个电影

​ title: 影片的标题

​ description: 一个简短的描述或电影的情节摘要

​ release_year: 电影发行的年份

​ language_id: 使用外键来标示语言

​ original_language_id: 电影的原始语音。使用外键来标示语言

​ rental_duration: 租赁期限的长短,以天作为单位

​ rental_rate: 指定的期限内电影的租金

​ length: 影片的长度,以分钟为单位。

​ replacement_cost: 如果电影未被归还或损坏状态向客户收取的款项

​ rating: 分配给电影评级。可以是 G, PG,PG - 13 , R 或NC - 17

​ special_features: 包括DVD上常见的特殊功能的列表

​ last_update: 该行已创建或最近更新的时间

8、film_actor:电影演员表,film_actor表是用来支持许多电影和演员之间的多对多关系 。对于每一个给定的电影演员,将有film_actor表中列出的演员和电影中的一个行 。

​ actor_id: 用于识别演员的外键

​ film_id: 用于识别电影的外键

​ last_update: 该行已创建或最近更新的时间

9、film_category:电影类别表,film_category表是用来支持许多电影和类别之间的多对多关系 。应用于电影的每个类别中,将有film_category表中列出的类别和电影中的一个行。

​ film_id: 用于识别电影的外键

​ category_id: 用于识别类别的外键

​ last_update: 该行已创建或最近更新的时间

10、film_text:电影信息表,film_text表是Sakila样例数据库唯一使用MyISAM存储引擎的表。MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持。MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快。此表提供允许全文搜索电影表中列出的影片的标题和描述。film_text表包含的film_id,标题和描述的列电影表,保存的内容与电影表上的内容同步(指电影表的插入、更新和删除操作)

​ film_id: 代理主键用于唯一标识表中的每个电影

​ title: 影片的标题

​ description: 一个简短的描述或电影的情节摘要

11、inventory:库存表,库存表的一行为存放在一个给定的商店里的一个给定的电影的copy副本。库存表是使用外键来识别电影和存储;在出租表中使用外键来识别库存。

​ inventory_id: 理主键用于唯一标识每个项目在库存

​ film_id: 使用外键来识别电影

​ store_id: 使用外键来识别物品所在的商店

​ last_update: 该行已创建或最近更新的时间

12、language:语言表,语言表是一个查找表,列出可能使用的语言,电影可以有自己的语言和原始语言值 。

语言表在电压表中被作为外键来使用。

​ language_id: 代理主键用于唯一标识每一种语言

​ name: 语言的英文名称

​ last_update: 该行已创建或最近更新的时间

13、payment:付款表,付款表记录每个客户的付款,如支付的金额和租金的资料。

付款表使用外键来表示客户、出租、和工作人员。

​ payment_id: 代理主键用于唯一标识每个付款

​ customer_id: 使用外键来标识付款的客户

​ staff_id: 工作人员,负责处理支付 。使用外键来标识

​ rental_id: 租借ID, 外键,参照rental表

​ amount: 付款金额

​ payment_date: 处理付款的日期

​ last_update: 该行已创建或最近更新的时间

14 、rental:租金表,租借表的一行表示每个inventory的租借客户、租借时间、归还时间

租借表是使用外键来标识库存 ,顾客 和工作人员;在支付表中使用了外键来标识租金 。

​ rental_id: 代理主键唯一标识的租金

​ rental_date: 该项目租用的日期和时间

​ inventory_id: 该项目被租用

​ customer_id: 租用该项目的客户

​ return_date: 归还日期

​ staff_id: 处理该项业务的工作人员

​ last_update: 该行已创建或最近更新的时间

15、staff:工作人员表,工作人员表列出了所有的工作人员,包括电子邮件地址,登录信息和图片信息 。

工作人员表是指使用外键来标识存储和地址表;在出租、支付和商店表中作为外键。

​ staff_id: 代理主键唯一标识的工作人员

​ first_name: 工作人员的名字

​ last_name: 工作人员的姓氏

​ address_id: 工作人员的地址在地址表的外键

​ picture: 工作人员的照片,使用了 BLOB属性

​ email: 工作人员的电子邮件地址

​ store_id: 工作人员所在的商店,用外键标识

​ active: 是否是活跃的工作人员。

​ username: 用户名,由工作人员用来访问租赁系统

​ password: 工作人员访问租赁系统所使用的密码。使用了 SHA1 函数

​ last_update: 该行已创建或最近更新的时间

​ active: 是否有效,删除时设置为False

16、store:商店表,store表列出了系统中的所有商店 。

store使用外键来标识工作人员和地址;在员工、客户、库存表被作为外键使用。

​ store_id: 代理主键唯一标识的商店

​ manager_staff_id: 使用外键来标识这家商店的经理

​ address_id: 使用外键来确定这家店的地址

​ last_update: 该行已创建或最近更新的时间

视图表

1、actor_info视图提供了所有演员的列表及所演的电影, 电影按category分组.

SELECT
a.actor_id,
a.first_name,
a.last_name,
GROUP_CONCAT(DISTINCT CONCAT(c.name, ‘: ‘,
        (SELECT GROUP_CONCAT(f.title ORDER BY f.title SEPARATOR ‘,)
                    FROM sakila.film f
                    INNER JOIN sakila.film_category fc
                      ON f.film_id = fc.film_id
                    INNER JOIN sakila.film_actor fa
                      ON f.film_id = fa.film_id
                    WHERE fc.category_id = c.category_id
                    AND fa.actor_id = a.actor_id
                 )
             )
             ORDER BY c.name SEPARATOR ‘;)
AS film_info
FROM sakila.actor a
LEFT JOIN sakila.film_actor fa
  ON a.actor_id = fa.actor_id
LEFT JOIN sakila.film_category fc
  ON fa.film_id = fc.film_id
LEFT JOIN sakila.category c
  ON fc.category_id = c.category_id
GROUP BY a.actor_id, a.first_name, a.last_name

2、customer_list:客户列表,firstname和lastname连接成fullname,将address, city, country 集成在一个视图里

SELECT
    cu.customer_id AS ID,
    CONCAT(
        cu.first_name,
        _utf8 ‘ ‘,
        cu.last_name
    ) AS NAME,
    a.address AS address,
    a.postal_code AS `zip code`,
    a.phone AS phone,
    city.city AS city,
    country.country AS country,

IF (
    cu.active,
    _utf8 ‘active‘,
    _utf8 ‘‘
) AS notes,
 cu.store_id AS SID
FROM
    customer AS cu
JOIN address AS a ON cu.address_id = a.address_id
JOIN city ON a.city_id = city.city_id
JOIN country ON city.country_id = country.country_id

3、film_list:电影列表视图,包含了每一部电影的信息及电影所对应的演员。电影对应的演员以逗号作为分隔符。连接了 film, film_category, category,film_actor and actor 表的数据

SELECT
    film.film_id AS FID,
    film.title AS title,
    film.description AS description,
    category. NAME AS category,
    film.rental_rate AS price,
    film.length AS length,
    film.rating AS rating,
    GROUP_CONCAT(
        CONCAT(
            actor.first_name,
            _utf8 ‘ ‘,
            actor.last_name
        ) SEPARATOR ‘,) AS actors
FROM
    category
LEFT JOIN film_category ON category.category_id = film_category.category_id
LEFT JOIN film ON film_category.film_id = film.film_id
JOIN film_actor ON film.film_id = film_actor.film_id
JOIN actor ON film_actor.actor_id = actor.actor_id
GROUP BY
    film.film_id

4、nicer_but_slower_film_list:电影列表视图,包含了每一部电影的信息及电影所对应的演员。电影对应的演员以逗号作为分隔符。连接了 film, film_category, category,film_actorandactor 表的数据。和The film_list View不同,演员名字只有单词首字母大写了。

SELECT
    film.film_id AS FID,
    film.title AS title,
    film.description AS description,
    category. NAME AS category,
    film.rental_rate AS price,
    film.length AS length,
    film.rating AS rating,
    GROUP_CONCAT(
        CONCAT(
            CONCAT(
                UCASE(
                    SUBSTR(actor.first_name, 1, 1)
                ),
                LCASE(
                    SUBSTR(
                        actor.first_name,
                        2,
                        LENGTH(actor.first_name)
                    )
                ),
                _utf8 ‘ ‘,
                CONCAT(
                    UCASE(
                        SUBSTR(actor.last_name, 1, 1)
                    ),
                    LCASE(
                        SUBSTR(
                            actor.last_name,
                            2,
                            LENGTH(actor.last_name)
                        )
                    )
                )
            )
        ) SEPARATOR ‘,) AS actors
FROM
    category
LEFT JOIN film_category ON category.category_id = film_category.category_id
LEFT JOIN film ON film_category.film_id = film.film_id
JOIN film_actor ON film.film_id = film_actor.film_id
JOIN actor ON film_actor.actor_id = actor.actor_id
GROUP BY
    film.film_id

5、sales_by_film_category:每个电影种类的销售额 , payment →rental →inventory → film → film_category → category

SELECT
c.name AS category
, SUM(p.amount) AS total_sales
FROM payment AS p
INNER JOIN rental AS r ON p.rental_id = r.rental_id
INNER JOIN inventory AS i ON r.inventory_id = i.inventory_id
INNER JOIN film AS f ON i.film_id = f.film_id
INNER JOIN film_category AS fc ON f.film_id = fc.film_id
INNER JOIN category AS c ON fc.category_id = c.category_id
GROUP BY c.name
ORDER BY total_sales DESC

6、sales_by_store:每个商店的manager及销售额。payment → rental → inventory → store → staff

SELECT
CONCAT(c.city, _utf8‘,, cy.country) AS store
, CONCAT(m.first_name, _utf8‘ ‘, m.last_name) AS manager
, SUM(p.amount) AS total_sales
FROM payment AS p
INNER JOIN rental AS r ON p.rental_id = r.rental_id
INNER JOIN inventory AS i ON r.inventory_id = i.inventory_id
INNER JOIN store AS s ON i.store_id = s.store_id
INNER JOIN address AS a ON s.address_id = a.address_id
INNER JOIN city AS c ON a.city_id = c.city_id
INNER JOIN country AS cy ON c.country_id = cy.country_id
INNER JOIN staff AS m ON s.manager_staff_id = m.staff_id
GROUP BY s.store_id
ORDER BY cy.country, c.city

7、staff_list:工作人员的列表

SELECT
    s.staff_id AS ID,
    CONCAT(
        s.first_name,
        _utf8 ‘ ‘,
        s.last_name
    ) AS NAME,
    a.address AS address,
    a.postal_code AS `zip code`,
    a.phone AS phone,
    city.city AS city,
    country.country AS country,
    s.store_id AS SID
FROM
    staff AS s
JOIN address AS a ON s.address_id = a.address_id
JOIN city ON a.city_id = city.city_id
JOIN country ON city.country_id = country.country_id

Performance Schema

介绍

MySQL的performance schema 用于监控MySQL server在一个较低级别的运行过程中的资源消耗、资源等待等情况

​ 特点如下:

​ 1、提供了一种在数据库运行时实时检查server的内部执行情况的方法。performance_schema 数据库中的表使用performance_schema存储引擎。该数据库主要关注数据库运行过程中的性能相关的数据,与information_schema不同,information_schema主要关注server运行过程中的元数据信息

​ 2、performance_schema通过监视server的事件来实现监视server内部运行情况, “事件”就是server内部活动中所做的任何事情以及对应的时间消耗,利用这些信息来判断server中的相关资源消耗在了哪里?一般来说,事件可以是函数调用、操作系统的等待、SQL语句执行的阶段(如sql语句执行过程中的parsing 或 sorting阶段)或者整个SQL语句与SQL语句集合。事件的采集可以方便的提供server中的相关存储引擎对磁盘文件、表I/O、表锁等资源的同步调用信息。
​ 3、performance_schema中的事件与写入二进制日志中的事件(描述数据修改的events)、事件计划调度程序(这是一种存储程序)的事件不同。performance_schema中的事件记录的是server执行某些活动对某些资源的消耗、耗时、这些活动执行的次数等情况。
​ 4、performance_schema中的事件只记录在本地server的performance_schema中,其下的这些表中数据发生变化时不会被写入binlog中,也不会通过复制机制被复制到其他server中。
​ 5、 当前活跃事件、历史事件和事件摘要相关的表中记录的信息。能提供某个事件的执行次数、使用时长。进而可用于分析某个特定线程、特定对象(如mutex或file)相关联的活动。
​ 6、PERFORMANCE_SCHEMA存储引擎使用server源代码中的“检测点”来实现事件数据的收集。对于performance_schema实现机制本身的代码没有相关的单独线程来检测,这与其他功能(如复制或事件计划程序)不同
​ 7、收集的事件数据存储在performance_schema数据库的表中。这些表可以使用SELECT语句查询,也可以使用SQL语句更新performance_schema数据库中的表记录(如动态修改performance_schema的setup_*开头的几个配置表,但要注意:配置表的更改会立即生效,这会影响数据收集)
​ 8、performance_schema的表中的数据不会持久化存储在磁盘中,而是保存在内存中,一旦服务器重启,这些数据会丢失(包括配置表在内的整个performance_schema下的所有数据)
​ 9、MySQL支持的所有平台中事件监控功能都可用,但不同平台中用于统计事件时间开销的计时器类型可能会有所差异。

入门

​ 在mysql的5.7版本中,性能模式是默认开启的,如果想要显式的关闭的话需要修改配置文件,不能直接进行修改,会报错Variable ‘performance_schema’ is a read only variable。

--查看performance_schema的属性
mysql> SHOW VARIABLES LIKE 'performance_schema';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| performance_schema | ON    |
+--------------------+-------+
1 row in set (0.01 sec)

--在配置文件中修改performance_schema的属性值,on表示开启,off表示关闭
[mysqld]
performance_schema=ON

--切换数据库
use performance_schema;

--查看当前数据库下的所有表,会看到有很多表存储着相关的信息
show tables;

--可以通过show create table tablename来查看创建表的时候的表结构
mysql> show create table setup_consumers;
+-----------------+---------------------------------
| Table           | Create Table                    
+-----------------+---------------------------------
| setup_consumers | CREATE TABLE `setup_consumers` (
  `NAME` varchar(64) NOT NULL,                      
  `ENABLED` enum('YES','NO') NOT NULL               
) ENGINE=PERFORMANCE_SCHEMA DEFAULT CHARSET=utf8 |  
+-----------------+---------------------------------
1 row in set (0.00 sec)                             

​ 想要搞明白后续的内容,需要理解两个基本概念:

​ instruments: 生产者,用于采集mysql中各种各样的操作产生的事件信息,对应配置表中的配置项我们可以称为监控采集配置项。

​ consumers:消费者,对应的消费者表用于存储来自instruments采集的数据,对应配置表中的配置项我们可以称为消费存储配置项。

表分类

​ performance_schema库下的表可以按照监视不同的纬度就行分组。

--语句事件记录表,这些表记录了语句事件信息,当前语句事件表events_statements_current、历史语句事件表events_statements_history和长语句历史事件表events_statements_history_long、以及聚合后的摘要表summary,其中,summary表还可以根据帐号(account),主机(host),程序(program),线程(thread),用户(user)和全局(global)再进行细分)
show tables like '%statement%';

--等待事件记录表,与语句事件类型的相关记录表类似:
show tables like '%wait%';

--阶段事件记录表,记录语句执行的阶段事件的表
show tables like '%stage%';

--事务事件记录表,记录事务相关的事件的表
show tables like '%transaction%';

--监控文件系统层调用的表
show tables like '%file%';

--监视内存使用的表
show tables like '%memory%';

--动态对performance_schema进行配置的配置表
show tables like '%setup%';

配置与使用

​ 数据库刚刚初始化并启动时,并非所有instruments(事件采集项,在采集项的配置表中每一项都有一个开关字段,或为YES,或为NO)和consumers(与采集项类似,也有一个对应的事件类型保存表配置项,为YES就表示对应的表保存性能数据,为NO就表示对应的表不保存性能数据)都启用了,所以默认不会收集所有的事件,可能你需要检测的事件并没有打开,需要进行设置,可以使用如下两个语句打开对应的instruments和consumers(行计数可能会因MySQL版本而异)。

--打开等待事件的采集器配置项开关,需要修改setup_instruments配置表中对应的采集器配置项
UPDATE setup_instruments SET ENABLED = 'YES', TIMED = 'YES'where name like 'wait%';

--打开等待事件的保存表配置开关,修改setup_consumers配置表中对应的配置项
UPDATE setup_consumers SET ENABLED = 'YES'where name like '%wait%';

--当配置完成之后可以查看当前server正在做什么,可以通过查询events_waits_current表来得知,该表中每个线程只包含一行数据,用于显示每个线程的最新监视事件
select * from events_waits_current\G
*************************** 1. row ***************************
            THREAD_ID: 11
             EVENT_ID: 570
         END_EVENT_ID: 570
           EVENT_NAME: wait/synch/mutex/innodb/buf_dblwr_mutex
               SOURCE: 
          TIMER_START: 4508505105239280
            TIMER_END: 4508505105270160
           TIMER_WAIT: 30880
                SPINS: NULL
        OBJECT_SCHEMA: NULL
          OBJECT_NAME: NULL
           INDEX_NAME: NULL
          OBJECT_TYPE: NULL
OBJECT_INSTANCE_BEGIN: 67918392
     NESTING_EVENT_ID: NULL
   NESTING_EVENT_TYPE: NULL
            OPERATION: lock
      NUMBER_OF_BYTES: NULL
                FLAGS: NULL
/*该信息表示线程id为11的线程正在等待buf_dblwr_mutex锁,等待事件为30880
属性说明:
	id:事件来自哪个线程,事件编号是多少
	event_name:表示检测到的具体的内容
	source:表示这个检测代码在哪个源文件中以及行号
	timer_start:表示该事件的开始时间
	timer_end:表示该事件的结束时间
	timer_wait:表示该事件总的花费时间
注意:_current表中每个线程只保留一条记录,一旦线程完成工作,该表中不会再记录该线程的事件信息
*/

/*
_history表中记录每个线程应该执行完成的事件信息,但每个线程的事件信息只会记录10条,再多就会被覆盖,*_history_long表中记录所有线程的事件信息,但总记录数量是10000,超过就会被覆盖掉
*/
select thread_id,event_id,event_name,timer_wait from events_waits_history order by thread_id limit 21;

/*
summary表提供所有事件的汇总信息,该组中的表以不同的方式汇总事件数据(如:按用户,按主机,按线程等等)。例如:要查看哪些instruments占用最多的时间,可以通过对events_waits_summary_global_by_event_name表的COUNT_STAR或SUM_TIMER_WAIT列进行查询(这两列是对事件的记录数执行COUNT(*)、事件记录的TIMER_WAIT列执行SUM(TIMER_WAIT)统计而来)
*/
SELECT EVENT_NAME,COUNT_STAR FROM events_waits_summary_global_by_event_name  ORDER BY COUNT_STAR DESC LIMIT 10;

/*
instance表记录了哪些类型的对象会被检测。这些对象在被server使用时,在该表中将会产生一条事件记录,例如,file_instances表列出了文件I/O操作及其关联文件名
*/
select * from file_instances limit 20; 

配置项参数说明

1、启动选项

performance_schema_consumer_events_statements_current=TRUE
是否在mysql server启动时就开启events_statements_current表的记录功能(该表记录当前的语句事件信息),启动之后也可以在setup_consumers表中使用UPDATE语句进行动态更新setup_consumers配置表中的events_statements_current配置项,默认值为TRUE

performance_schema_consumer_events_statements_history=TRUE
与performance_schema_consumer_events_statements_current选项类似,但该选项是用于配置是否记录语句事件短历史信息,默认为TRUE

performance_schema_consumer_events_stages_history_long=FALSE
与performance_schema_consumer_events_statements_current选项类似,但该选项是用于配置是否记录语句事件长历史信息,默认为FALSE

除了statement(语句)事件之外,还支持:wait(等待)事件、state(阶段)事件、transaction(事务)事件,他们与statement事件一样都有三个启动项分别进行配置,但这些等待事件默认未启用,如果需要在MySQL Server启动时一同启动,则通常需要写进my.cnf配置文件中
performance_schema_consumer_global_instrumentation=TRUE
是否在MySQL Server启动时就开启全局表(如:mutex_instances、rwlock_instances、cond_instances、file_instances、users、hostsaccounts、socket_summary_by_event_name、file_summary_by_instance等大部分的全局对象计数统计和事件汇总统计信息表 )的记录功能,启动之后也可以在setup_consumers表中使用UPDATE语句进行动态更新全局配置项
默认值为TRUE

performance_schema_consumer_statements_digest=TRUE
是否在MySQL Server启动时就开启events_statements_summary_by_digest 表的记录功能,启动之后也可以在setup_consumers表中使用UPDATE语句进行动态更新digest配置项
默认值为TRUE

performance_schema_consumer_thread_instrumentation=TRUE
是否在MySQL Server启动时就开启

events_xxx_summary_by_yyy_by_event_name表的记录功能,启动之后也可以在setup_consumers表中使用UPDATE语句进行动态更新线程配置项
默认值为TRUE

performance_schema_instrument[=name]
是否在MySQL Server启动时就启用某些采集器,由于instruments配置项多达数千个,所以该配置项支持key-value模式,还支持%号进行通配等,如下:

# [=name]可以指定为具体的Instruments名称(但是这样如果有多个需要指定的时候,就需要使用该选项多次),也可以使用通配符,可以指定instruments相同的前缀+通配符,也可以使用%代表所有的instruments

## 指定开启单个instruments

--performance-schema-instrument= 'instrument_name=value'

## 使用通配符指定开启多个instruments

--performance-schema-instrument= 'wait/synch/cond/%=COUNTED'

## 开关所有的instruments

--performance-schema-instrument= '%=ON'

--performance-schema-instrument= '%=OFF'

注意,这些启动选项要生效的前提是,需要设置performance_schema=ON。另外,这些启动选项虽然无法使用show variables语句查看,但我们可以通过setup_instruments和setup_consumers表查询这些选项指定的值。

2、系统变量

show variables like '%performance_schema%';
--重要的属性解释
performance_schema=ON
/*
控制performance_schema功能的开关,要使用MySQL的performance_schema,需要在mysqld启动时启用,以启用事件收集功能
该参数在5.7.x之前支持performance_schema的版本中默认关闭,5.7.x版本开始默认开启
注意:如果mysqld在初始化performance_schema时发现无法分配任何相关的内部缓冲区,则performance_schema将自动禁用,并将performance_schema设置为OFF
*/

performance_schema_digests_size=10000
/*
控制events_statements_summary_by_digest表中的最大行数。如果产生的语句摘要信息超过此最大值,便无法继续存入该表,此时performance_schema会增加状态变量
*/
performance_schema_events_statements_history_long_size=10000
/*
控制events_statements_history_long表中的最大行数,该参数控制所有会话在events_statements_history_long表中能够存放的总事件记录数,超过这个限制之后,最早的记录将被覆盖
全局变量,只读变量,整型值,5.6.3版本引入 * 5.6.x版本中,5.6.5及其之前的版本默认为10000,5.6.6及其之后的版本默认值为-1,通常情况下,自动计算的值都是10000 * 5.7.x版本中,默认值为-1,通常情况下,自动计算的值都是10000
*/
performance_schema_events_statements_history_size=10
/*
控制events_statements_history表中单个线程(会话)的最大行数,该参数控制单个会话在events_statements_history表中能够存放的事件记录数,超过这个限制之后,单个会话最早的记录将被覆盖
全局变量,只读变量,整型值,5.6.3版本引入 * 5.6.x版本中,5.6.5及其之前的版本默认为10,5.6.6及其之后的版本默认值为-1,通常情况下,自动计算的值都是10 * 5.7.x版本中,默认值为-1,通常情况下,自动计算的值都是10
除了statement(语句)事件之外,wait(等待)事件、state(阶段)事件、transaction(事务)事件,他们与statement事件一样都有三个参数分别进行存储限制配置,有兴趣的同学自行研究,这里不再赘述
*/
performance_schema_max_digest_length=1024
/*
用于控制标准化形式的SQL语句文本在存入performance_schema时的限制长度,该变量与max_digest_length变量相关(max_digest_length变量含义请自行查阅相关资料)
全局变量,只读变量,默认值1024字节,整型值,取值范围0~1048576
*/
performance_schema_max_sql_text_length=1024
/*
控制存入events_statements_current,events_statements_history和events_statements_history_long语句事件表中的SQL_TEXT列的最大SQL长度字节数。 超出系统变量performance_schema_max_sql_text_length的部分将被丢弃,不会记录,一般情况下不需要调整该参数,除非被截断的部分与其他SQL比起来有很大差异
全局变量,只读变量,整型值,默认值为1024字节,取值范围为0~1048576,5.7.6版本引入
降低系统变量performance_schema_max_sql_text_length值可以减少内存使用,但如果汇总的SQL中,被截断部分有较大差异,会导致没有办法再对这些有较大差异的SQL进行区分。 增加该系统变量值会增加内存使用,但对于汇总SQL来讲可以更精准地区分不同的部分。
*/

配置表相关说明

/*
performance_timers表中记录了server中有哪些可用的事件计时器
字段解释:
	timer_name:表示可用计时器名称,CYCLE是基于CPU周期计数器的定时器
	timer_frequency:表示每秒钟对应的计时器单位的数量,CYCLE计时器的换算值与CPU的频率相关、
	timer_resolution:计时器精度值,表示在每个计时器被调用时额外增加的值
	timer_overhead:表示在使用定时器获取事件时开销的最小周期值
*/
select * from performance_timers;

/*
setup_timers表中记录当前使用的事件计时器信息
字段解释:
	name:计时器类型,对应某个事件类别
	timer_name:计时器类型名称
*/
select * from setup_timers;

/*
setup_consumers表中列出了consumers可配置列表项
字段解释:
	NAME:consumers配置名称
	ENABLED:consumers是否启用,有效值为YES或NO,此列可以使用UPDATE语句修改。
*/
select * from setup_consumers;

/*
setup_instruments 表列出了instruments 列表配置项,即代表了哪些事件支持被收集:
字段解释:
	NAME:instruments名称,instruments名称可能具有多个部分并形成层次结构
	ENABLED:instrumetns是否启用,有效值为YES或NO,此列可以使用UPDATE语句修改。如果设置为NO,则这个instruments不会被执行,不会产生任何的事件信息
	TIMED:instruments是否收集时间信息,有效值为YES或NO,此列可以使用UPDATE语句修改,如果设置为NO,则这个instruments不会收集时间信息
*/
SELECT * FROM setup_instruments;

/*
setup_actors表的初始内容是匹配任何用户和主机,因此对于所有前台线程,默认情况下启用监视和历史事件收集功能
字段解释:
	HOST:与grant语句类似的主机名,一个具体的字符串名字,或使用“%”表示“任何主机”
	USER:一个具体的字符串名称,或使用“%”表示“任何用户”
	ROLE:当前未使用,MySQL 8.0中才启用角色功能
	ENABLED:是否启用与HOST,USER,ROLE匹配的前台线程的监控功能,有效值为:YES或NO
	HISTORY:是否启用与HOST, USER,ROLE匹配的前台线程的历史事件记录功能,有效值为:YES或NO
*/
SELECT * FROM setup_actors;

/*
setup_objects表控制performance_schema是否监视特定对象。默认情况下,此表的最大行数为100行。
字段解释:
	OBJECT_TYPE:instruments类型,有效值为:“EVENT”(事件调度器事件)、“FUNCTION”(存储函数)、“PROCEDURE”(存储过程)、“TABLE”(基表)、“TRIGGER”(触发器),TABLE对象类型的配置会影响表I/O事件(wait/io/table/sql/handler instrument)和表锁事件(wait/lock/table/sql/handler instrument)的收集
	OBJECT_SCHEMA:某个监视类型对象涵盖的数据库名称,一个字符串名称,或“%”(表示“任何数据库”)
	OBJECT_NAME:某个监视类型对象涵盖的表名,一个字符串名称,或“%”(表示“任何数据库内的对象”)
	ENABLED:是否开启对某个类型对象的监视功能,有效值为:YES或NO。此列可以修改
	TIMED:是否开启对某个类型对象的时间收集功能,有效值为:YES或NO,此列可以修改
*/
SELECT * FROM setup_objects;

/*
threads表对于每个server线程生成一行包含线程相关的信息,
字段解释:
	THREAD_ID:线程的唯一标识符(ID)
	NAME:与server中的线程检测代码相关联的名称(注意,这里不是instruments名称)
	TYPE:线程类型,有效值为:FOREGROUND、BACKGROUND。分别表示前台线程和后台线程
	PROCESSLIST_ID:对应INFORMATION_SCHEMA.PROCESSLIST表中的ID列。
	PROCESSLIST_USER:与前台线程相关联的用户名,对于后台线程为NULL。
	PROCESSLIST_HOST:与前台线程关联的客户端的主机名,对于后台线程为NULL。
	PROCESSLIST_DB:线程的默认数据库,如果没有,则为NULL。
	PROCESSLIST_COMMAND:对于前台线程,该值代表着当前客户端正在执行的command类型,如果是sleep则表示当前会话处于空闲状态
	PROCESSLIST_TIME:当前线程已处于当前线程状态的持续时间(秒)
	PROCESSLIST_STATE:表示线程正在做什么事情。
	PROCESSLIST_INFO:线程正在执行的语句,如果没有执行任何语句,则为NULL。
	PARENT_THREAD_ID:如果这个线程是一个子线程(由另一个线程生成),那么该字段显示其父线程ID
	ROLE:暂未使用
	INSTRUMENTED:线程执行的事件是否被检测。有效值:YES、NO 
	HISTORY:是否记录线程的历史事件。有效值:YES、NO * 
	THREAD_OS_ID:由操作系统层定义的线程或任务标识符(ID):
*/
select * from threads

注意:在performance_schema库中还包含了很多其他的库和表,能对数据库的性能做完整的监控,大家需要参考官网详细了解。

实践

​ 基本了解了表的相关信息之后,可以通过这些表进行实际的查询操作来进行实际的分析。

--1、哪类的SQL执行最多?
SELECT DIGEST_TEXT,COUNT_STAR,FIRST_SEEN,LAST_SEEN FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--2、哪类SQL的平均响应时间最多?
SELECT DIGEST_TEXT,AVG_TIMER_WAIT FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--3、哪类SQL排序记录数最多?
SELECT DIGEST_TEXT,SUM_SORT_ROWS FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--4、哪类SQL扫描记录数最多?
SELECT DIGEST_TEXT,SUM_ROWS_EXAMINED FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--5、哪类SQL使用临时表最多?
SELECT DIGEST_TEXT,SUM_CREATED_TMP_TABLES,SUM_CREATED_TMP_DISK_TABLES FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--6、哪类SQL返回结果集最多?
SELECT DIGEST_TEXT,SUM_ROWS_SENT FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--7、哪个表物理IO最多?
SELECT file_name,event_name,SUM_NUMBER_OF_BYTES_READ,SUM_NUMBER_OF_BYTES_WRITE FROM file_summary_by_instance ORDER BY SUM_NUMBER_OF_BYTES_READ + SUM_NUMBER_OF_BYTES_WRITE DESC
--8、哪个表逻辑IO最多?
SELECT object_name,COUNT_READ,COUNT_WRITE,COUNT_FETCH,SUM_TIMER_WAIT FROM table_io_waits_summary_by_table ORDER BY sum_timer_wait DESC
--9、哪个索引访问最多?
SELECT OBJECT_NAME,INDEX_NAME,COUNT_FETCH,COUNT_INSERT,COUNT_UPDATE,COUNT_DELETE FROM table_io_waits_summary_by_index_usage ORDER BY SUM_TIMER_WAIT DESC
--10、哪个索引从来没有用过?
SELECT OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME FROM table_io_waits_summary_by_index_usage WHERE INDEX_NAME IS NOT NULL AND COUNT_STAR = 0 AND OBJECT_SCHEMA <> 'mysql' ORDER BY OBJECT_SCHEMA,OBJECT_NAME;
--11、哪个等待事件消耗时间最多?
SELECT EVENT_NAME,COUNT_STAR,SUM_TIMER_WAIT,AVG_TIMER_WAIT FROM events_waits_summary_global_by_event_name WHERE event_name != 'idle' ORDER BY SUM_TIMER_WAIT DESC
--12-1、剖析某条SQL的执行情况,包括statement信息,stege信息,wait信息
SELECT EVENT_ID,sql_text FROM events_statements_history WHERE sql_text LIKE '%count(*)%';
--12-2、查看每个阶段的时间消耗
SELECT event_id,EVENT_NAME,SOURCE,TIMER_END - TIMER_START FROM events_stages_history_long WHERE NESTING_EVENT_ID = 1553;
--12-3、查看每个阶段的锁等待情况
SELECT event_id,event_name,source,timer_wait,object_name,index_name,operation,nesting_event_id FROM events_waits_history_longWHERE nesting_event_id = 1553;

执行计划explain

​ 在企业的应用场景中,为了知道优化SQL语句的执行,需要查看SQL语句的具体执行过程,以加快SQL语句的执行效率。

​ 可以使用explain+SQL语句来模拟优化器执行SQL查询语句,从而知道mysql是如何处理sql语句的。

​ 官网地址: https://dev.mysql.com/doc/refman/5.5/en/explain-output.html

基本信息
Column Meaning
id The SELECT identifier
select_type The SELECT type
table The table for the output row
partitions The matching partitions
type The join type
possible_keys The possible indexes to choose
key The index actually chosen
key_len The length of the chosen key
ref The columns compared to the index
rows Estimate of rows to be examined
filtered Percentage of rows filtered by table condition
extra Additional information

id

select查询的序列号,包含一组数字,表示查询中执行select子句或者操作表的顺序

id号分为三种情况:

​ 1、如果id相同,那么执行顺序从上到下

explain select * from emp e join dept d on e.deptno = d.deptno join salgrade sg on e.sal between sg.losal and sg.hisal;

​ 2、如果id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行

explain select * from emp e where e.deptno in (select d.deptno from dept d where d.dname = 'SALES');

​ 3、id相同和不同的,同时存在:相同的可以认为是一组,从上往下顺序执行,在所有组中,id值越大,优先级越高,越先执行

explain select * from emp e join dept d on e.deptno = d.deptno join salgrade sg on e.sal between sg.losal and sg.hisal where e.deptno in (select d.deptno from dept d where d.dname = 'SALES');

select_type

主要用来分辨查询的类型,是普通查询还是联合查询还是子查询

select_type Value Meaning
SIMPLE Simple SELECT (not using UNION or subqueries)
PRIMARY Outermost SELECT
UNION Second or later SELECT statement in a UNION
DEPENDENT UNION Second or later SELECT statement in a UNION, dependent on outer query
UNION RESULT Result of a UNION.
SUBQUERY First SELECT in subquery
DEPENDENT SUBQUERY First SELECT in subquery, dependent on outer query
DERIVED Derived table
UNCACHEABLE SUBQUERY A subquery for which the result cannot be cached and must be re-evaluated for each row of the outer query
UNCACHEABLE UNION The second or later select in a UNION that belongs to an uncacheable subquery (see UNCACHEABLE SUBQUERY)
--sample:简单的查询,不包含子查询和union
explain select * from emp;

--primary:查询中若包含任何复杂的子查询,最外层查询则被标记为Primary
explain select staname,ename supname from (select ename staname,mgr from emp) t join emp on t.mgr=emp.empno ;

--union:若第二个select出现在union之后,则被标记为union
explain select * from emp where deptno = 10 union select * from emp where sal >2000;

--dependent union:跟union类似,此处的depentent表示union或union all联合而成的结果会受外部表影响
explain select * from emp e where e.empno  in ( select empno from emp where deptno = 10 union select empno from emp where sal >2000)

--union result:从union表获取结果的select
explain select * from emp where deptno = 10 union select * from emp where sal >2000;

--subquery:在select或者where列表中包含子查询
explain select * from emp where sal > (select avg(sal) from emp) ;

--dependent subquery:subquery的子查询要受到外部表查询的影响
explain select * from emp e where e.deptno in (select distinct deptno from dept);

--DERIVED: from子句中出现的子查询,也叫做派生类,
explain select staname,ename supname from (select ename staname,mgr from emp) t join emp on t.mgr=emp.empno ;

--UNCACHEABLE SUBQUERY:表示使用子查询的结果不能被缓存
 explain select * from emp where empno = (select empno from emp where deptno=@@sort_buffer_size);
 
--uncacheable union:表示union的查询结果不能被缓存:sql语句未验证

table

对应行正在访问哪一个表,表名或者别名,可能是临时表或者union合并结果集
1、如果是具体的表名,则表明从实际的物理表中获取数据,当然也可以是表的别名

​ 2、表名是derivedN的形式,表示使用了id为N的查询产生的衍生表

​ 3、当有union result的时候,表名是union n1,n2等的形式,n1,n2表示参与union的id

type

type显示的是访问类型,访问类型表示我是以何种方式去访问我们的数据,最容易想的是全表扫描,直接暴力的遍历一张表去寻找需要的数据,效率非常低下,访问的类型有很多,效率从最好到最坏依次是:

system > const > eq_ref > ref> fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般情况下,得保证查询至少达到range级别,最好能达到ref

--all:全表扫描,一般情况下出现这样的sql语句而且数据量比较大的话那么就需要进行优化。
explain select * from emp;

--index:全索引扫描这个比all的效率要好,主要有两种情况,一种是当前的查询时覆盖索引,即我们需要的数据在索引中就可以索取,或者是使用了索引进行排序,这样就避免数据的重排序
explain  select empno from emp;

--range:表示利用索引查询的时候限制了范围,在指定范围内进行查询,这样避免了index的全索引扫描,适用的操作符: =, <>, >, >=, <, <=, IS NULL, BETWEEN, LIKE, or IN() 
explain select * from emp where empno between 7000 and 7500;

--index_subquery:利用索引来关联子查询,不再扫描全表
explain select * from emp where emp.job in (select job from t_job);

--unique_subquery:该连接类型类似与index_subquery,使用的是唯一索引
 explain select * from emp e where e.deptno in (select distinct deptno from dept);
 
--index_merge:在查询过程中需要多个索引组合使用,没有模拟出来

--ref_or_null:对于某个字段即需要关联条件,也需要null值的情况下,查询优化器会选择这种访问方式
explain select * from emp e where  e.mgr is null or e.mgr=7369;

--ref:使用了非唯一性索引进行数据的查找
 create index idx_3 on emp(deptno);
 explain select * from emp e,dept d where e.deptno =d.deptno;

--eq_ref :使用唯一性索引进行数据查找
explain select * from emp,emp2 where emp.empno = emp2.empno;

--const:这个表至多有一个匹配行,
explain select * from emp where empno = 7369;
 
--system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现

possible_keys

​ 显示可能应用在这张表中的索引,一个或多个,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用

explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;

key

​ 实际使用的索引,如果为null,则没有使用索引,查询中若使用了覆盖索引,则该索引和查询的select字段重叠。

explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;

key_len

表示索引中使用的字节数,可以通过key_len计算查询中使用的索引长度,在不损失精度的情况下长度越短越好。

explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;

ref

显示索引的哪一列被使用了,如果可能的话,是一个常数

explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;

rows

根据表的统计信息及索引使用情况,大致估算出找出所需记录需要读取的行数,此参数很重要,直接反应的sql找了多少数据,在完成目的的情况下越少越好

explain select * from emp;

extra

包含额外的信息。

--using filesort:说明mysql无法利用索引进行排序,只能利用排序算法进行排序,会消耗额外的位置
explain select * from emp order by sal;

--using temporary:建立临时表来保存中间结果,查询完成之后把临时表删除
explain select ename,count(*) from emp where deptno = 10 group by ename;

--using index:这个表示当前的查询时覆盖索引的,直接从索引中读取数据,而不用访问数据表。如果同时出现using where 表名索引被用来执行索引键值的查找,如果没有,表面索引被用来读取数据,而不是真的查找
explain select deptno,count(*) from emp group by deptno limit 10;

--using where:使用where进行条件过滤
explain select * from t_user where id = 1;

--using join buffer:使用连接缓存,情况没有模拟出来

--impossible where:where语句的结果总是false
explain select * from emp where empno = 7469;

索引优化

索引基本优化详细点

分析案例

预先准备好数据

SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `itdragon_order_list`;
CREATE TABLE `itdragon_order_list` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '主键id,默认自增长',
  `transaction_id` varchar(150) DEFAULT NULL COMMENT '交易号',
  `gross` double DEFAULT NULL COMMENT '毛收入(RMB)',
  `net` double DEFAULT NULL COMMENT '净收入(RMB)',
  `stock_id` int(11) DEFAULT NULL COMMENT '发货仓库',
  `order_status` int(11) DEFAULT NULL COMMENT '订单状态',
  `descript` varchar(255) DEFAULT NULL COMMENT '客服备注',
  `finance_descript` varchar(255) DEFAULT NULL COMMENT '财务备注',
  `create_type` varchar(100) DEFAULT NULL COMMENT '创建类型',
  `order_level` int(11) DEFAULT NULL COMMENT '订单级别',
  `input_user` varchar(20) DEFAULT NULL COMMENT '录入人',
  `input_date` varchar(20) DEFAULT NULL COMMENT '录入时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10003 DEFAULT CHARSET=utf8;

INSERT INTO itdragon_order_list VALUES ('10000', '81X97310V32236260E', '6.6', '6.13', '1', '10', 'ok', 'ok', 'auto', '1', 'itdragon', '2017-08-28 17:01:49');
INSERT INTO itdragon_order_list VALUES ('10001', '61525478BB371361Q', '18.88', '18.79', '1', '10', 'ok', 'ok', 'auto', '1', 'itdragon', '2017-08-18 17:01:50');
INSERT INTO itdragon_order_list VALUES ('10002', '5RT64180WE555861V', '20.18', '20.17', '1', '10', 'ok', 'ok', 'auto', '1', 'itdragon', '2017-09-08 17:01:49');

逐步开始进行优化:

第一个案例:

select * from itdragon_order_list where transaction_id = "81X97310V32236260E";
--通过查看执行计划发现type=all,需要进行全表扫描
explain select * from itdragon_order_list where transaction_id = "81X97310V32236260E";

--优化一、为transaction_id创建唯一索引
 create unique index idx_order_transaID on itdragon_order_list (transaction_id);
--当创建索引之后,唯一索引对应的type是const,通过索引一次就可以找到结果,普通索引对应的type是ref,表示非唯一性索引赛秒,找到值还要进行扫描,直到将索引文件扫描完为止,显而易见,const的性能要高于ref
 explain select * from itdragon_order_list where transaction_id = "81X97310V32236260E";
 
 --优化二、使用覆盖索引,查询的结果变成 transaction_id,当extra出现using index,表示使用了覆盖索引
 explain select transaction_id from itdragon_order_list where transaction_id = "81X97310V32236260E";

第二个案例

--创建复合索引
create index idx_order_levelDate on itdragon_order_list (order_level,input_date);

--创建索引之后发现跟没有创建索引一样,都是全表扫描,都是文件排序
explain select * from itdragon_order_list order by order_level,input_date;

--可以使用force index强制指定索引
explain select * from itdragon_order_list force index(idx_order_levelDate) order by order_level,input_date;
--其实给订单排序意义不大,给订单级别添加索引意义也不大,因此可以先确定order_level的值,然后再给input_date排序
explain select * from itdragon_order_list where order_level=3 order by input_date;

覆盖索引

1、当发起一个被索引覆盖的查询时,在explain的extra列可以看到using index的信息,此时就使用了覆盖索引

mysql> explain select store_id,film_id from inventory\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: inventory
   partitions: NULL
         type: index
possible_keys: NULL
          key: idx_store_id_film_id
      key_len: 3
          ref: NULL
         rows: 4581
     filtered: 100.00
        Extra: Using index
1 row in set, 1 warning (0.01 sec)

2、在大多数存储引擎中,覆盖索引只能覆盖那些只访问索引中部分列的查询。不过,可以进一步的进行优化,可以使用innodb的二级索引来覆盖查询。

例如:actor使用innodb存储引擎,并在last_name字段又二级索引,虽然该索引的列不包括主键actor_id,但也能够用于对actor_id做覆盖查询

mysql> explain select actor_id,last_name from actor where last_name='HOPPER'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: actor
   partitions: NULL
         type: ref
possible_keys: idx_actor_last_name
          key: idx_actor_last_name
      key_len: 137
          ref: const
         rows: 2
     filtered: 100.00
        Extra: Using index
1 row in set, 1 warning (0.00 sec)

前缀索引

有时候需要索引很长的字符串,这会让索引变的大且慢,通常情况下可以使用某个列开始的部分字符串,这样大大的节约索引空间,从而提高索引效率,但这会降低索引的选择性,索引的选择性是指不重复的索引值和数据表记录总数的比值,范围从1/#T到1之间。索引的选择性越高则查询效率越高,因为选择性更高的索引可以让mysql在查找的时候过滤掉更多的行。

一般情况下某个列前缀的选择性也是足够高的,足以满足查询的性能,但是对应BLOB,TEXT,VARCHAR类型的列,必须要使用前缀索引,因为mysql不允许索引这些列的完整长度,使用该方法的诀窍在于要选择足够长的前缀以保证较高的选择性,通过又不能太长。

案例演示:

--创建数据表
create table citydemo(city varchar(50) not null);
insert into citydemo(city) select city from city;

--重复执行5次下面的sql语句
insert into citydemo(city) select city from citydemo;

--更新城市表的名称
update citydemo set city=(select city from city order by rand() limit 1);

--查找最常见的城市列表,发现每个值都出现45-65次,
select count(*) as cnt,city from citydemo group by city order by cnt desc limit 10;

--查找最频繁出现的城市前缀,先从3个前缀字母开始,发现比原来出现的次数更多,可以分别截取多个字符查看城市出现的次数
select count(*) as cnt,left(city,3) as pref from citydemo group by pref order by cnt desc limit 10;
select count(*) as cnt,left(city,7) as pref from citydemo group by pref order by cnt desc limit 10;
--此时前缀的选择性接近于完整列的选择性

--还可以通过另外一种方式来计算完整列的选择性,可以看到当前缀长度到达7之后,再增加前缀长度,选择性提升的幅度已经很小了
select count(distinct left(city,3))/count(*) as sel3,
count(distinct left(city,4))/count(*) as sel4,
count(distinct left(city,5))/count(*) as sel5,
count(distinct left(city,6))/count(*) as sel6,
count(distinct left(city,7))/count(*) as sel7,
count(distinct left(city,8))/count(*) as sel8 
from citydemo;

--计算完成之后可以创建前缀索引
alter table citydemo add key(city(7));

--注意:前缀索引是一种能使索引更小更快的有效方法,但是也包含缺点:mysql无法使用前缀索引做order by 和 group by。 

事务的ACID

  • A:原子性(Atomicity)
  • C:一致性(consistency)
  • I:隔离性(isolation)
  • D:持久性(durability)

事务的隔离级别

start transaction 开启事务

commit 提交事务

rollback 回滚事务

  • 未提交读(read uncommitted)事务中的修改,即使没有提交,对其他事务也是可见的
  • 提交读(read committed)一个事务开始时,只能看见已经提交过的修改,也叫不可重复读
  • 可重复读(repeatable read)同一个事务中,多次读取是同样的记录结果是一样的,mysql默认的隔离级别
  • 可串行化(serializable)最高隔离级别,读取每一行都加锁,导致超时和锁争用(抢锁)问题,一般不推荐使用

概念

  • 脏读(dirty read):事务可以读取未提交的数据
  • 不可重复读:前后读取的数据不一致,事务A读取为5,事务B读取改为10,事务A再读为10,也叫虚读
  • 幻读:当事务A在读取某个范围时,事务B在该范围内进行了修改或删除的操作,导致事务A再读取的时候出现了幻行,实际中事务B已经把一条记录删除了,但事务A在读取的时候没有读到新的记录。
隔离级别 脏读可能性 不可重复读可能性 幻读可能性 加锁度
READ UNCOMMITTED Y Y Y N
READ COMMITTED N Y Y N
REPEATABLE READ N N Y N
SERIALIZABLE N N N Y

你可能感兴趣的:(数据库,mysql,数据库,sql)