MySQL调优

MySQL调优

根据高并发、高可用MySQL视频进行整理

建议关掉MySQL5.6、5.7自带的缓存

存储引擎

MyISAM

mysql5.5.5之前默认的存储引擎,插入数据快。空间利用率高。因为采用B+树结构和不支持事务。

查询效率要求非常高的可考虑

InnoDB

MySQL5.5.5之后默认存储引擎、最主流

支持事务、外键

支持崩溃修复、并发控制

Memory

数据在内存、速度快、不安全

临时表

Archive

数据压缩、空间利用率高

插入快

查询差、不支持索引(磁带)

归档

索引组织表

表按照主键顺序组织存放

InnoDB表均为索引组织表,数据被主键的索引组织起来

主键:非空、最先申明唯一索引

索引算法:B+树

主索引/聚簇索引:

索引和数据放在一起了

叶子节点直接存放数据

按照主键构造B+树

辅助索引

叶子节点不包含行数据

InnoDB逻辑存储结构

表空间(tablespace)

默认所有数据存在共享表空间

最好挡在独占表空间(idb文件)

段(segment)

数据段:叶子节点

索引段:非叶子节点

区(extent)

大小为1M,64个page(节点)

页(page)

InnoDB磁盘读写的最小逻辑单位,默认16kb

行(row)

Trx id

回滚指针

数据

变长列

  • 长度不固定的数据类型:varchar、varbinary、blob、text
  • 占用空间大于768byte的不变长类型:char
  • 变长编码下的char

行溢出

解决数据表某一字段存的数据太大,字段过长,退化为二叉树

将大的数据转移到blob页存,热表尽量不要存长字段

禁止将图片等大文件序列化成二进制数据存在数据库

行记录

Dynamic格式

索引注意事项

联合索引

可代替最左侧字段的单独索引

口头禅:带头大哥不能死,中间兄弟不能丢

字符串的前缀索引

如果字符串过长,可考虑使用前缀索引节约空间,如邮箱

如果前缀区分度太小,可考虑:

  • 倒序存储
  • 新建hash字段

例:alter table user add index index2(email(6)) 以邮箱前六位建索引

或alter bable 表名 add key (列名(n))

字符串like

使用模糊查询(like %关键字% 或 like %关键字)会使索引失效

使用左模糊(like 关键字%)可以使用索引

InnoDB约束数据方法

主键/唯一值

主键:唯一且非空

唯一索引:唯一

唯一约束插入性能开销大,慎用

外键

对数据正确性实现约束,使用少

默认值/非空

Default/NOT NULL

如果innodb_strict_mode开启了,设置非空生效,未开启不生效

触发器

插入修改时校验数据

干扰业务,使用少

视图View

视图算法:

merge:

将视图sql合并到主查询sql,用一个sql查询,性能更高

temptable

将视图当作一张临时表处理

无法使用merge的SQL

  • 聚集函数
  • distinct
  • group by
  • having
  • union、union all
  • 子查询

提高查询效率

覆盖索引

查询语句从执行到返回均使用同一索引,不用回表,如查询联合索引的字段

查看建表语句:show create table 表名;

查看表结构:desc +表名;

查看索引:show index from 表名; cardinality 基数,代表索引可能性

查看执行计划:explain + 查询语句;

执行计划中extra代表是否索引覆盖,possible_keys 代表使用的索引

索引优化

analyze table 重新统计索引信息,修复基数估计错误

force index 强制使用某条索引

优化count

count(非索引字段)效率低,需要逐条扫描,判断是否为空

count(索引字段)、count(主键)、count(1)都需要提交给server层判断是否为null

最佳办法:使用count(*)

在MyISAM中,count(*)能直接返回数据库中记录的数据表行数,在InnoDB中数据库不记录,但MySQL专门优化了count(*)直接返回索引树中数据个数!

优化order by

排序缓存:sort_bufffer

优化排序查询时间,适当增大sort_buffer_size,使中间表(where查出来的数据)尽可能放在内存中运行

加了order by,前面的where语句相当于查的中间表,是不能走索引的!

排序阈值:max_length_for_sort_data 大于阈值时,只生成排序字段+主键的中间表,然后回表查出所有数据

最高效:使用索引覆盖

当筛选字段和排序字段全在一条索引中可用

select film_id,title from film order by title;
--title字段加了索引

优化RAND()

rand()生成0-1的随机数

order by rand() 随机排序输出

下面语句存在性能问题:

select title,description from film order by rand() limit 1;

步骤:

1.创建一个临时表,临时表的字段为rand、title、description_
2.从表中取出一行,调用RAND(),将结果和数据放入临时表,以此类推
3.针对临时表,将rand字段+行位置(主键)放入sort buffer
4.对sort_buffer排序,取出第一个的行位置(主键),查询临时表

发现只查询一条数据,确创了两个临时表,进行了排序

优化:

临时方案:

select max(film_id), min(film_id) into @M,@N from film;
set @X = floor((@M - @N + 1) * rand() + @N);
select title, description from film where film_id >= @X limit 1;

业务方案:

查询数据表总数total
total范围内,随机选取一个数字r
执行以下SQL(从第r个开始选一个,分页方式):
select title, description from film limit r,1;

索引下推

在MySQL5.6及以后自带,不需要手动开启

指的是在查询辅助索引或联合索引时,查询第二个字段,不用再回表查询

SELECT * FROM inventory WHERE store_id in (1,2) and film_id=3;

其中,(store_id,film_id)为联合索引

extra:using index condition

松散索引扫描

SELECT film from inventor WHERE film_id = 3;

MySQL8.0新特性

可以不管带头大哥

索引失效

1.对索引字段做函数操作,优化器会放弃索引

如month()

2.字符串与数字比较

3.隐式字符编码转换,utf8会隐式转换为utf8mb4,不同编码表联表时,需要高级转低级

分页查询

原语句:

SELECT film_id,title,description FROM film ORDER BY title LIMIT 900,10;

虽然有title索引,但要查询description时回表前还要全表排序,相当于没走索引,性能下降

可以再加一个(title,description)的联合索引,如果不能加,使用下面优化方案

优化后:

SELECT f.film_id, f.title, f.description FROM film f
INNER JOIN (SELECT film_id FROM film ORDER BY title LIMIT 900,10) m 
ON f.film__id = m.film_id;

数据更新优化

日志

binlog

归档日志:server层产生的逻辑日志

进行数据复制和数据传送

数据闪回手段

redo log

重做日志:InnoDB产生的物理日志,保证持久化

数据页的变化、日志优先于数据

内存数据更新后写redo log,写入硬盘后删除

undo log

回滚日志:InnoDB产生的逻辑日志,保证隔离性、原子性

事务回滚和展示历史版本

数据缓存更新

执行更新时,顺序一般是undo log -> redo log -> binlog

因为一旦提交了binlog十五就相当于提交了,MySQL实行日志有限,日志刷盘,数据就不会丢,为了数据安全,一般吧redo log和binlog的刷盘设为1。

MySQL锁

粒度分:全局锁(整库无法修改)、表级锁、行锁

全局锁

Flush Table with read lock

整库只读

一般用来备库备份

表锁

lock tables 表名 read/write

使用少

元数据锁

元数据指表结构、字段、数据类型、索引等

事务访问一般要加上,MDL读锁

事务修改加MDL写锁

行锁

读锁/写锁=共享锁/排他锁=共享锁/独占锁=S锁/X锁

S锁:自己要读,不让被人写

X锁:自己要写,不让别人读写

S锁和S锁兼容外,其他均不兼容

事务

ACID

原子性【两阶段提交+undo log保证】、一致性【锁+两阶段提交】、隔离性【并发事务隔离,锁+undo log保证】、持久性【redo log】

隔离性有四种级别:读未提交、都提交、可重复读、串行化

格式:

开启:
> begin;
sql语句……
回滚:
> rollback;
提交:
> commit;
查询隔离级别:
> select @@tx_isolation;

读未提交

读写都不加锁、不隔离

很少采用

set session transaction isolation level read uncommitted;

读提交

read committed

Oracle默认级别

提交前会加X锁

可重复读

repeatable read

MySQL默认

读取本事务开始时的状态,写数据加X锁

必须两边事务都提交,再次开启事务才能看到改的数据

串行化

serializable

读加S锁,写加X锁

很少用

隔离级别问题

脏读

读到未提交的数据

不可重复读

两次查询数据不一样

幻影读

两次查询读到数据条目数不一致

MySQL在可重复读级别,通过Next-Key锁解决了幻读问题,即【行锁+间隙锁】

Next-Key Lock

行锁+间隙锁

目的:在可重复读的级别下部分预防幻读

一旦锁,将锁上 前一个间隙和当前行

主键等值查询只加行锁

非唯一索引等值查询间隙+行锁

非索引字段,锁全表

注意:当前读时,不要查询没有索引的项目

MySQL避免被迫刷脏

1.正确调配硬盘参数

使用fio工具查看服务器IOPS

innodb_io_capacity

2.合理配置脏页比例上限

innodb_max_dirty_pages_pct

3.控制“顺便刷脏”策略

innodb_flush_neighbors

SSD建议设为0,MySQL8.0已默认

查看影响性能的锁

查看长事务:information_schema库中innodb_trx表

查看锁:information_schema库中innodb_locks表

查看阻塞的事务:information_schema库中innodb_lock_waits表

MySQL8.0:

查看锁:performance_schema库中的data_locks表

查看锁等待:performance_schema库中的data_lock_waits表

查看MDL锁:performance_schema库中的metadata_locks表

没有必要最好不要开启事务

可以调整锁超时时间、死锁检测等参数

业务上尽量将加锁(更新)的操作后移,降低锁时间,先进行快照读【只加元数据锁,没有行锁】

ORM

Object Relational Mapping

对象和关系型数据库的映射

如Java的mybadis

架构层次

接口层

处理层

支撑层

连接层

数据备份

备份时状态

热备(Hot Backup):正常运行时备份

冷备(Cold Backup):数据库停掉后备份

温备(Warm Backup):数据库只读

备份工具

mysqldump:逻辑、热、全量备份

xtrabackup:物理、热、全量+增量备份

outfile

原生备份指令

查看导出文件安全目录:show variables like ‘%secure%’;

命令:

select * into outfile '/var……/导出文件名' from 表名
//数据以,分隔结尾
select * into outfile '/var……/导出文件名' fields terminated by ',' from 表名

不能还原,很少用

mysqldump

原理:select SQL_NO_CATCH from 表名;不用缓存

–single-transaction:包含事务,在RR级别下进行(InnoDB)

–lock-all-table:使用FTWRL锁全表(MyISAM)

–lock-tables:使用READ LOCAL锁当前库的表(MyISAM)

–all_databases:备份所有库

命令:

全库备份
mysqldump -uroot -p123 --databases 数据库名 --single-transaction > 文件名.sql;
mysqldump增量备份

采用mysqldump全量备份+binlog增量备份

–flush-logs:备份后切换binlog文件

–master-data = 2:记录切换后的binlog文件名

步骤一:mysqldump全备

mysqldump -uroot -p123 --databases 数据库名 --single-transaction --flush-logs --master-data = 2> 文件名.sql;

步骤二:切换增量备份

mysqladmin -uroot -p123 flush-logs

步骤三:还原

先还原全量备份的sql
source 文件名.sql;
再将binlog增量还原
mysqlbinlog MySQL-bin.000002...|mysql -uroot -p123

ibbackup

现名:MySQL Enterprise Backup ,InnoDB官方出品

收费,所以才有开源的XtraBackup

XtraBackup

物理备份,Percona公司开发

直接备份InnoDb底层数据文件

全量热备:备份idb文件+备份期间的redo log

1启动redo log监听线程,开始收集redo log
2拷贝ibd数据文件
3停止收集redo log
4加FTWRL锁拷贝元数据frm

增量热备:根据每个页的LSN号

XtraBackup 8.0 ->mysql 8.0

XtraBackup 2.4 ->mysql 5.1-5.7

时间非常快

全备命令:

数据备份:
innobackupex --user=root --password=123 存放文件夹/
数据还原(要停掉mysqld):
innobackupex --copy-back 存放文件夹/日期文件夹/

增量备份:

增量备份:
innobackupex --user=root --password=123 --incremental 存放文件夹/ --incremental-basedir='全备存放文件夹/日期文件夹/'
增量备份合并至全量备份
innobackupex --apply-log 全备存放文件夹/日期文件夹/ --incremental-dir=存放文件夹/新日期文件夹/
数据还原(要停掉mysqld):
innobackupex --copy-back 存放文件夹/日期文件夹/

mylvmbackup

利用逻辑卷管理器,直接备份磁盘数据

mydumper

实现多线程并发备份还原,速度更快

Zmanda Recovery Manager

图形化,备份恢复管理工具

集成多种备份工具

集成binlog日志分析功能

数据事故解决

sql审计工具:Inception

伪删表:删表前先把表加一个特殊后缀改名,观察业务是否影响,无误后使用脚本删除

三高

高并发、高可用、高性能

手段

复制

扩展

切换

高并发:通过复制和拓展,将数据分散至多节点

高性能:复制提升速度,拓展提升容量

高可用:节点间身份切换保证随时可用

复制

binlog->relay log

异步复制

半同步复制

组复制

主从复制(异步)步骤:

第一步,打开主从数据库服务器的binlog
vim /etc/my.cnf
进入配置:
log-bin=/var/lib/mysql/mysql-bin
server-id=123456
改完之后,重启mysqld(冷知识:mysql是MySQL客户端,mysqld才是MySQL数据库)
systemctl restart mysqld
第二步:主库上全局锁
mysql> FLUSH TABLES WITH READ LOCK;
mysql> SHOW MASTER STATUS \G;//查看主库写到哪个binlog文件了
第三步:备份主库所有数据
mysqldump -uroot -p123 --all-databases --master-data > dbdump.sql
第四步:使用scp等方式将备份文件传到从库
scp dbdump.sql [email protected]:/root/dbdump.sql
解掉主库全局读锁
mysql> UNLOCK TABLES;
第五步:从库执行SQL文件
mysql> source /root/dbdump.sql;
mysql> show slave status \G;//查看从库状态
如果从库已配置好了主从,先停掉,再重设主备
mysql> stop slave;
mysql> reset slave;
第六步:从库设置备份主库
mysql> change master to MASTER_HOST='主机IP',MASTER_USER='root',MATSER_LOG_FILE='mysql-bin.0000?',MASTER_LOG_POS=?;
mysql> start slave;

备库最好要加上只读

GTID进行主从复制

事务的全局唯一id:GTID

MySQL5.6开始引入

GTID=server_uuid:gno

即由节点的id+事务流水号组成

启用GTID:gtid_mode=on;enforce_gtid_consistency=on;

-- 主从都打开配置文件
vim /etc/my.cnf
-- 加两行
gtid_mode=on
enforce_gtid_consistency=on
-- 保存重启mysqld
-- 从库重新设置备份主库
mysql> stop slave;
mysql> change master to MASTER_HOST='主机IP',MASTER_USER='root',MASTER_POSSWORD='123',master_auto_position=1;
mysql> start slave;

binlog格式

statement格式:MySQL5.0之前,基于语句的复制

可能会出现如果两个库索引不同,导致同一条语句(如删除数据)执行结果不同

ROW格式:不记录sql原文,记录数据行的变化,占空间大,使用最广,基于行的复制

mixed格式:有数据风险的使用ROW,没风险的使用statement,

mysql> set binlog_format=ROW;

备库延迟处理

重放中继日志耗时

方案:

  1. 主备使用相同配置机器
  2. 备库关闭log实时刷盘
  3. 增加从库数量
  4. binlog传送到大数据系统
  5. 大事务一分多

并行复制

MySQL5.6开始按库并行

配置文件加上:slave-parallel-type = DATABASE

MySQL5.7按照事务组并行

binlog_group_commit_sync_delay:延迟多少微秒调用fsync,保存binlog

binlog_group_commit_sync_no_delay_count:累计多少次以后调用fsync

配置文件加上:slave-parallel-type = LOGICAL_CLOCK

MySQL5.7.22

多了个binlog-transaction-dependency-tracking参数:

commit_order:按事务组并行,和5.7一样

writeset:没有修改像同行的事务并行

writeset_session:同一线程先后执行的两个事务不能并行

如何判断备库追上

强制延时

seconds_behind_master = 0

对比binlog执行位点

对比GTID执行情况

主主复制架构

MySQL调优_第1张图片

配置步骤:

-- 如果已经配置好了主从复制,很简单,只需要再在主库设置备份从库即可
-- 主库操作
mysql> stop slave;
mysql> change master to MASTER_HOST='从库IP',MASTER_USER='root',MASTER_POSSWORD='123',master_auto_position=1;
mysql> start s

扩展

分区表

-- 根据时间范围将t表分成4张InnoDB表,但在server层和客户端看来,还是一张表
-- INDEX DIRECTORY、DATA DIRECTORY指定数据存放在哪个位置
CREATE TABLE t (
    ftime datetime NOT NULL,
    cint (11) DEFAULT NULL,
    KEY (ftime)
) ENGINE = InnoDB DEFAULT CHARSET = latin1
PARTITION BY RANGE (YEAR(ftime))(
    PARTITION p_2030
    VALUES
        LESS THAN (2030) ENGINE = InnoDB,
    	INDEX DIRECTORY ='/data1',
    	DATA DIRECTORY = '/data2',
        PARTITION p_2031
    VALUES
        LESS THAN (2031) ENGINE = InnoDB,
        PARTITION p_2032
    VALUES
        LESS THAN (2032) ENGINE = InnoDB,
        PARTITION p_others
    VALUES
        LESS THAN MAXVALUE ENGINE = InnoDB
);

分区方式:

  1. 范围分区
  2. hash分区
  3. List分区

缺点:一次访问需要打开所有的idb文件,共用MDL锁

分表

垂直分表:按照字段分

水平分表:按行分,类似分区表,但server层也分了

分库

垂直分库:数据表被分到不同库

水平分库:按行分表

优点:

  • 增加隔离性
  • 提升容量与并发性能

缺点:

  • 部分失效可能性成倍增加
  • 无法使用单点事务
  • 垂直切分后无法JOIN
  • 范围查询困难

针对缺点处理方式:

  • 业务特殊处理
  • 业务应用使用中间层
  • 使用分库分表中间件

分库分表中间件:dble

基于开源项目MyCat

schema:虚拟数据库(不同于传统的schema)

shardingTable :虚拟表(被拆分的表)

shardingNode :虚拟节点

dbGroup :实际的MySQL集群

database :实际的database

只支持水平拆分

安装
第一步:先装java
配置Java环境变量:cd /usr/lib/jvm/jre……
vim ~/.bashrc
export JAVA HOME-/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.el79.x8664
export PATHE$PATH:$JAVA HOME/bin
回到home文件夹 cd ~
第二步:下载dble
下载解压到var/lib下
cd var/lib/dble
cd conf
mv cluster_template.cnf cluster.cnf
mv bootstrap_template.cnf bootstrap.cnf
配置dble主配置文件
vim db_template.xml
更改url、user、pwd
mv db_template.xml db.xml
配置外部连接数据库用户密码
vim user_template.xml
mv user_template.xml user.xml
mv sharding_template.xml sharding.xml
第三步:启动dble
cd ..
bin/dble start
第四步:使用dble设置的用户名密码连接dble管理
mysql -h'127.0.0.1' -uman1 -P9066 -p654321
mysql> create database @@shardingnode='dn$1-6';
dble读写分离

db.xml里面

rwSplitMode=0:直接分发到主实例

rwSplitMode=1:读操作必须在所有从实例中均衡

rwSplitMode=2:读操作在所有实例中均衡

rwSplitMode-3:读操作尽量在所有从实例中均衡

MySQL调优_第2张图片

注意事项

查询语句尽可能带拆分字段

插入语句必须带有拆分字段

拆分字段尽量等值(最好不要用范围查询,缩减in子句值的数量)

减少表的搜索遍历(尽量少出现distinct、group by、order by)

减少结果集

跨节点连表(常join的表按照相同拆分规则,使用连表字段作为拆分字段)

切换

主从切换

可靠性优先策略
  1. 检查B库seconds behind master,不能过大
  2. A库只读readonly = true
  3. 检查B库seconds behind master=0
  4. B库关只读readonly = false
  5. B库停止复制A库, A库开始复制B库

有一段时间两个库均不可写

可用性优先策略
  1. 取消等待数据一致的过程
  2. A库只读、B库关只读
  3. B库停止复制A库, A库开始复制B库

但可能会造成数据不一致:B可能还没有写完中继日志,被强行变更为主库去写

使用keepalived漂移、代理等

自动切主从组件

keepalived

MySQL调优_第3张图片

MHA

Master High Availability

支持GTID

不能自动漂移VIP

MySQL调优_第4张图片

步骤:

  1. 从宕机的master抢救为传送的binlog
  2. 等待slave执行中继日志,追上master
  3. 从slave执行从master抢救出的binlog
  4. 提升一个slave成为新的master
  5. 其他slave连接新的master进行复制
安装MHA
第一步:先配置主从
第二步:从库装上MHA manager,主从库装上node
第三步: 设置所有机器免密登录
vim /etc/ssh/sshd_config 
修改两行:PermitRootLogin、PubkeyAuthentication
从库:
vim /etc/mha.cnf
免密检查:materha_check_ssh --conf=/etc/mha.cnf
主备复制检查:masterha_check_repl --conf=/etc/mha.cnf
MHA工作状态检查:masterha_check_status --conf=/etc/mha.cnf
第三步:启动MHA
从库启动
masterha_manager --conf=/etc/mha.cnf &
查看日志
tail -f /var/log/masterha/app1/app1.log
第四步:测试主库宕机
systemctl stop mysqld
自研高可用组件

代价高

方便维护

MHA+dble

也叫DRDS架构:分布式关系数据库服务

MySQL调优_第5张图片

让MHA Manager通知dble切换主库
MySQL调优_第6张图片

MySQL调优_第7张图片

MySQL调优_第8张图片

未来数据库发展

MySQL8.0新特性

其实就是MySQL5.8

窗口函数

SELECT
    *,
    rank() over (
        PARTITION BY customer_id
        ORDER BY
        	amount desc
    ) AS ranking
FROM payment;

隐藏索引

建索引时加上 invisible/visible

用来测试索引

降序索引

CREATE INDEX idx1 ON payment (payment date desc);

通用表表达式 CTE

CTE可看作一个临时试图

可以在复杂语句中反复使用中间结果

WITH
    cte1 AS (SELECT a, b FROM tablel),
    cte2 AS (SELECT C, d FROM table2) 
SELECT b, d
FROM cte1 JOIN cte2
WHERE cte1.a = cte2.c;

UTF-8

使用utf8mb4作为默认字符集

DDL事务

元数据操作可以回滚

事务分类

OLTP

在线事务交易/处理系统

并发量大

MySQL/PostgreSQL

OLAP

在线分析处理系统

语句复杂,数据量大

Hive/SparkSQL/GreenPlum

HTAP

混合事务/分析处理

PostgreSQL

单体数据库最强

  • 与MySQL类似的功能
  • 性能更好、更稳定
  • 代码质量更高
  • 有赶超MySQL的趋势

自带分库分表中间件:Postgres-XL(OLTP ) 、GreenPlum(OLAP)【高性能sql优化器GPORCA】

PolarDB

阿里研发

共享存储

将三种log简化为redo log

OceanBase

阿里

TiDB

国产公认最好的开源数据库

  • 一键水平扩容或者缩容
  • 金融级高可用
  • 实时HTAP
  • 云原生的分布式数据库
  • 兼容MySQL 5.7协议和MySQL生态

MySQL调优_第9张图片

CockroachDB

2015年启动,由谷歌前员工发起

完全开源

分布式shared-nothing架构

兼容Postgrest协议

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