当前位置:Gxlcms > 数据库问题 > 数据库的性能优化的一些思考

数据库的性能优化的一些思考

时间:2021-07-01 10:21:17 帮助过:12人阅读

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 参考:https://www.jianshu.com/p/169315f2124a?utm_source=oschina-app   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,数据库监控