数据库的性能优化的一些思考
Posted hellohell
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据库的性能优化的一些思考相关的知识,希望对你有一定的参考价值。
1,服务器目前的架构 cpu 内存 io 网络
一主 -》 多从(14) 主服务器宕机 切换从服务器慢
监控指标 :qps和tps (慢sql占用cpu时间太长,每个sql只能是一个cpu执行,qps超高造成阻塞)
并发量和cpu使用率 (连接数被占满,cpu资源耗尽出现宕机)
磁盘IO
网卡IO
大表(千万行数据,文件10g数据,查询条件复杂)
查询慢容易产生慢sql,产生大量的磁盘IO,更改列会很慢,建立索引时间长,主从延迟大
ddl操作会长时间锁表,造成主从延迟,正常数据会被阻塞,大量的链接超市
分库分表:主键选择 跨分区数据查询和统计
数据归档:时间点的选择(历史数据的查询做单独的入口,分离冷热数据,历史数据的统计和计算数据做异步处理)
归档操作的方式选择
大事务(关系型数据库的特性,原子性操作,原子性 一致性 隔离性 持久性)
要么全部成功要么失败回滚,
事务操作前和操作后完整性不能被破坏,
事务未提交之前对事务的操作不可见(未提交读,已提交读(默认隔离级别),可重复读(mysql默认),可串行化)
事务执行完之后持久化到磁盘不会丢失
运行时间比较长,操作数据比较多的事务叫做大事务
锁定的数据太多,造成大量的阻塞和锁超时
回滚所需的时间比较长
执行时间比较长,主从同步延迟太大
避免一次处理太多数据(分批处理)
移除不必要的select操作
2,影响性能的几个方面
硬件
服务器系统
存储引擎(插件式的存储引擎选择)
myisam 不支持事务,表级锁
innodb 事务,行级锁 ACID特性
数据库配置参数
表结构的设计和sql语句
3,硬件的影响
cpu 频率和核数,
mysql单条sql无法使用多个cpu,qps:每秒处理sql数量,核数比处理能力重要
内存大小
内存的io大于磁盘很多 热数据 > 可用内存数据
myisam 把索引缓存在内存中,数据通过操作系统缓存
innodb 同时缓存 数据和索引到内存
读减少磁盘io,写减少磁盘io
磁盘IO和网络资源
存储大小,传输速度,访问时间,主轴转速,物理尺寸
RAID技术:磁盘冗余队列,小磁盘组成大磁盘,提供数据冗余保持数据完整性
RAID0 没有冗余和数据修复能力,多个磁盘串联在一起,并发写入,容量相加
RAID1 磁盘镜像,生成数据备份镜像,并发写同时备份,磁盘冗余,容量减少50%
RAID5 奇偶校验磁盘阵列,任何数据失效都可以从奇偶校验块中重建
READ10 分片镜像 RAID1+READ0
固态硬盘SSD, PCIE SSD 比机械盘有更好的随机读写的性能,更好的支持并发,更容易损坏
SSD SATA接口 直接替换传统磁盘,同样支持RAID技术
PCI-E SSD 无法使用SATA接口,需要独特的驱动配置,贵性能更好
适用于大量随机IO,解决单线程负载的IO瓶颈
网络存储NAS和SAN
SAN -》服务器-》SAN 通过光纤访问服务器
缓存 大量顺序读写 随机读写慢
NAS 使用网络连接,通过文件协议 NFS和SMB访问
网络设备 (延迟和带宽)
高性能高代开网络接口设备
多个网卡绑定增加可用性和带宽
网络隔离 内外网隔离 业务隔离
总结,64位架构运行在64位系统
并发高的场景cpu核数比频率重要,cpu密集型和复杂sql频率越高越好
选择主办能使用的最高内存,尽可能的大
IO子系统 PCIe-》SSD-〉Raid10-》磁盘-〉SAN
3,服务器的影响
mysql比较常见的运行到linux,window对大小写不敏感,强制使用小写
window FreeBSD Solaris Linux
centos 参数优化
/etc/sysctl.conf
net.core.somaxconn=65535
net.core.netdev_max_backlog=65535
net.ipv4.tcp_max_syn_backlog=65535
net.ipv4.tcp_fin_timeout=10
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_recycle=1
net.core.wmem_default=87380
net.core.wmem_max=16777216
net.core.rmem_default=87380
net.core.rmem_max=16777216
net.ipv4.tcp_keepalive_time=120
net.ipv4.tcp_keepalive_intvl=30
net.ipv4.tcp_keepalive_probes=3
kernel.shmmax=429497295
vm.swappiness=0
/etc/security/limit.conf
* soft nofile 65535
* hard nofile 65535
/sys/block/devname/queue/scheduler
磁盘调度策略
文件系统
windows (FAT NTFS)
linux(EXT3 EXT4 XFS)
4,mysql架构
插件式存储引擎,数据处理和存储相分离
cs结构
客户端(java php c ODBC JDBC),负责连接处理,授权认证,安全等,一个连接智能化用到一个cpu,所有的操作都是在一个线程操作
服务层 连接管理()->查询缓存-》查询解析-〉查询优化 所有跨存储引擎的功能都在这一层
存储引擎层 针对于表的,不这对库文件
myisam: mysql5.5只前的默认存储引擎,
系统表,临时表(排序分组操作,数量超过一定大小,由查询优化器建立临时表)使用的存储引擎。
MYD和MYI两个文件存储数据,frm存储表结构。
表级别锁,读写混合的并发性不好;
表修复check table tablename;repair table tablename;
全文索引(mysql5.7之前唯一原生支持全文索引的引擎),对text和blog的前500字符的前缀索引
支持表数据压缩(myisampack),压缩表的只读表不能插入和更新
mysql5.0之前单表最大4G(MAX_Rows AVG_ROW)LENGTH),mysql5.0之后最大256TB
使用场景:非事务应用,只读类型的并发性好,空间类应用(空间函数 5.0之前的必选)
Innodb:mysql默认存储引擎,表空间存储数据,(innodb_file_per_table: on 独立表空间 .ibd , off系统表空间 ibdataX)
.frm和表空间文件
系统表空间无法简单收缩文件大小造成磁盘碎片,独立表空间使用optimize table命令重建表空间收缩文件系统
系统表空间产生IO瓶颈,独立表空间可以同时刷新多个文件数据,mysql5.6之后默认独立表空间
frm存储的是服务器层的数据字典跟引擎无关的,系统表空间存放引擎相关的数据字典,回滚段
事务型存储引擎ACID,redolog和undolog实现;行级锁,在存储引擎层实现,服务层不可见
锁(管理共享资源的并发访问,实现事务隔离),共享锁(读锁),独占锁(写锁)
阻塞:等待其他的锁释放资源,慢查询产生大量的阻塞
死锁:事务相互占用资源,系统自动识别死锁,并释放占用资源较少的事务,使得事务继续运行
show engine innodb status ;//状态检测
除了5.7之前的空间应用和全文索引情况都要默认innodb引擎
CSV存储引擎 存储文件.csv文件,一文件方式存储,myisam和innodb是二进制文件存储的
.frm表结构 .csv存储表数据内容 .csm存储表的元数据 数据量 表状态等
所有的列不能是null,不支持索引,不适合做发表和在线处理数据,可以直接对数据进行编辑
适合做数据交换中间表
Archive 以zlib对表数据进行压缩,占用更少的磁盘IO和存储空间,数据问价 .frm和.arz文件
只支持insert和select操作,行级锁,专用的缓冲区,支持高并发插入,只支持自增ID建立索引
适合 日志和数据采集的应用
memory引擎 数据存储在内存中,表结构保持在磁盘,也叫HEAP引擎
支持HASH(默认)和BTREE索引,等于查询时hash索引快,btree索引做范围查找快
所有字段都是固定长度 varchar(10)=char(10)
不支持blog和text字段类型
表级锁,并发性能有影响,max_heap_table_size决定最大表数据
系统自动创建: tmp-table-size 或 max-heap-table-size
1,超过限制使用myisam临时表 2,未超过现在使用memory临时表
主动创建: create temporary table
适用于映射表查找表,保存分析数据的中间表 缓存周期性聚合结果表
federated引擎 提供远程访问mysql服务器上表的功能,本地不存储数据,本地保存表结构和远程服务器的连接信息
默认mysql禁止使用,性能不好。
设置federated=1开启;
适用于 手动统计分析查询
存储引擎选择,没有特殊需求innodb是最佳选择
事务支持:innodb最稳定,select和insert 选myisam insert选archive
备份:innodb有在线热备份方案,mysqldump不是热备份(会加锁的逻辑备份)
崩溃恢复 innodb比myisam好很多
特殊引擎特性需求
mysql服务器配置
mysql配置文件 my.conf
参数作用域 全局set global 参数=值
会话 set session 参数=值
内存相关参数:可使用内存上限(单进程运行)
每个sql连接的使用内存
(sort_buffer_size 需要排序的sql会分配这部分内存)
(join_buffer_size 每个线程使用的连接缓冲区,每个连表分配一个)
(read_buffer_size 读缓冲区 全表扫描myisam时分配,4k的倍数)
(read_rnd_buffer_size 索引缓冲区)
缓存池分配的内存
innodb_buffer_poll_size 缓存索引和数据,延迟写入(合并多个写入一起写入磁盘)
总内存 - 单个线程内存*连接数 -系统保留内存 建议服务器内存的75%
key_buffer_size 主要是myisam用,缓存索引,数据依赖操作系统缓存
IO相关的配置参数:性能 安全 做平衡 贵啊
innodb:innodb_log_file_size * innodb_log_files_in_group = 事务日志总大小
innodb_log_buffer_size 事务日志缓冲区秒级持久化
innodb_flush_log_at_trx_commit 0秒级写入chach并flush到磁盘
1每次事务提交都写入cache并flush到磁盘
2每次都写入cache,每秒flush到磁盘
innodb_flush_method=O_DIRECT 操作系统不缓存数据
innodb_file_per_table =1 innodb的表空间
innodb_foublewrite = 1 双写缓存,避免叶没写完整就写到文件中
myisam:delay_key_write OFF ON ALL
安全相关配置:
expire_log_days 指定binlog自动清理的天数
max_allowed_packet 控制mysql可接收的包的大小
skip_name_resolve 禁用DNS查找
syadate_is_now 确保sysdate()返回确定日期
read_only 禁止super全县用户的写权限
skip_slave_start 禁止slave自动恢复
sql_mode 设置mysql使用的sql模式
(strict_trans_tables, no_engine_subtitution,no_zero_date,no_zero_in_date,only_full_group_by)
其他配置的影响
sysc_binlog 默认为0 ,操作系统决定什么时候刷新binlog;1,每次事务都会有写binlog操作
tmp_table_size 和 max_heap_table_size 内存临时表的大小,超过就会生产磁盘临时表
max_connections 控制最大连接数
结构设计和sql优化
表的列太多,由于插件式引擎设计,mysql的服务层和引擎层是分离的,在引擎层的api和服务器层需要缓存格式来拷贝数据,然后在服务器层解析转换成各个列。
关联太多的表 10个是极限
OLTP环境不恰当使用分区表
外键的使用 保证数据完整性,效率低,修改和备份时候,要检查约束,额外的锁的开销
总结:数据库结构设计 和 sql优化 > 数据库的存储引擎和参数配置 > 系统参数的优化 > 硬件的升级
5,mysql性能测试
基准测试 直接简单,易于比较,评估服务器处理能力,不涉及逻辑处理
压力测试 正式业务数据进行测试,获得真实的业务系统的压力
指标:TPS QPS 响应时间(最大 最小平均 响应时间 各个时间百分比) 并发请求数量(同时操作的数量 不是同时存在的进程)
收集信息:cpu使用率 IO 网络流量 状态 计数器信息等
工具:ab,mysqlslap(mysql5.1之后自带),sysbench ,Super Smack
6,表结构设计对性能的影响
良好的数据库逻辑设计和物理设计是数据库良好性能的jichu
目标:减少数据的冗余,避免数据的依赖维护异常,节约数据的存储空间,提高查询效率
步骤:需求分析(存储需求,处理需求,安全性和完整性)
逻辑设计(设计数据的逻辑存储结构,数据实体之间的逻辑关系,解决数据冗余,维护数据异常)
物理设计(表结构的设计)
维护优化(根据实际业务情况进行索引,存储结构的优化)
表逻辑设计 范式+反范式结合 = 高性能
范式:...... 减少冗余,体积小,更新快
查询关联表多,查询效率低;索引优化更加困难
反范式化: 空间换时间,更加方便查询条件的设计,提高查询效率,减少关联表,更好的索引优化(覆盖索引等)
物理设计
命名规范:库,表,字段的命名规范,不能用大小写区分,可读性,表意义的表达,完整单词
存储引擎:没特殊要求就用innodb,上边写过各个引擎的使用场景
表字段:合适的数据类型
数字》日期〉二进制》字符 (字符的比较排序等是根据字符集排序规则相关的,数字二进制日期等不用二进制大小可以直接比较)
同时占用空间越小越好(数据处理以页为单位的 innodb是16k,列越小每页的数据量就越多)
tinnyint 1字节 -128,128 0,255
smallint 2字节
mediuint 3字节
int 4字节
bigint 8字节
float 4字节
double 8个字节
decimal 每4字节存储9个数字,小数点占一个字节
varchar 变长字符串,只占用必要的存储空间
额外的字节存储字符串长度,列的长度超过255需要两个字节存储字符串长度,最大就是65535长度,所以不可能用3个字节
使用最小符合需求的长度(改变长度会锁表,5.7之后不超过255不会锁表)
varchar(5)和varchar(200)性能比较,mysql在加载数据到内存中使用的是固定宽度,缓存和内存临时表的时候消耗的内存比较多
适合存储长度比较分散的数据,很少被更新的数据(字符长度变化,可能引起存储页的分裂,产生磁盘碎片),多字节字符集
char 固定宽度字符串,末尾的空格会被过滤删除,最大宽度255
适合存储长度比较集中,长度短小的字符串,经常被更新的字符列
日期类型 datetime类型 以yyyy-mm-dd hh:mm:ss 存储,与时区无关,占用8个字节存储空间
date 和 time 类型
timestamp类型 时间戳,显示格式yyyy-mm-dd hh:mm:ss,只占用4个字节,依赖时区,自动修改更新
存储空间比字符串少,日期可以对比,日期函数方便计算;不要用int来代替timestamp存储时间,并没有什么好处
7,mysql架构设计
主从复制:分担mysql的读负载,高可用,灾难恢复备份,通过二进制日志同步数据,异步处理,有延迟
二进制日志增量进行复制,不需要太多带宽,基于行的复制在大批量修改时会带来带宽压力
mysql的二进制日志,慢查询日志,通用日志属于服务层日志,innodb的重做日志和回滚日志属于引擎层日志
二进制日志:记录了mysql数据库的修改事件,增删改事件和表结构变化
binlog_format=STATEMENT 基于段的日志格式,记录sql和上下文信息,日志量少,节约磁盘和网络io
但是使用非确定性函数,导致上下文信息无法确定的情况时无法复制的、
binlog_format=ROW 基于行的日志格式 默认格式 官方推荐,记录每行数据的修改
更加安全的复制,复制的效率高,降低主从延迟时间
日志的量比较大,binlog_row_image = FULL | MINIMAL | NOBLOB
binlog_format=MIXED 混合行和段的日志格式 系统根据sql内容一定的算法确定
主从复制过程:
1,开启主服务器二进制日志,主服务器将写入更新写入二进制日志
2,从服务器读取主服务器二进制增量写入到relay_log中,io线程,转储线程
3,从服务器重新执行relay_log的内容
基于日志的复制建立过程:初始化从库数据(mysqldump 等工具)-》启动复制(指定 master 用户 密码 二进制文件 偏移量)
基于GTID的复制:GTID全局事务ID,每个主库上提交的事务生成唯一ID,从服务器会告诉主服务器已执行的事务的GTID值,主库会告诉从哪些GTID事务没有被执行。
故障处理麻烦,没有skip冲突的操作
GTID = source_id:transsction_id
mysql5.7之后支持多主多从
一主多从 mysql5.7之前一直支持
配置简单,
多个从库分担读负载,分担主库的读负载
不同的业务使用不同的从库,不同的业务使用不同的索引
双主复制 互为主从
容易产生冲突,尽量分开每个库操作的表
主备复制 高可用方案
一台mysql只读,做热备份,主库故障才会切换成主服务器
一台mysql做主服务器,切换后作为备份服务器的从库
主从延迟 主从延迟不可避免
主库写入二进制的时间(大事务),控制事务的大小,分割成小事务
二进制的传输时间(日志量,磁盘IO,网络带宽),设置给予MIXED的日志格式
从库重放sql,单线程串行处理重放sql(主库的并发写入从库变成串行),mysql5.6后支持单库的多线程复制
mysql5,7支持逻辑时钟的多线程复制
主从复制中断
数据损坏丢失,主从的二进制日志损坏
从库数据被修改,造成数据冲突
不唯一的server_id,server_uuid
高可用 High Availability 缩短故障和系统维护的停机时间,提高可用性
服务器磁盘空间耗尽,性能糟糕的sql,表结构索引没有优化,人为操作失误,主从故障等
建立完整的监控系统,备份数据的恢复测试,及时归档和清理不需要的数据
增加系统的冗余,避免单点故障,主从切换故障转移
单点故障
利用SUN共享存储或DRDB磁盘镜像解决mysql单点故障
多写集群(pxc)或NDB集群解决单点故障
主从复制来解决单点故障
MMM(perl):监控管理mysql主主复制的拓扑,切换主备服务器的切换和故障转移,提供读写虚拟ip
同一时间只有一台主mysql对外服务,其他的事只读模式
监控主从中断,延迟过大时,进程故障转移
MHA(perl) : 监控主从复制的拓扑,切换主从服务器完成故障转移
监控主服务器是否可用,不可以就从众多的从服务器选举出一个做主服务器
读写分离 负载均衡
写操作在主服务器进行,读操作在多个从库进行负载均衡
程序实现读写分离,更灵活控制连接主从数据库,直接连接效率高,但是增加工作量程序复杂,人为控制容易出错
读写中间件实现 mysql-proxy maxScale 根据语法分析解析出时读操作还是写操作,存储过程等直接算主操作,多程序透明,节约开发成本;但是增加了中间层,对查询效率损耗很大,对延迟敏感的业务无法在主库执行
负载均衡:程序轮循,软件(LVS Haproxy MaxScale) ,硬件F5等
8,索引查询的优化
索引作用是快速查找到需要的数据(太多,太少的索引都会损耗性能),mysql在存储引擎层实现的索引,不同的存储引擎索引的使用是不一样的;减少存储引擎扫描的数据量(innodb的每次io以页为单位默认16k),帮助排序避免临时表的产生,把随机i 哦转变为顺序io。
同时,索引会增加更新插入成本,要维护索引和统计信息,innodb引入了插入缓存把多次插入操作合并操作;同时mysql的查询优化器会分析索引选择合适的索引,太多的索引也会增加查询优化器的时间。
b-tree索引:以B+树的结构存储数据,是一个平衡查找树。能加快查找数据的速度,更加合适范围查找
每个叶子到根的距离是相同的,每个节点按照键值的大小顺序存放
全值匹配查询,匹配最左前缀查询,匹配列前缀的查询,匹配范围值的查询,精确匹配左前列并范围匹配下一列,索引覆盖,order by 排等;
not in, != ,<> 等否定查询在b-tree不会用到索引(网上查的待求证 跟引擎有关)
索引列上不能使用表达式和函数
innodb 索引最大大小不能超过767个字节,myisam是1000个字节,大字符串要使用前缀索引,前缀索引就要考虑到索引的选择性
联合索引 mysql5.0之前一条sql只能使用到一个列上的索引;mysql5.0之后引入了索引合并,可以合并多个列上的索引进行过滤,但是需要更多的内存和磁盘IO来缓存这些索引获取的数据,联合索引更好;
列的顺序:经常被使用的列优先,选择性高的列优先,宽度小的列优先
覆盖索引 除了where条件之外,可以直接通过索引获取查询的数据,也就没必要去读取数据行的数据了,避免innodb主键索引的二次查询,避免myisam的系统调用
hash索引:基于hash表实现,只有查询精确匹配到hash索引所有的列才会用到索引,存储引擎会为每一行计算hash码,hash索引存储的就是hash码,就是说查找会很快,必须二次查找;存储顺序是按照hash码的大小存储的,不能用于排序、范围查找等;hash冲突
索引优化排序:通过索引扫描数据,索引的顺序和order by字段完全一致,排序方向也要完全一致,连表查询必须在主表中
索引优化锁:减少锁定的行数量,加快处理速度,加快行释放的速度
删除重复和冗余的索引,查找不会被使用的索引,更新索引的统计信息,减少索引碎片(analyze table table_name, optimize table会锁表)
9,sql查询的优化
获取性能有问题的sql:通过用户反馈,慢日志获取,实时获取性能有问题的sql
慢日志 开销主要是磁盘的IO和磁盘存储空间
慢查询开启 slow_query_log
慢查询日志存储路径 slow_query_log_file
慢查询日志sql时间伐值 long_query_time
是否记录未使用索引的sql log_queries_not_using_indexes
mysqldumpslow ,pt-query-digest慢日志分析工具
实时获取有问题的sql show processlist
infomation_schema 数据库下的processlist表:SELECT user, host, time, command, time FROM [mysql|information_schema].processlist WHERE user = ‘me‘ and state IS NOT NULL;
mysql的执行sql的生命周期分析
客户端通过mysql的接口发送sql给服务器
这个影响微乎其微
服务器检查查询缓存是否命中该sql,命中则直接返回结果
打开查询缓存,优先查询缓存是否命中,通过一个大小写敏感的hash查找实现,hash是全值匹配,所以sql必须完全一样,每次更新查询缓存涉及的表时缓存都会被刷新,而且在缓存查询是否命中时候会对缓存加锁,在并发高和读写太频繁的系统中很可能是降低查询处理效率
query_cache_type = ON | OFF | DEMAND
query_cache_size 查询缓存内存大小1024 * n
query_cache_limit 查询缓存可存储的最大值
query_cache_wlock_invalidate 数据表锁住是否返回缓存中的数据 默认关闭
query_cache_min_res_unit 查询缓存分配的内存最小单位
在sql上加sql_no_cache 不去查询缓存
服务器对sql进行解析,预处理,优化器生成执行计划
mysql的解析器通过关键字对mysql进行语句解析,使用mysql的语法规则和解析查询,生成一颗解析树
预处理是进一步检查解析树是否合法(查询中涉及的表和数据列是否存在,名字别名歧义等)
查询优化器生成查询计划,一条sql可用有很多执行方式,优化器会对每种执行方式存储引擎提供的统计信息进行比较,找到成本最低的计划;可能重新定义表的关联顺序,将外连接转化为内连接,等价变换,将表达式转换为常数表达式,子查询转换为关联表
索引太多会导致优化器耗时太多;
存储引擎提供的统计信息不准确、执行计划的成本骨断可能并不是实际执行计划的成本,服务层并不知道存储层的所有信息,那些数据是在内存中还是磁盘上,那些数据是顺序读还是随机读,就会错误的选择读区数据少的执行计划
优化器认为的最优结果可能不是想要的(执行时间,资源利用最少)
优化器不会考虑并发问题,也就是锁的问题对执行的影响
优化器会基于一些固定的规则生成执行计划,不会考虑执行成本
根据执行计划,调用引擎api查询数据
这个影响微乎其微
返回客户端结果
这个影响微乎其微
获取sql执行各个阶段的时间 profile(官方要废弃) performance_schema(mysql5.5官方推荐的性能分析存储引擎)
set profile = 1
执行sql
show profiles;
show profile for query N;
show profile cpu for query N;
...
开启performance_schema 会记录所有sql的阶段执行时间监控
update `setup_instruments` SET enabled=‘YES‘ where NAME like ‘stage&‘;
update `setup_consumers` SET enabled=‘YES‘ where NAME like ‘event%‘;
10,分库分表
11,数据库监控
以上是关于数据库的性能优化的一些思考的主要内容,如果未能解决你的问题,请参考以下文章