AI智能
改变未来

MySQL之十—性能优化

优化不总是对一个单纯的环境进行!还很可能是一个复杂的已投产的系统。 优化手段本来就有很大的风险,只不过你没能力意识到和预见到! 任何的技术可以解决一个问题,但必然存在带来一个问题的风险! 对于优化来说解决问题而带来的问题控制在可接受的范围内才是有成果。 保持现状或出现更差的情况都是失败! 稳定性和业务可持续性通常比性能更重要! 优化不可避免涉及到变更,变更就有风险! 优化使性能变好,维持和变差是等概率事件! 优化不能只是数据库管理员担当风险,但会所有的人分享优化成果! 所以优化工作是由业务需要驱使的!!!

参与优化:

数据库管理员
业务部门代表
应用程序架构师
应用程序设计人员
应用程序开发人员
硬件及系统管理员
存储管理员数据库管理员
业务部门代表
应用程序架构师
应用程序设计人员
应用程序开发人员
硬件及系统管理员
存储管理员

优化方向

安全优化(业务持续性)
性能优化(业务高效性)

优化的范围及思路

优化范围:
存储、主机和操作系统:
主机架构稳定性
I/O规划及配置
Swap
OS内核参数
网络问题
应用程序:(Index,lock,session)
应用程序稳定性和性能
SQL语句性能
串行访问资源
性能欠佳会话管理
数据库优化:(内存、数据库设计、参数)
内存
数据库结构(物理&逻辑)
实例配置

优化效果和成本的评估

优化工具的使用

系统层面的

CPU

top

cpu使用情况的平均值:

CPU每个核心的分别使用的情况(按1):

程序是如何使用CPU的?

系统给每个程序分配CPU的时候,以时间来划分表的。

CPU有效工作时间?

计算: 程序运行,数据处理
控制: 少量的关于申请资源和释放资源等

CPU无效工作时间

等待 IO

CPU各项指标说明

  0.0 us
用户程序,在运行过程中,使用的CPU时间的占比。
我们希望的是越高越好,尽量控制在90%
0.0 sy
控制: 资源管理,内核的工作(系统调用)
sys高的原因:
bug ,中病毒了
锁的问题
99.9 id
CPU空间的时间占比
0.0 wa
CPU花在等待上的时间
wa高的原因:
锁
(raid,过度条带化)
索引
多cpu使用情况监控:
主要判断我们cpu多核心有没有被充分利用。
现象:单颗很忙,其他很闲,对于MySQL来讲,有可能是并发参数设定不合理导致的。

MEM

KiB Mem :  4028432 total,  3774464 free,   116240 used,   137728 buff/cache名称介绍total :总内存大小free  :空闲的used  :在使用的buff/cache :缓冲区 和 缓存

内存管理子系统:

slab Allocator
buddy system
程序=指令+数据
对于page cache来讲(OS buffer)
内存的可用空间的计算 free +buffer cache
内存回收(buffer)的方式:
写入磁盘
swap
对于数据库来讲:需要将swap屏蔽掉

swap

KiB Swap: 2097148 total, 2097148 free,    0 used. 3701464 avail Mem 
Linux 6操作系统,默认回收策略(buffer cache),不立即回收策略
内存使用达到100%-60%时候,40% 会使用swap
Linux 7操作系统
内存使用达到100%-30%(70%)时候,才会使用swap
cat /proc/sys/vm/swappiness30echo 0 >/proc/sys/vm/swappiness    的内容改成0(临时)vim /etc/sysctl.conf添加:vm.swappiness=0sysctl -p

iostat 命令

dd if=/dev/zero of=/tmp/bigfile bs=1M count=4096iostat -dm 1
现象说明
IO 高 cpu us 也高,属于正常现象
CPU us高 IO很低 ,MySQL 不在做增删改查,有可能是存储过程,函数,排序,分组,多表连接
Wait,SYS 高 , IO低:IO出问题了,锁等待过多的几率比较大.
IOPS:每秒磁盘最多能够发生的IO次数,这是个定值
频繁小事务,IOPS很高,达到阈值,可能IO吞吐量没超过IO最大吞吐量.无法新的IO了
存储规划有问题.

数据库优化工具

show statusshow variablesshow indexshow processlistshow slave statusshow engine innodb statusdesc /explainslowlog扩展类深度优化:pt系列mysqlslapsysbenchinformation_schemaperformance_schemasys

优化思路分解

硬件层面优化

硬件选配

真实的硬件(PC Server): DELL R系列 ,华为,浪潮,HP,联想
云产品:ECS、数据库RDS、DRDS
IBM 小型机 P6 570 595 P7 720 750 780 P8

CPU:I,E

CPU:I、E
OLTP
OLAP
IO密集型:线上系统,OLTP主要是IO密集型的业务,高并发
CPU密集型:数据分析数据处理,OLAP,cpu密集型的,需要CPU高计算能力(i系列,IBM power系列)
CPU密集型: I 系列的,主频很高,核心少
IO密集型: E系列(至强),主频相对低,核心数量多

内存:ECC

建议2-3倍cpu核心数量 (ECC)

磁盘选择

SATA-III SAS Fc SSD(sata) pci-e ssd Flash Nvme flash
主机 RAID卡的BBU(Battery Backup Unit)关闭

存储

##最优选择:Raid10
根据存储数据种类的不同,选择不同的存储设备
配置合理的RAID级别(raid5、raid10、热备盘)
Raid0 :条带化 ,性能高
Raid1 :镜像,安全
Raid5 :校验+条带化,安全较高+性能较高(读),写性能较低 (适合于读多写少)
Raid10:安全+性能都很高,最少四块盘,浪费一半的空间(高IO要求)

网卡

单卡单口 bonding  + 交换机堆叠

云服务器

ECS ,RDS,TDSQL,PolarxDB

关闭

NUMA

(是一种关于多个cpu如何访问内存的架构模型)

SMP

(是指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构。它是相对非对称多处理技术而言的、应用十分广泛的并行技术) NUMA(是一种关于多个cpu如何访问内存的架构模型)

bios

级别

在bios层面numa关闭时,无论os层面的numa是否打开,都不会影响性能。

# numactl --hardware
available: 1 nodes (0) #如果是2或多个nodes就说明numa没关掉

OS grub

级别

vi /boot/grub2/grub.cfg#/* Copyright 2010, Oracle. All rights reserved. */default=0timeout=5hiddenmenuforeground=000000background=ffffffsplashimage=(hd0,0)/boot/grub/oracle.xpm.gztitle Trying_C0D0_as_HD0root (hd0,0)kernel /boot/vmlinuz-2.6.18-128.1.16.0.1.el5 root=LABEL=DBSYS ro bootarea=dbsys rhgb quietlinux16 /vmlinuz-0-rescue-c7c0850619f74e35b874b9b844bd79b7root=UUID=14e7cbfb-31d3-4e6e-8527-f6c949493583ro biosdevname=0 net.ifnames=0 rhgb quietnuma=off         #关闭numa 查询度降低15-30%initrd /boot/initrd-2.6.18-128.1.16.0.1.el5.img

注:在os层numa关闭时,打开bios层的numa会影响性能,QPS会下降15-30%;

数据库级别:

法一:

mysql -S /data/3306/mysql.sockmysql> show variables like \'%numa%\';+------------------------+-------+| Variable_name          | Value |+------------------------+-------+| innodb_numa_interleave | OFF   |+------------------------+-------+

法二:

vi /etc/init.d/mysqld# Give extra arguments to mysqld with the my.cnf file. This script# may be overwritten at next upgrade.$bindir/mysqld_safe --datadir=\"$datadir\" --pid-file=\"$mysqld_pid_file_path\"$other_args >/dev/null &wait_for_pid created \"$!\" \"$mysqld_pid_file_path\"; return_value=$?将$bindir/mysqld_safe --datadir=\"$datadir\"这一行修改为:/usr/bin/numactl --interleave all $bindir/mysqld_safe --datadir=\"$datadir\" 
--pid-file=\"$mysqld_pid_file_path\" $other_args >/dev/null &wait_for_pid created \"$!\" \"$mysqld_pid_file_path\"; return_value=$?

阵列卡配置

raid10(推荐)
SSD或者PCI-E或者Flash
强制回写(Force WriteBack)
BBU 电池 : 如果没电会有较大性能影响、定期充放电,如果UPS、多路电源、发电机。可以关闭。
关闭预读
有可能的话开启Cache(如果UPS、多路电源、发电机。)
vi /etc/rc.localif test -f /sys/kernel/mm/transparent_hugepage/enabled; thenecho never > /sys/kernel/mm/transparent_hugepage/enabledfiif test -f /sys/kernel/mm/transparent_hugepage/defrag; thenecho never > /sys/kernel/mm/transparent_hugepage/defragfi[root@db01 ~]# chmod +x /etc/rc.local[root@db01 ~]# reboot[root@db01 ~]# cat /sys/kernel/mm/transparent_hugepage/enabledalways madvise [never][root@db01 ~]# cat  /sys/kernel/mm/transparent_hugepage/defragalways madvise [never]

网卡绑定

bonding技术,业务数据库服务器都要配置bonding继续。建议是主备模式。
交换机一定要堆叠。

存储多路径

使用独立存储设备的话,需要配置多路径。
linux 自带 : multipath
厂商提供 :

系统层面优化

更改文件句柄和进程数

内核优化

vim /etc/sysctl.confvm.swappiness = 5            也可设置为0   #swap分区值 尽可能调低,不要使用swap,防止oom的出现vm.dirty_ratio = 20                       #绝对的脏数据限制,内存里的脏数据百分比不能超过这个值。如果脏数据超过这个数量,
新的IO请求将会被阻挡。vm.dirty_background_ratio = 10 #内存可以填充“脏数据”的百分比net.ipv4.tcp_max_syn_backlog = 819200 #增大SYN队列的长度,容纳更多连接net.core.netdev_max_backlog = 400000 #该参数决定了,网络设备接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。net.core.somaxconn = 4096 #Linux kernel参数,表示socket监听的backlog(监听队列)上限net.ipv4.tcp_tw_reuse=1 #表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;net.ipv4.tcp_tw_recycle=0 #表示关闭TCP连接中TIME-WAIT sockets的快速回收.vim /etc/security/limits.conf #文件句柄root - nofile 63000 #最大文件句柄数

防火墙

禁用

selinux: /etc/sysconfig/selinux 更改SELINUX=disabled.

iptables如果不使用可以关闭。可是需要打开MySQL需要的端口号

文件系统优化

推荐使用

XFS

文件系统

MySQL

数据分区独立 ,例如挂载点为: /data

mount参数 defaults, noatime, nodiratime, nobarrier 如/etc/fstab:
/dev/sdb /data xfs
defaults #默认的参数
noatime,#显著提高文件系统的性能。
nodiratime,#一般不用设置
nobarrier 1 2 #可以保证文件系统在日志数据写入磁盘之后才写commit记录,但影响性能。重要数据应用慎用,有可能造成数据损坏。

不使用

LVM

(逻辑卷管理)

io

调度

SAS : deadline
SSD&PCI-E: noop 生命电梯
centos 7 默认是deadline
cat /sys/block/sda/queue/scheduler
#临时修改为deadline(centos6)
echo deadline >/sys/block/sda/queue/scheduler
vi /boot/grub/grub.conf
更改到如下内容:
kernel /boot/vmlinuz-2.6.18-8.el5 ro root=LABEL=/ elevator=deadline rhgb quiet

数据库版本选择

1.稳定版:选择开源的社区版的稳定版GA版本。

2.选择mysql数据库GA版本发布后6个月-12个月的GA双数版本,大约在15-20个小版本左右。 3.选择前后几个月没有大的BUG修复的版本,而不是大量修复BUG的集中版本。 4.要考虑开发人员开发程序使用的版本是否兼容你选的版本。 5.作为内部开发测试数据库环境,跑大概3-6个月的时间。 6.优先企业非核心业务采用新版本的数据库GA版本软件。

7.向DBA高手请教,或者在技术氛围好的群里和大家一起交流,使用真正的高手们用过的好用的GA版本产品。 8.最终建议: 8.0.20是一个不错的版本选择。向后可以选择双数版。

数据库三层结构及核心参数优化

连接层

max_connections=1000(3000max)#MySQL 服务所允许的同时会话数的上限,经常出现Too Many Connections的错误提示,则需要增大此值max_connect_errors=999999  # 每个客户端连接最大的错误允许数量,
当超过该次数,MYSQL服务器将禁止此主机的连接请求,直到MYSQL服务器重启或通过flush hosts命令清空此主机的相关信息wait_timeout=600 # 连接超时时间interactive_wait_timeout=3600 #交互式的连接超时时间net_read_timeout = 120 #网络连接读传输数据包net_write_timeout = 120 #网络连接写传输数据包max_allowed_packet= 32M #服务器端限制server接受的数据包大小back_log # 可以在堆栈中的连接数量

Server层

query_cache_size:           #查询缓存sql_safe_updates =1          #安全模式 使用where子句,并且where子句中列必须为prefix索引列或者limit语句 也可同时使用where语句和limitslow_query_log  =ON          #慢日志开启slow_query_log_file   =/data/3307/slow.log  #慢日志路径long_query_time =1                  # 慢查询时间,超过1秒为慢查询log_queries_not_using_indexes =ON   # 开启记录没有使用索引查询语句log_throttle_queries_not_using_indexes = 10 # #设定每分钟记录到日志的未使用索引的语句数目,超过这个数目后只记录语句数量和花费的总时间sort_buffer = 1M   #不走索引排序缓存区join_buffer = 1M   # 联合查询操作所能使用的缓冲区大小read_buffer = 1M   # MySQL 读入缓冲区大小read_rnd_buffer = 1M #MySQL 的随机读(查询操作)缓冲区大小tmp_table       = 16M  #临时表heap_table      = 16M  #是Oracle的默认数据表存储结构,数据行是按照“随机存取”的方式进行管理。max_execution_time = 28800  #一条语句花费在执行的时间长度lock_wait_timeout   = 60 默认一年 #触发锁等待60slower_case_table_names =1        #  不区分大小写thread_cache_size      =64     # 默认(8)控制mysql缓存客户服务线程的数量log_timestamps     =SYSTEM    #5.7版本新增时间戳所属时区参数,默认记录UTC时区的时间戳到慢查询日志,应修改为记录系统时区init_connect       =\"set names utf8\"    #客户端字符集event_scheduler    =OFF                 #开启定时器secure-file-priv   =/tmp                #指定导出目录binlog_expire_logs_seconds =2592000(必须调)     # binlog过期的时间sync_binlog =1                         # 双一标准log-bin =/data/3307/mysql-bin  #二进制日志路径log-bin-index=/data/3307/mysql-bin.index  #指定binlog文件的索引文件,这个文件管理了所有的binlog文件的目录max_binlog_size=500M #指定binlog日志文件的大小binlog_format =ROW    #确保使用DRS进行MySQL的增量迁移或同步时,不会导致任务失败甚至数据丢失max_binlog_cache_size=8M //表示的是binlog能够使用的最大cache内存大小binlog_cache_size=2M   //为每个session分配的内存,在事务过程中用来存储二进制日志的缓存,提高记录bin-log的效率。

存储引擎层

transaction-isolation=\"READ-COMMITTED\"  # *****RC级别innodb_data_home_dir  =/xxx              #目录innodb_log_group_home_dir=/xxx          #文件存放路径innodb_log_file_size =2048M  #确定数据日志文件的大小,更大的设置可以提高性能innodb_log_files_in_group=3   #为提高性能,MySQL可以以循环方式将日志文件写到多个文件innodb_flush_log_at_trx_commit=2 # *****  双一另一个标准gtid每次提交事务都会写日志,但并不会执行刷的操作。每秒定时会刷到日志文件。innodb_flush_method =O_DIRECT # *****  数据文件的写入操作是直接从mysql innodb buffer到磁盘的,并不用通过操作系统的缓冲,而真正的完成也是在flush这步,日志还是要经过OS缓冲。innodb_io_capacity =1000      # *****设定io ps并发能力 固态盘1000innodb_io_capacity_max =4000  #设定io ps并发最大能力innodb_buffer_pool_size=64G   # *****  指定大小的内存来缓冲数据和索引innodb_buffer_pool_instances=4 # *****  调整缓存池数量innodb_log_buffer_size =64M     # *****确定日志文件所用的内存大小,缓冲区更大能提高性能,对于较大的事务,可以增大缓存大小。innodb_max_dirty_pages_pc=85   # ***** innodb主线程刷新缓存池中的数据,使脏数据比例小于85%innodb_lock_wait_timeout =10   # *****   InnoDB事务在被回滚之前可以等待一个锁定的超时秒数 默认值是50秒innodb_open_files=63000        # *****  限制Innodb能打开的表的数据   默认300innodb_page_cleaners =4     #从缓冲池实例刷新脏页的页面清理线程数innodb_sort_buffer_size =64M  #创建InnoDB索引时用于指定对数据排序的排序缓冲区的大小。innodb_print_all_deadlocks =1   #死锁的打开innodb_rollback_on_timeout =ON  #回滚业务,回滚当前事务或者回滚当前语句innodb_deadlock_detect     =ON  #开启死锁自动检测机制innodb_write_io_threads = 4     #写线程 默认四个,负责数据块的写入innodb_read_io_threads = 4      #读线程  默认四个,负责数据块的读取innodb使用后台线程处理数据页上的读写 I/O(输入输出)请求

复制

relay_log  =/opt/log/mysql/blog/relay  #定义relay_log的位置和名称relay_log_index              =/opt/log/mysql/blog/relay.index       #定义relay_log的位置和名称;max_relay_log_size =500M  #最大的relay_log文件大小;relay_log_recovery =ON    #当slave从库宕机后,假如relay-log损坏了,导致一部分中继日志没有处理,则自动放弃所有未执行的relay-log,并且重新从master上获取日志,这样就保证了relay-log的完整性。rpl_semi_sync_master_enabled =ON       #打开半同步复制rpl_semi_sync_master_timeout =1000     #等待ack的超时时间rpl_semi_sync_master_trace_level =32   #日志打印参数rpl_semi_sync_master_wait_for_slave_count=1 #必须等待多少个slave的ack信息,才可以提交rpl_semi_sync_master_wait_no_slave=ON  #在没有slave时,是否选择等待。rpl_semi_sync_master_wait_point=AFTER_SYNC#控制等待ack的逻辑处于整个事务提交过程的哪个阶段,目前支持两种模式AFTER_SYNCAFTER_COMMIT提高主从一致性rpl_semi_sync_slave_enabled =ON      #slave半同步的开关rpl_semi_sync_slave_trace_level =32  #日志相关binlog_group_commit_sync_delay =1       #影响commit提交时间binlog_group_commit_sync_no_delay_count=1000 #影响commit提交时间gtid_mode                       =ON       #gtid开启enforce_gtid_consistency        =ON       #开启gtid事务skip-slave-start  =1          #防止复制随着mysql启动而自动启动read_only        =ON    #普通用户只读权限super_read_only  =ON    #root用户管理员权限log_slave_updates =ON   #只有开启log_slave_updates,从库binlog才会记录主库同步的操作日志。server_id =2330602      #服务器唯一IDreport_host=10.0.0.51report_port =3306slave_parallel_type =LOGICAL_CLOCK   #SQL线程事务slave_parallel_workers =4        #决定并发处理的线程数master_info_repository =TABLE     #避免relay.info更新不及时,SLAVE 重启后导致的主从复制出错relay_log_info_repository=TABLE   #决定slave同步的位置信息记录在哪里

其它

客户端配置: 
[mysql]
no-auto-rehash #扫描数据库下的所有文件,打开所有的文件

开发规范

字段规范

  • 每个表建议在

    30

    个字段以内。

  • 需要存储

    emoji

    字符的,则选择

    `utf8mb4

    字符集。

  • 机密数据,加密后存储。

  • 整型数据,默认加上

    UNSIGNED

    。

  • 存储

    IPV4

    地址建议用

    bigINT UNSIGNE`,查询时再利用`INET_ATON()、INET_NTOA()

    函数转换。

  • 如果遇到

    BLOB、TEXT

    大字段单独存储表或者附件形式存储。

  • 选择尽可能小的数据类型,用于节省磁盘和内存空间。

  • 存储浮点数,可以放大倍数存储。

  • 每个表必须有主键,

    INT/BIGINT

    并且自增做为主键,分布式架构使用

    sequence

    序列生成器保存。

  • 每个列使用

    not null

    ,或增加默认值。

SQL语句规范

  • 去掉不必要的括号 如:

    ((a AND b) AND c OR (((a AND b) AND (c AND d))))

    修改成

    (a AND b AND c) OR (a AND b AND c AND d)

  • 去掉重叠条件 如:

    (a<b AND b=c) AND a=5

    修改成

    b>5 AND b=c AND a=5

    如:

    (B>=5 AND B=5) OR (B=6 AND 5=5) OR (B=7 AND 5=6)

    修改成

    B=5 OR B=6

  • 避免使用

    not in`、`not exists` 、`<>

    、

    like %%

  • 多表连接,小表驱动大表

  • 减少临时表应用,优化

    order by

    、

    group by

    、

    union

    、

    distinct

    、

    join

    等

  • 减少语句查询范围,精确查询条件

  • 多条件,符合联合索引最左原则

  • 查询条件减少使用函数、拼接字符等条件、条件隐式转换

  • union all

    替代

    union

  • 减少

    having

    子句使用

  • 如非必须不使用

    for update

    语句

update

和

delete

,开启安全更新参数

减少

inset ... select

语句应用

使用

load

替代

insert

录入大数据

导入大量数据时,可以禁用索引、增大缓冲区、增大

redo

文件和

buffer

、关闭

autocommit

、

RC

级别可以提高效率

优化

limit

,最好业务逻辑中先获取主键

ID

,再基于

ID

进行查询

limit 5000000,10
limit 10 , 200

(跳过前10行)

DDL

执行前要审核(邮件审核需要备份)

多表连接语句执行前要看执行计划

索引优化

  • 非唯一索引按照“

    i

    或者is字段名称字段名称[_字段名]”进行命名。

  • 唯一索引按照“

    u

    字段名称字段名称[_字段名]”进行命名。

  • 索引名称使用小写。

  • 索引中的字段数不超过

    5

    个。

  • 唯一键由3个以下字段组成,并且字段都是整形时,使用唯一键作为主键。

  • 没有唯一键或者唯一键不符合5中的条件时,使用自增id作为主键。

  • 唯一键不和主键重复。

  • 索引选择度高的列作为联合索引最左条件

  • ORDER BY

    ,

    GROUP BY

    ,

    DISTINCT

    的字段需要添加在索引的后面。

  • 单张表的索引数量控制在5个以内,若单张表多个字段在查询需求上都要单独用到索引,需要经过

    DBA

    评估。 查询性能问题无法解决的,应从产品设计上进行重构。

  • 使用

    EXPLAIN

    判断

    SQL

    语句是否合理使用索引,尽量避免

    extra

    列出现:

    Using File Sort

    ,

    Using Temporary

    。

UPDATE

、

DELETE

语句需要根据

WHERE

条件添加索引。

对长度大于50的

VARCHAR

字段建立索引时,按需求恰当的使用前缀索引,或使用其他方法。

下面的表增加一列

url_crc32

,然后对url_crc32建立索引,减少索引字段的长度,提高效率。

CREATE TABLE all_url(ID INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,url VARCHAR(255) NOT NULL DEFAULT 0,url_crc32 INT UNSIGNED NOT NULL DEFAULT 0,index idx_url(url_crc32));

合理创建联合索引(避免冗余),

(a,b,c)

相当于

(a)

、

(a,b) (a,b,c)

。

  • 合理利用覆盖索引,减少回表。

  • 减少冗余索引和使用率较低的索引

mysql> select * from schema_unused_indexes;mysql> select * from schema_redundant_indexes\\G

锁优化

全局锁

Global Read lock

全局读锁。 加锁方法:

FTWRL

,

flush tables with read lock.

解锁方法:

unlock tables;

出现场景:

mysqldump --master-data
xtrabackup

(8.0之前早期版本)等备份时。 属于类型:

MDL(matedatalock)

层面锁 影响情况: 加锁期间,阻塞所有事务写入,阻塞所有已有事务

commit

。

MDL

,等待时间受

lock_wait_timeout=31536000

检测方法

UPDATE performance_schema.setup_instrumentsSET ENABLED = \'YES\', TIMED = \'YES\'WHERE NAME = \'wait/lock/metadata/sql/mdl\';mysql> select * from performance_schema.metadata_locks;mysql> select OBJECT_SCHEMA ,OBJECT_NAME ,LOCK_TYPE,LOCK_DURATION,LOCK_STATUS ,OWNER_THREAD_ID,OWNER_EVENT_ID from performance_schema.metadata_locks;mysql> show processlist;mysql> select * from sys.schema_table_lock_waits;

经典故障1:

5.7 xtrabackup/mysqldump

备份时数据库出现

hang

状态,所有查询都不能进行

session1:

模拟一个大的查询或事务

mysql> select id,sleep(100) from city where id<100 for update;

session2:

模拟备份时的

FTWRL

mysql> flush tables with read lock;

此时发现命令被阻塞

session3:

发起查询,发现被阻塞

mysql> select * from world.city where id=1 for update;

结论:备份时,一定要选择业务不繁忙期间,否则有可能会阻塞正常业务。

故障2:

5.7版本 innobackupex

备份全库,进程死了,

`mysql

里就是全库读锁,后边

insert

全阻塞了

show processlist---->  select * from performance_schema.metadata_locks;---> pending---->granted----> OWNER_THREAD_ID: 66---->  select * from threads  \\G----->processlist_Id---->  show processlist----->  kill processlist_Id

row lock wait

(行锁定等待)

介绍

record lock

、

gap

、

next lock

都是基于索引加锁,与事务隔离级别有关。

行锁监控及分析

  • 确认有没有锁等待:

show status like \'innodb_row_lock%\'select * from information_schema.innodb_trx;
  • 查询锁等待详细信息

select * from sys.innodb_lock_waits;   ----> blocking_pid(锁源的连接线程)
  • 通过连接线程找SQL线程

select * from performance_schema.threads;
  • 通过SQL线程找到 SQL语句

select * from performance_schema.events_statements_history;

优化方向

  • 优化索引

  • 减少事务的更新范围

  • RC

拆分语句: 例如:

update t1 set num=num+10 where k1 <100;  k1 是辅助索引,record lock gap next

改为:

select id from t1 where  k1 <100; ---> id: 20,30,50update t1 set num=num+10   where id in (20,30,50);

架构优化

高可用架构:

MHA+ProxySQL+GTID
MGR\\InnoDB Cluster
PXC

读写分离:

ProxySQL

、

`MySQL-router
NoSQL

:

Redis+sentinel,Redis Cluster
MongoDB RS/MongoDB SHARDING Cluster
ES

安全优化

使用普通

nologin

用户管理

MySQL

合理授权用户、密码复杂度及最小权限、系统表保证只有管理员用户可访问。 删除数据库匿名用户 锁定非活动用户

MySQL

尽量不暴露互联网,需要暴露互联网用户需要设置明确白名单、替换MySQL默认端口号、使用ssl连接 优化业务代码,防止

SQL

注入。

常用工具介绍

PT

(

percona-toolkits

)工具的应用:

pt工具安装

[root@master ~]# yum install -y percona-toolkit-3.1.0-2.el7.x86_64.rpm

常用工具使用介绍

pt-archiver

归档表 场景:

面试题: 亿级的大表,delete批量删除100w左右数据。 面试题: 定期按照时间范围,进行归档表。

重要参数

--limit 100     每次取100行数据用pt-archive处理 
--txn-size 100 设置100行为一个事务提交一次,
--where \'id<3000\' 设置操作条件
--progress 5000 每处理5000行输出一次处理信息
--statistics 输出执行过程及最后的操作统计。(只要不加上--quiet,默认情况下pt- archive都会输出执行过程的)
--charset=UTF8 指定字符集为UTF8—这个最后加上不然可能出现乱码。
--bulk-delete 批量删除source上的旧数据(例如每次1000行的批量删除操作)

注意: 需要归档表中至少有一个索引,做好是

`where

条件列有索引

案例:

归档到数据库

db01 [test]>create table test1 like t100w;pt-archiver --source h=10.0.0.51,D=test,t=t100w,u=oldguo,p=123--dest h=10.0.0.51,D=test,t=test1,u=oldguo,p=123--where \'id<10000\'--no-check-charset --no-delete --limit=1000 --commit-each --progress 1000 --statistics

清理数据

pt-archiver --source h=10.0.0.51,D=test,t=t100w,u=oldguo,p=123--where \'id<10000\' --purge --limit=1 --no-check-charset

把数据导出到外部文件,但是不删除源表里的数据

pt-archiver --source h=10.0.0.51,D=world,t=city,u=root,p=123 --where \'1=1\' --no-check-charset --no-delete --file=\"/tmp/archiver.dat\"pt-osc
  • 场景: 修改表结构、索引创建删除 不能加快速度,但能减少业务影响(锁)。 面试题 :

    pt-osc

    工作流程: 1、检查更改表是否有主键或唯一索引,是否有触发器 2、检查修改表的表结构,创建一个临时表,在新表上执行

    ALTER TABLE

    语句

create table bak like t1; 
alter table bak add telnum char(11) not null;

3、在源表上创建三个触发器分别对于

INSERT
UPDATE
DELETE

操作

create trigger 
a
b
c

从源表拷贝数据到临时表,在拷贝过程中,对源表的更新操作会写入到新建表中

将临时表和源表

rename

(需要元数据修改锁,需要短时间锁表)

删除源表和触发器,完成表结构的修改。

pt-osc

工具限制

源表必须有主键或唯一索引,如果没有工具将停止工作 如果线上的复制环境过滤器操作过于复杂,工具将无法工作 如果开启复制延迟检查,但主从延迟时,工具将暂停数据拷贝工作 如果开启主服务器负载检查,但主服务器负载较高时,工具将暂停操作 当表使用外键时,如果未使用

--alter-foreign-keys-method

参数,工具将无法执行 只支持

Innodb

存储引擎表,且要求服务器上有该表1倍以上的空闲空间。

pt-osc

之

alter

语句限制

不需要包含alter table关键字,可以包含多个修改操作,使用逗号分开,如\"drop clolumn c1, add column c2 int\"
不支持rename语句来对表进行重命名操作
不支持对索引进行重命名操作
如果删除外键,需要对外键名加下划线,如删除外键fk_uid, 修改语句为\"DROP FOREIGN KEY _fk_uid\"

pt-osc

之命令模板

--execute表示执行
--dry-run表示只进行模拟测试
表名只能使用参数t来设置,没有长参数
pt-online-schema-change \\--host=\"127.0.0.1\" \\--port=3358 \\--user=\"root\" \\--password=\"root@root\" \\--charset=\"utf8\" \\--max-lag=10 \\--check-salve-lag=\'xxx.xxx.xxx.xxx\' \\--recursion-method=\"hosts\" \\--check-interval=2 \\--database=\"testdb1\" \\t=\"tb001\" \\--alter=\"add column c4 int\" \\--execute

应用实战:

pt-online-schema-change --user=oldguo --password=123 --host=10.0.0.51 
--alter \"add column state int not null default 1\" D=test,t=t100w --print --executept-online-schema-change --user=oldguo --password=123 --host=10.0.0.51
--alter \"add index idx(num)\" D=test,t=t100w --print --execute

pt-table-checksum

场景:校验主从数据一致性

创建数据库

Create database pt CHARACTER SET utf8;

创建用户

checksum

并授权

create user  \'checksum\'@\'10.0.0.%\' identified with mysql_native_password by \'checksum\';GRANT ALL ON *.* TO \'checksum\'@\'10.0.0.%\' ;flush privileges;

参数:

--[no]check-replication-filters:是否检查复制的过滤器,默认是yes,建议启用不检查模式。--databases | -d:指定需要被检查的数据库,多个库之间可以用逗号分隔。--[no]check-binlog-format:是否检查binlog文件的格式,默认值yes。建议开启不检查。因为在默认的row格式下会出错。--replicate:把checksum的信息写入到指定表中。--replicate-check-only:只显示不同步信息pt-table-checksum --nocheck-replication-filters --no-check-binlog-format --replicate=pt.checksums --create-replicate-table --databases=test --tables=t1 h=10.0.0.51,u=checksum,p=checksum,P=3307#!/bin/bashdate >> /root/db/checksum.logpt-table-checksum --nocheck-binlog-format --nocheck-plan --nocheck-replication-filters --replicate=pt.checksums --set-vars innodb_lock_wait_timeout=120 --databases test --tables t1 -u\'checksum\' -p\'checksum\' -h\'10.0.0.51\' >> /tmp/checksum.logdate >> /root/db/checksum.log

pt-table-sync

主要参数介绍

--replicate :指定通过pt-table-checksum得到的表.--databases : 指定执行同步的数据库。--tables :指定执行同步的表,多个用逗号隔开。--sync-to-master :指定一个DSN,即从的IP,他会通过show processlist或show slave status 去自动的找主。h= :服务器地址,命令里有2个ip,第一次出现的是Master的地址,第2次是Slave的地址。u= :帐号。p= :密码。--print :打印,但不执行命令。--execute :执行命令。pt-table-sync --replicate=pt.checksums --databases test--tables t1 h=10.0.0.51,u=checksum,p=checksum,P=3307 h=10.0.0.52,u=checksum,p=checksum,P=3307 --printpt-table-sync --replicate=pt.checksums --databases test--tables t1 h=10.0.0.51,u=checksum,p=checksum,P=3307 h=10.0.0.52,u=checksum,p=checksum,P=3307 --execute

pt-show-grants

作用: 用户和权限信息迁移。

pt-show-grants -h10.0.0.51  -P3307  -uchecksum -pchecksum-- Grants dumped by pt-show-grants-- Dumped from server 10.0.0.51 via TCP/IP, MySQL 5.7.28-log at 2020-05-15 17:11:06-- Grants for \'checksum\'@\'10.0.0.%\'CREATE USER IF NOT EXISTS \'checksum\'@\'10.0.0.%\';ALTER USER \'checksum\'@\'10.0.0.%\' 
IDENTIFIED WITH \'mysql_native_password\' AS
\'*E5E390AF1BDF241B51D9C0DBBEA262CC9407A2DF\' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;GRANT ALL PRIVILEGES ON *.* TO \'checksum\'@\'10.0.0.%\';-- Grants for \'mysql.session\'@\'localhost\'CREATE USER IF NOT EXISTS \'mysql.session\'@\'localhost\';ALTER USER \'mysql.session\'@\'localhost\'
IDENTIFIED WITH \'mysql_native_password\'
AS \'*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE\' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT LOCK;GRANT SELECT ON `mysql`.`user` TO \'mysql.session\'@\'localhost\';GRANT SELECT ON `performance_schema`.* TO \'mysql.session\'@\'localhost\';GRANT SUPER ON *.* TO \'mysql.session\'@\'localhost\';-- Grants for \'mysql.sys\'@\'localhost\'CREATE USER IF NOT EXISTS \'mysql.sys\'@\'localhost\';ALTER USER \'mysql.sys\'@\'localhost\'
IDENTIFIED WITH \'mysql_native_password\' AS \'*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE\'
REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT LOCK;GRANT SELECT ON `sys`.`sys_config` TO \'mysql.sys\'@\'localhost\';GRANT TRIGGER ON `sys`.* TO \'mysql.sys\'@\'localhost\';GRANT USAGE ON *.* TO \'mysql.sys\'@\'localhost\';-- Grants for \'repl\'@\'10.0.0.%\'CREATE USER IF NOT EXISTS \'repl\'@\'10.0.0.%\';ALTER USER \'repl\'@\'10.0.0.%\' IDENTIFIED WITH \'mysql_native_password\'
AS \'*23AE809DDACAF96AF0FD78ED04B6A265E05AA257\' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;GRANT REPLICATION SLAVE ON *.* TO \'repl\'@\'10.0.0.%\';-- Grants for \'root\'@\'10.0.0.%\'CREATE USER IF NOT EXISTS \'root\'@\'10.0.0.%\';ALTER USER \'root\'@\'10.0.0.%\' IDENTIFIED WITH \'mysql_native_password\'
AS \'*23AE809DDACAF96AF0FD78ED04B6A265E05AA257\' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;GRANT ALL PRIVILEGES ON *.* TO \'root\'@\'10.0.0.%\';-- Grants for \'root\'@\'localhost\'CREATE USER IF NOT EXISTS \'root\'@\'localhost\';ALTER USER \'root\'@\'localhost\' IDENTIFIED WITH \'mysql_native_password\'
AS \'*23AE809DDACAF96AF0FD78ED04B6A265E05AA257\' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;GRANT ALL PRIVILEGES ON *.* TO \'root\'@\'localhost\' WITH GRANT OPTION;GRANT PROXY ON \'\'@\'\' TO \'root\'@\'localhost\' WITH GRANT OPTION;

mysql

死锁监测

pt-deadlock-logger h=\'127.0.0.1\' --user=root --password=123456

主键冲突检查

pt-duplicate-key-checker --database=world h=\'127.0.0.1\' --user=root --password=123456

pt-kill

语句

参数说明

--daemonize  放在后台以守护进程的形式运行;--interval  多久运行一次,单位可以是s,m,h,d等默认是s –不加这个默认是5秒--victims 默认是oldest,只杀最古老的查询。这是防止被查杀是不是真的长时间运行的查询,他们只是长期等待 这种种匹配按时间查询,杀死一个时间最高值。--all 杀掉所有满足的线程--kill-query      只杀掉连接执行的语句,但是线程不会被终止--print               打印满足条件的语句--busy-time 批次查询已运行的时间超过这个时间的线程;--idle-time 杀掉sleep 空闲了多少时间的连接线程,必须在--match-command sleep时才有效—也就是匹配使用 -- –match-command 匹配相关的语句。----ignore-command 忽略相关的匹配。 这两个搭配使用一定是ignore-commandd在前 match-command在后,--match-db cdelzone 匹配哪个库command有:Query、Sleep、Binlog Dump、Connect、Delayed insert、Execute、Fetch、Init DB、Kill、Prepare、Processlist、Quit、Reset stmt、Table Dump

—杀掉空闲链接sleep 5秒的 SQL 并把日志放到/home/pt-kill.log文件中

/usr/bin/pt-kill  --user=用户名 --password=密码--match-command Sleep  --idle-time 5--victim all --interval 5 --kill --daemonize -S /tmp/mysql.sock--pid=/tmp/ptkill.pid --print --log=/tmp/pt-kill.log &

—查询SELECT 超过1分钟路

/usr/bin/pt-kill --user=用户名 --password=密码--busy-time 60  --match-info \"SELECT|select\"--victim all --interval 5 --kill--daemonize -S  -S /tmp/mysql.sock--pid=/tmp/ptkill.pid --print --log=/tmp/pt-kill.log &

— Kill掉 select IFNULl.*语句开头的SQL

pt-kill --user=用户名 --password=密码 --victims all--busy-time=0 --match-info=\"select IFNULl.*\"--interval 1 -S /tmp/mysqld.sock --kill--daemonize --pid=/tmp/ptkill.pid--print --log=/tmp/pt-kill.log &

—-kill掉state Locked

/usr/bin/pt-kill --user=用户名 --password=密码--victims all --match-state=\'Locked\'--victim all --interval 5 --kill--daemonize -S /tmp/mysqld.sock--pid=/tmp/ptkill.pid --print --log=/tmp/pt-kill.log &

—kill掉 a库,web为10.0.0.11的链接

pt-kill  --user=用户名 --password=密码 
--victims all --match-db=\'a\'
--match-host=\'10.0.0.11\' --kill
--daemonize --interval 10 -S /tmp/mysqld.sock --pid=/tmp/ptkill.pid --print-log=/tmp/pt-kill.log &

—指定哪个用户kill

pt-kill   --user=用户名 --password=密码 
--victims all --match-user=\'root\'
--kill --daemonize --interval 10 -S /home/zb/data/my6006/socket/mysqld.sock
--pid=/tmp/ptkill.pid --print --log=/home/pt-kill.log &

—查询SELECT 超过1分钟路

pt-kill  --user=用户名 --password=密码 --busy-time 60--match-info \"SELECT|select\" --victim all--interval 5 --kill --daemonize -S /tmp/mysqld.sock--pid=/tmp/ptkill.pid --print --log=/tmp/pt-kill.log &

—-kill掉 command query | Execute

pt-kill --user=用户名 --password=密码 
--victims all --match-command= \"query|Execute\" --interval 5
--kill --daemonize -S /tmp/mysqld.sock --pid=/tmp/ptkill.pid --print --log=/home/pt-kill.log &

其他

pt-find ---找出几天之前建立的表
pt-slave-restart -----主从报错,跳过报错
pt-summary ---整个系统的的概述
pt-mysql-summary ---MySQL的表述,包括配置文件的描述
pt-duplicate-key-checker ---检查数据库重复索引

Max_connections

(1)简介
Mysql的最大连接数,如果服务器的并发请求量比较大,可以调高这个值,当然这是要建立在机器能够支撑的情况下,因为如果连接数越来越多,mysql会为每个连接提供缓冲区,就会开销的越多的内存,所以需要适当的调整该值,不能随便去提高设值。
(2)判断依据
show variables like \'max_connections\';+-----------------+-------+| Variable_name   | Value |+-----------------+-------+| max_connections | 151   |+-----------------+-------+show status like \'Max_used_connections\';+----------------------+-------+| Variable_name        | Value |+----------------------+-------+| Max_used_connections | 101   |+----------------------+-------+

​
(3)修改方式举例
vim /etc/my.cnf
Max_connections=1024
​
补充:
1.开启数据库时,我们可以临时设置一个比较大的测试值
2.观察show status like \'Max_used_connections\';变化
3.如果max_used_connections跟max_connections相同,
那么就是max_connections设置过低或者超过服务器的负载上限了,
低于10%则设置过大.

back_log

(1)简介
mysql能暂存的连接数量,当主要mysql线程在一个很短时间内得到非常多的连接请求时候它就会起作用,如果mysql的连接数据达到max_connections时候,新来的请求将会被存在堆栈中,等待某一连接释放资源,该推栈的数量及back_log,如果等待连接的数量超过back_log,将不被授予连接资源。
back_log值指出在mysql暂时停止回答新请求之前的短时间内有多少个请求可以被存在推栈中,只有如果期望在一个短时间内有很多连接的时候需要增加它
(2)判断依据
show full processlist
发现大量的待连接进程时,就需要加大back_log或者加大max_connections的值
(3)修改方式举例
vim /etc/my.cnf
back_log=1024

wait_timeout和interactive_timeout

(1)简介
wait_timeout:指的是mysql在关闭一个非交互的连接之前所要等待的秒数
interactive_timeout:指的是mysql在关闭一个交互的连接之前所需要等待的秒数,比如我们在终端上进行mysql管理,使用的即使交互的连接,这时候,如果没有操作的时间超过了interactive_time设置的时间就会自动的断开,默认的是28800,可调优为7200。
wait_timeout:如果设置太小,那么连接关闭的就很快,从而使一些持久的连接不起作用
(2)设置建议
如果设置太大,容易造成连接打开时间过长,在show processlist时候,能看到很多的连接 ,一般希望wait_timeout尽可能低
(3)修改方式举例
wait_timeout=60
interactive_timeout=1200
长连接的应用,为了不去反复的回收和分配资源,降低额外的开销。
一般我们会将wait_timeout设定比较小,interactive_timeout要和应用开发人员沟通长链接的应用是否很多。如果他需要长链接,那么这个值可以不需要调整。
另外还可以使用类外的参数弥补。

key_buffer_size

(1)简介
key_buffer_size指定索引缓冲区的大小,它决定索引处理的速度,尤其是索引读的速度
《1》此参数与myisam表的索引有关
《2》临时表的创建有关(多表链接、子查询中、union)
在有以上查询语句出现的时候,需要创建临时表,用完之后会被丢弃
临时表有两种创建方式:
内存中------->key_buffer_size
磁盘上------->ibdata1(5.6)
ibtmp1 (5.7)
(2)设置依据
通过key_read_requests和key_reads可以直到key_baffer_size设置是否合理。
mysql> show variables like \"key_buffer_size%\";+-----------------+---------+| Variable_name   | Value   |+-----------------+---------+| key_buffer_size | 8388608 |+-----------------+---------+1 row in set (0.00 sec)​mysql>mysql> show status like \"key_read%\";+-------------------+-------+| Variable_name     | Value |+-------------------+-------+| Key_read_requests | 10    || Key_reads         | 2     |+-------------------+-------+2 rows in set (0.00 sec)

​
mysql>
一共有10个索引读取请求,有2个请求在内存中没有找到直接从硬盘中读取索引
控制在 5%以内 。
注:key_buffer_size只对myisam表起作用,即使不使用myisam表,但是内部的临时磁盘表是myisam表,也要使用该值。
可以使用检查状态值created_tmp_disk_tables得知:
​
mysql> show status like \"created_tmp%\";
+-------------------------+-------+
| Variable_name | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 0 |
| Created_tmp_files | 6 |
| Created_tmp_tables | 1 |
+-------------------------+-------+
3 rows in set (0.00 sec)
mysql>
通常地,我们习惯以 Created_tmp_tables/(Created_tmp_disk_tables + Created_tmp_tables)
Created_tmp_disk_tables/(Created_tmp_disk_tables + Created_tmp_tables)
​
或者已各自的一个时段内的差额计算,来判断基于内存的临时表利用率。所以,我们会比较关注 Created_tmp_disk_tables 是否过多,从而认定当前服务器运行状况的优劣。
Created_tmp_disk_tables/(Created_tmp_disk_tables + Created_tmp_tables)
控制在5%-10%以内
看以下例子:
在调用mysqldump备份数据时,大概执行步骤如下:
180322 17:39:33       7 Connect     root@localhost on7 Query       /*!40100 SET @@SQL_MODE=\'\' */7 Init DB     guo7 Query       SHOW TABLES LIKE \'guo\'7 Query       LOCK TABLES `guo` READ /*!32311 LOCAL */7 Query       SET OPTION SQL_QUOTE_SHOW_CREATE=17 Query       show create table `guo`7 Query       show fields from `guo`7 Query       show table status like \'guo\'7 Query       SELECT /*!40001 SQL_NO_CACHE */ * FROM `guo`7 Query       UNLOCK TABLES7 Quit
​
其中,有一步是:show fields from `guo`。从slow query记录的执行计划中,可以知道它也产生了 Tmp_table_on_disk。
​
所以说,以上公式并不能真正反映到mysql里临时表的利用率,有些情况下产生的 Tmp_table_on_disk 我们完全不用担心,因此没必要过分关注 Created_tmp_disk_tables,但如果它的值大的离谱的话,那就好好查一下,你的服务器到底都在执行什么查询了。
(3)配置方法
key_buffer_size=64M

query_cache_size

(1)简介:
查询缓存简称QC,使用查询缓冲,mysql将查询结果存放在缓冲区中,今后对于同样的select语句(区分大小写),将直接从缓冲区中读取结果。
​
SQL层:
select * from t1 where name=:NAME;
select * from t1 where name=:NAME;
​
1、查询完结果之后,会对SQL语句进行hash运算,得出hash值,我们把他称之为SQL_ID
2、会将存储引擎返回的结果+SQL_ID存储到缓存中。
​
存储方式:
例子:select * from t1 where id=10; 100次
​
1、将select * from t1 where id=10; 进行hash运算计算出一串hash值,我们把它称之为“SQL_ID\"
2、将存储引擎返回上来的表的内容+SQLID存储到查询缓存中
​
使用方式:
1、一条SQL执行时,进行hash运算,得出SQLID,去找query cache
2、如果cache中有,则直接返回数据行,如果没有,就走原有的SQL执行流程
​
一个sql查询如果以select开头,那么mysql服务器将尝试对其使用查询缓存。
注:两个sql语句,只要想差哪怕是一个字符(列如大小写不一样;多一个空格等),那么这两个sql将使用不同的一个cache。
​
(2)判断依据
mysql> show status like \"%Qcache%\";+-------------------------+---------+| Variable_name           | Value   |+-------------------------+---------+| Qcache_free_blocks      | 1       || Qcache_free_memory      | 1031360 || Qcache_hits             | 0       || Qcache_inserts          | 0       || Qcache_lowmem_prunes    | 0       || Qcache_not_cached       | 2002    || Qcache_queries_in_cache | 0       || Qcache_total_blocks     | 1       |+-------------------------+---------+8 rows in set (0.00 sec)
​
---------------------状态说明--------------------
Qcache_free_blocks:缓存中相邻内存块的个数。
如果该值显示较大,则说明Query Cache 中的内存碎片较多了,FLUSH QUERY CACHE会对缓存中的碎片进行整理,从而得到一个空闲块。
注:当一个表被更新之后,和它相关的cache
blocks将被free。但是这个block依然可能存在队列中,除非是在队列的尾部。可以用FLUSH QUERY CACHE语句来清空free blocks
​
Qcache_free_memory:Query Cache 中目前剩余的内存大小。通过这个参数我们可以较为准确的观察出当前系统中的Query Cache 内存大小是否足够,是需要增加还是过多了。
​
Qcache_hits:表示有多少次命中缓存。我们主要可以通过该值来验证我们的查询缓存的效果。数字越大,缓存效果越理想。
​
Qcache_inserts:表示多少次未命中然后插入,意思是新来的SQL请求在缓存中未找到,不得不执行查询处理,执行查询处理后把结果insert到查询缓存中。这样的情况的次数越多,表示查询缓存应用到的比较少,效果也就不理想。当然系统刚启动后,查询缓存是空的,这很正常。
​
Qcache_lowmem_prunes:
多少条Query因为内存不足而被清除出QueryCache。通过“Qcache_lowmem_prunes”和“Qcache_free_memory”相互结合,能够更清楚的了解到我们系统中Query Cache 的内存大小是否真的足够,是否非常频繁的出现因为内存不足而有Query 被换出。这个数字最好长时间来看;如果这个数字在不断增长,就表示可能碎片非常严重,或者内存很少。(上面的free_blocks和free_memory可以告诉您属于哪种情况)
​
Qcache_not_cached:不适合进行缓存的查询的数量,通常是由于这些查询不是 SELECT 语句或者用了now()之类的函数。
​
Qcache_queries_in_cache:当前Query Cache 中cache 的Query 数量;
Qcache_total_blocks:当前Query Cache 中的block 数量;。
Qcache_hits / (Qcache_inserts+Qcache_not_cached+Qcache_hits)
90/ 10000 0 90
​
如果出现hits比例过低,其实就可以关闭查询缓存了。使用redis专门缓存数据库
​
Qcache_free_blocks 来判断碎片
Qcache_free_memory + Qcache_lowmem_prunes 来判断内存够不够
Qcache_hits 多少次命中 Qcache_hits / (Qcache_inserts+Qcache_not_cached+Qcache_hits)
​
(3)配置示例
mysql> show variables like \'%query_cache%\' ;
+------------------------------+---------+
| Variable_name | Value |
+------------------------------+---------+
| have_query_cache | YES |
| query_cache_limit | 1048576 |
| query_cache_min_res_unit | 4096 |
| query_cache_size | 1048576 |
| query_cache_type | OFF |
| query_cache_wlock_invalidate | OFF |
+------------------------------+---------+
6 rows in set (0.00 sec)
​
mysql>
-------------------配置说明-------------------------------
以上信息可以看出query_cache_type为off表示不缓存任何查询
​
各字段的解释:
query_cache_limit:超过此大小的查询将不缓存
query_cache_min_res_unit:缓存块的最小大小,query_cache_min_res_unit的配置是一柄”双刃剑”,默认是4KB,设置值大对大数据查询有好处,但如果你的查询都是小数据查询,就容易造成内存碎片和浪费。
query_cache_size:查询缓存大小 (注:QC存储的最小单位是1024byte,所以如果你设定了一个不是1024的倍数的值,这个值会被四舍五入到最接近当前值的等于1024的倍数的值。)
​
query_cache_type:缓存类型,决定缓存什么样的查询,注意这个值不能随便设置,必须设置为数字,可选项目以及说明如下:
如果设置为0,那么可以说,你的缓存根本就没有用,相当于禁用了。
如果设置为1,将会缓存所有的结果,除非你的select语句使用SQL_NO_CACHE禁用了查询缓存。
如果设置为2,则只缓存在select语句中通过SQL_CACHE指定需要缓存的查询。
​
修改/etc/my.cnf,配置完后的部分文件如下:
query_cache_size=128M
query_cache_type=1

max_connect_errors

max_connect_errors是一个mysql中与安全有关的计数器值,它负责阻止过多尝试失败的客户端以防止暴力破解密码等情况,当超过指定次数,mysql服务器将禁止host的连接请求,直到mysql服务器重启或通过flush hosts命令清空此host的相关信息 max_connect_errors的值与性能并无太大关系。
修改/etc/my.cnf文件,在[mysqld]下面添加如下内容
max_connect_errors=2000

sort_buffer_size

(1)简介:
每个需要进行排序的线程分配该大小的一个缓冲区。增加这值加速
ORDER BY
GROUP BY
distinct
union
​
(2)配置依据
Sort_Buffer_Size并不是越大越好,由于是connection级的参数,过大的设置+高并发可能会耗尽系统内存资源。
列如:500个连接将会消耗500*sort_buffer_size(2M)=1G内存
(3)配置方法
修改/etc/my.cnf文件,在[mysqld]下面添加如下:
sort_buffer_size=1M

max_allowed_packet

(1)简介:
mysql根据配置文件会限制,server接受的数据包大小。
(2)配置依据:
有时候大的插入和更新会受max_allowed_packet参数限制,导致写入或者更新失败,更大值是1GB,必须设置1024的倍数
(3)配置方法:
max_allowed_packet=32M

join_buffer_size

select a.name,b.name from a join b on a.id=b.id where xxxx
用于表间关联缓存的大小,和sort_buffer_size一样,该参数对应的分配内存也是每个连接独享。
尽量在SQL与方面进行优化,效果较为明显。
优化的方法:在on条件列加索引,至少应当是有MUL索引

thread_cache_size

(1)简介
服务器线程缓存,这个值表示可以重新利用保存在缓存中线程的数量,当断开连接时,那么客户端的线程将被放到缓存中以响应下一个客户而不是销毁(前提是缓存数未达上限),如果线程重新被请求,那么请求将从缓存中读取,如果缓存中是空的或者是新的请求,那么这个线程将被重新创建,如果有很多新的线程,增加这个值可以改善系统性能.
(2)配置依据
通过比较 Connections 和 Threads_created 状态的变量,可以看到这个变量的作用。
设置规则如下:1GB 内存配置为8,2GB配置为16,3GB配置为32,4GB或更高内存,可配置更大。
服务器处理此客户的线程将会缓存起来以响应下一个客户而不是销毁(前提是缓存数未达上限)
​
试图连接到MySQL(不管是否连接成功)的连接数
mysql>  show status like \'threads_%\';+-------------------+-------+| Variable_name     | Value |+-------------------+-------+| Threads_cached    | 8     || Threads_connected | 2     || Threads_created   | 4783  || Threads_running   | 1     |+-------------------+-------+4 rows in set (0.00 sec)

​
Threads_cached :代表当前此时此刻线程缓存中有多少空闲线程。
Threads_connected:代表当前已建立连接的数量,因为一个连接就需要一个线程,所以也可以看成当前被使用的线程数。
Threads_created:代表从最近一次服务启动,已创建线程的数量,如果发现Threads_created值过大的话,表明MySQL服务器一直在创建线程,这也是比较耗cpu SYS资源,可以适当增加配置文件中thread_cache_size值。
Threads_running :代表当前激活的(非睡眠状态)线程数。并不是代表正在使用的线程数,有时候连接已建立,但是连接处于sleep状态。
(3)配置方法:
thread_cache_size=32
​
整理:
Threads_created :一般在架构设计阶段,会设置一个测试值,做压力测试。
结合zabbix监控,看一段时间内此状态的变化。
如果在一段时间内,Threads_created趋于平稳,说明对应参数设定是OK。
如果一直陡峭的增长,或者出现大量峰值,那么继续增加此值的大小,在系统资源够用的情况下(内存)

innodb_buffer_pool_size

(1)简介
对于InnoDB表来说,innodb_buffer_pool_size的作用就相当于key_buffer_size对于MyISAM表的作用一样。
(2)配置依据:
InnoDB使用该参数指定大小的内存来缓冲数据和索引。
对于单独的MySQL数据库服务器,最大可以把该值设置成物理内存的80%,一般我们建议不要超过物理内存的70%。
(3)配置方法
innodb_buffer_pool_size=2048M

innodb_flush_log_at_trx_commit

(1)简介
主要控制了innodb将log buffer中的数据写入日志文件并flush磁盘的时间点,取值分别为0、1、2三个。
0,表示当事务提交时,不做日志写入操作,而是每秒钟将log buffer中的数据写入日志文件并flush磁盘一次;
1,
每次事务的提交都会引起redo日志文件写入、flush磁盘的操作,确保了事务的ACID;
2,每次事务提交引起写入日志文件的动作,但每秒钟完成一次flush磁盘操作。
​
(2)配置依据
实际测试发现,该值对插入数据的速度影响非常大,设置为2时插入10000条记录只需要2秒,设置为0时只需要1秒,而设置为1时则需要229秒。因此,MySQL手册也建议尽量将插入操作合并成一个事务,这样可以大幅提高速度。
根据MySQL官方文档,在允许丢失最近部分事务的危险的前提下,可以把该值设为0或2。
(3)配置方法
innodb_flush_log_at_trx_commit=1
双1标准中的一个1

innodb_thread_concurrency

(1)简介
此参数用来设置innodb线程的并发数量,默认值为0表示不限制。
(2)配置依据
在官方doc上,对于innodb_thread_concurrency的使用,也给出了一些建议,如下:
如果一个工作负载中,并发用户线程的数量小于64,建议设置innodb_thread_concurrency=0;
如果工作负载一直较为严重甚至偶尔达到顶峰,建议先设置innodb_thread_concurrency=128,
并通过不断的降低这个参数,96, 80, 64等等,直到发现能够提供最佳性能的线程数,
例如,假设系统通常有40到50个用户,但定期的数量增加至60,70,甚至200。你会发现,
性能在80个并发用户设置时表现稳定,如果高于这个数,性能反而下降。在这种情况下,
建议设置innodb_thread_concurrency参数为80,以避免影响性能。
如果你不希望InnoDB使用的虚拟CPU数量比用户线程使用的虚拟CPU更多(比如20个虚拟CPU),
建议通过设置innodb_thread_concurrency 参数为这个值(也可能更低,这取决于性能体现),
如果你的目标是将MySQL与其他应用隔离,你可以l考虑绑定mysqld进程到专有的虚拟CPU。
但是需 要注意的是,这种绑定,在myslqd进程一直不是很忙的情况下,可能会导致非最优的硬件使用率。在这种情况下,
你可能会设置mysqld进程绑定的虚拟 CPU,允许其他应用程序使用虚拟CPU的一部分或全部。
在某些情况下,最佳的innodb_thread_concurrency参数设置可以比虚拟CPU的数量小。
定期检测和分析系统,负载量、用户数或者工作环境的改变可能都需要对innodb_thread_concurrency参数的设置进行调整。
128 -----> top cpu
设置标准:
1.当前系统cpu使用情况,均不均匀
top
​
2.当前的连接数,有没有达到顶峰
show status like \'threads_%\';
show processlist;
(3)配置方法:
innodb_thread_concurrency=8
方法:
1. 看top ,观察每个cpu的各自的负载情况
2. 发现不平均,先设置参数为cpu个数,然后不断增加(一倍)这个数值
3. 一直观察top状态,直到达到比较均匀时,说明已经到位了.

innodb_log_buffer_size

此参数确定些日志文件所用的内存大小,以M为单位。缓冲区更大能提高性能,对于较大的事务,可以增大缓存大小。
innodb_log_buffer_size=128M
设定依据:
1.大事务: 存储过程调用 CALL
2.多事务

innodb_log_file_size = 100M

设置 ib_logfile0  ib_logfile1 
此参数确定数据日志文件的大小,以M为单位,更大的设置可以提高性能.
innodb_log_file_size = 100M

innodb_log_files_in_group = 3

为提高性能,MySQL可以以循环方式将日志文件写到多个文件。推荐设置为3

read_buffer_size = 1M

MySql读入缓冲区大小。对表进行顺序扫描的请求将分配一个读入缓冲区,MySql会为它分配一段内存缓冲区。如果对表的顺序扫描请求非常频繁,并且你认为频繁扫描进行得太慢,可以通过增加该变量值以及内存缓冲区大小提高其性能。和 sort_buffer_size一样,该参数对应的分配内存也是每个连接独享

read_rnd_buffer_size = 1M

MySql的随机读(查询操作)缓冲区大小。当按任意顺序读取行时(例如,按照排序顺序),将分配一个随机读缓存区。进行排序查询时,MySql会首先扫描一遍该缓冲,以避免磁盘搜索,提高查询速度,如果需要排序大量数据,可适当调高该值。但MySql会为每个客户连接发放该缓冲空间,所以应尽量适当设置该值,以避免内存开销过大。
注:顺序读是指根据索引的叶节点数据就能顺序地读取所需要的行数据。随机读是指一般需要根据辅助索引叶节点中的主键寻找实际行数据,而辅助索引和主键所在的数据段不同,因此访问方式是随机的。

bulk_insert_buffer_size = 8M

批量插入数据缓存大小,可以有效提高插入效率,默认为8M
tokuDB    perconamyrocksRocksDBTiDBMongoDB

binary log

log-bin=/data/mysql-binbinlog_cache_size = 2M //为每个session 分配的内存,在事务过程中用来存储二进制日志的缓存, 提高记录bin-log的效率。没有什么大事务,dml也不是很频繁的情况下可以设置小一点,
如果事务大而且多,dml操作也频繁,则可以适当的调大一点。前者建议是--1M,后者建议是:即 2--4Mmax_binlog_cache_size = 8M //表示的是binlog 能够使用的最大cache 内存大小max_binlog_size= 512M //指定binlog日志文件的大小,如果当前的日志大小达到max_binlog_size,还会自动创建新的二进制日志。你不能将该变量设置为大于1GB或小于4096字节。默认值是1GB。
在导入大容量的sql文件时,建议关闭sql_log_bin,否则硬盘扛不住,而且建议定期做删除。expire_logs_days = 7 //定义了mysql清除过期日志的时间。二进制日志自动删除的天数。默认值为0,表示“没有自动删除”。
log-bin=/data/mysql-binbinlog_format=rowsync_binlog=1
双1标准(基于安全的控制):
sync_binlog=1   什么时候刷新binlog到磁盘,每次事务commitinnodb_flush_log_at_trx_commit=1set sql_log_bin=0;show status like \'com_%\';

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » MySQL之十—性能优化