一.前言

现如今,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显,所以要重视数据库的性能优化。

一个成熟的数据库架构并不是一开始设计就具备高可用、高伸缩等特性的,它是随着用户量的增加,基础架构才逐渐完善。

SQL执行慢的原因:

  • 网络速度慢,内存不足,I/O吞吐量小,磁盘空间满了等硬件问题
  • 数据库整体结构已经扛不住压力
  • 没有索引或者索引失效
  • 数据表里的数据记录过多
  • 服务器调优及各个参数设置也可能会影响
  • 开发者编写的SQL效率
  • 其它

根据上述问题,将数据库的优化分为几个阶段进行调整,力求让数据库发挥好的性能和稳定运行。

二.数据库表设计

项目立项后,开发部门根据产品部门需求开发项目。开发工程师在开发项目初期会对表结构设计。对于数据库来说,表结构设计很重要,如果设计不当,会直接影响到用户访问网站速度,用户体验不好!

这种情况具体影响因素有很多,例如慢查询(低效的查询语句)、没有适当建立索引、数据库堵塞(锁)等。当然,有测试部门的团队,会做产品测试,找Bug。

由于开发工程师重视点不同,初期不会考虑太多数据库设计是否合理,而是尽快完成功能实现和交付。等项目上线有一定访问量后,隐藏的问题就会暴露,这时再去修改就不是这么容易的事了!

sql语句优化

1.EXPLAIN分析SELECT查询

很多情况下,使用EXPLAIN关键字可以知道MySQL是如何处理SQL语句的,这可以帮助分析查询语句,从而或许能尽快的找到优化方法以及潜在的性能问题。

2.SELECT查询必须指明字段名

SELECT * 的查询会加很多不必要的消耗(例如CPU、I/O等),同时,也有可能增加了使用覆盖索引。所以SELECT查询时,要求直接在后面指明需要查询的对应字段名。

3.查询一条数据的时候,使用 LIMIT 1

减少多余的查询,因为指定limit 1后,查询到一条数据就不再继续查询了,使得EXPLAIN中type列达到const类型,查询语句更优。

4.为搜索的WHERE字段建立索引

一般,每个表都会设置一个主键,而索引并不一定就是给主键。如果在表中,有某个字段总要会经常用来做WHERE查询搜索,而且是读大于写的。

5.千万不要使用 ORDER BY RAND()

如果想随机取数据,不要用用随机数取,因为这种查询,对数据库的性能毫无益处(消耗CPU)。更好的方案之一是先找到数据所在的条数N,然后再用LIMIT N, 1这样查询。

6.保证每张表都有一个主键ID

每设计新建一张表的时候,都应该为其设计一个ID字段,并让其成为主键,而且最好是INT型(也有使用UUID的),同时设置这个ID字段为自增(AUTO_INCREMENT)的标志。

7.尽可能的使用 NOT NULL

NULL也需要额外的空间,NULL字段在进行查询比较的时候,是比较麻烦的。如果不是必须使用NULL,就建议使用NOT NULL。

8.选择合适的存储引擎

在MySQL中有MyISAM和InnoDB两种存储引擎,两者各有利弊,需要了解两者的差异然后来做出最合适的选择,例如InnoDB支持事务而MyISAM不支持,MyISAM查询比InnoDB快等等;若不太清楚选择什么的话,那就用InnoDB。

9.把IP地址存为UNSIGNED INT

在遇到需要存储IP地址的时候,大多数想法都会是存储VARCHAR(15)字符串类型的,而不会想到要用INT整型来存储;如果用整型来存储,只需要4个字节,并且你可以有定长的字段,而且会带来查询上的优势。

10.尽量不要在WHERE查询时对字段进行null值判断

当对一个字段进行null的判断时候,会比较慢的,这是因为这个判断会导致引擎放弃使用所有已有的索引而进行全表扫描搜索。

11.尽量不要使用%前缀的LIKE模糊查询

模糊查询,在日常开发中会经常遇到,很多都是直接 LIKE '%key_word%' 或者 LIKE '%key_word' 这样搜索的,这两种搜索方式,都会导致索引失效从而进行全表扫描搜索。建议使用“全文索引”。

12.避免在WHERE查询时对字段进行表达式操作

例如查询语句SELECT id FROM table WHERE num * 2 = 50;,这样的查询,对字段num做了一个乘2的算数操作,就会导致索引失效。

13.减少不必要的排序

排序操作会消耗较多的CPU资源,所以减少不必要的排序可以在缓存命中率高等I/O足够的情况下,会降低SQL的响应时间。

14.建议用JOIN代替子查询

JOIN的性能相比子查询有更大优势。

15.避免发生隐式类型转换

类型转换主要是指在WHERE子句中出现字段的类型和传入的参数类型不一致的时候发生的类型转换;这是因为如果传入的数据类型和字段类型不一致,MySQL可能会对数据进行类型转换操作,也可能不进行处理而直接交由存储引擎去处理,这样一来,就可能会出现索引无法使用的情况而造成执行计划问题。

16.避免多表查询字段类型不一致

在遇到需要多表联合查询的时候,设计表结构的时候,尽量保持表与表的关联字段一致,并且都要设置索引。同时,多表连接查询时,尽量把结果集小的表作为驱动表。

17.建议开启查询缓存

大多数的MySQL服务器都开启了查询缓存,这是提高性能最有效的方法之一,因为查询缓存由MySQL数据库引擎自动处理,当有很多相同的查询被执行了多次的时候,这些查询结果会被放到一个缓存中,这样,后续的相同的查询就不用操作表,而直接访问缓存结果了。

18.使用UNION代替临时表

UNION查询可以把两条或更多的SELECT查询结果合并到一个查询中,从而不再需要创建临时表来完成。需要注意的是,使用UNION的所有SELECT语句中的字段数目要相同。

19.慎用IN查询

IN以及NOT IN查询都要慎重,因为可能会导致全表扫描,而对于连续的数值,能用BETWEEN就不要用IN了。

三.数据库结构设计

当开发人员设计好表语句后,就需要运维工程师进行服务部署,项目上线。这里应该根据需求进行预估访问量,再进行配置的选择和结构设计。

项目初期访问量一般是寥寥无几,此阶段Web+数据库单台部署足以应对在1000左右的QPS(每秒查询率)。考虑到单点故障,应做到高可用性,可采用MySQL主从复制+Keepalived实现双机热备。主流HA软件有:Keepalived(推荐)、Heartbeat。

四.数据库性能优化

硬件配置选择

硬盘

磁盘寻道能力(磁盘I/O),以目前高转速SCSI硬盘(7200转/秒)为例,服务器硬盘读200M/S,写120M/S。而固态硬盘读1500MB/S,写800MB/S,这个差距是非常明显的。修改磁盘调度算法为deadline,echo 'deadline' >/sys/block/sda/queue/scheduler,查看结果# cat /sys/block/sda/queue/scheduler

noop anticipatory [deadline] cfq

MySQL每秒钟都在进行大量、复杂的查询操作,对磁盘的读写量可想而知。所以,通常认为磁盘I/O是制约MySQL性能的最大因素之一。解决这一制约因素可以考虑以下几种解决方案:

  1. 使用RAID-0+1磁盘阵列,注意不要尝试使用RAID-5,性能会很差
  2. 资金充足,可单独对数据库服务器选择固态方式来提高性能

CPU

CPU对于MySQL应用,推荐使用S.M.P.架构的多路对称CPU,例如:可以使用8颗Intel Xeon 3.6GHz的CPU。数据库对于CPU的需求没有内存这么大,通常64G内存,只需要8核CPU就可以了。如果是单实例的mysql,可以在/etc/grub.conf配置文件中,加入参数numa=off,禁用numa功能。

内存

物理内存对于一台使用MySQL的Database Server来说,服务器内存建议不要小于8GB,用以应对高速增长的咨询等信息。内存方面可以关闭swap功能, echo 0 >/proc/sys/vm/swappiness ,关闭swap功能

Linux内核有一个特性,会从物理内存中划分出缓存区(系统缓存和数据缓存)来存放热数据,通过文件系统延迟写入机制,等满足条件时(如缓存区大小到达一定百分比或者执行sync命令)才会同步到磁盘。

也就是说物理内存越大,分配缓存区越大,缓存数据越多,建议物理内存至少富裕50%以上。

数据库配置优化

MySQL应用最广泛的有两种存储引擎:一个是MyISAM,不支持事务处理,读性能处理快,表级别锁。另一个是InnoDB,支持事务处理(ACID属性),设计目标是为大数据处理,行级别锁。

表锁:开销小,锁定粒度大,发生死锁概率高,相对并发也低。

行锁:开销大,锁定粒度小,发生死锁概率低,相对并发也高。

用表锁和行锁,主要为保证数据完整性。例如,一个用户在操作一张表,其他用户也想操作这张表,那么就要等第一个用户操作完,其他用户才能操作,表锁和行锁就是这个作用。否则多个用户同时操作一张表,肯定会数据产生冲突或者异常。

根据这些方面看,使用InnoDB存储引擎是最好的选择,也是MySQL5.5+版本默认存储引擎。每个存储引擎相关运行参数比较多,以下列出可能影响数据库性能的参数。

公共参数默认值

#同时处理最大连接数,建议设置最大连接数是上限连接数的80%左右
max_connections = 151 #查询排序时缓冲区大小,只对order by和group by起作用,建议增大为16M
sort_buffer_size = 2M #打开文件数限制,如果show global status like 'open_files'查看的值等于或者大于open_files_limit值时,程序会无法连接数据库或卡死
open_files_limit = 1024

MyISAM参数默认值

#索引缓存区大小,一般设置物理内存的30-40%
key_buffer_size = 16M #读操作缓冲区大小,建议设置16M或32M
read_buffer_size = 128K #打开查询缓存功能
query_cache_type = ON #查询缓存限制,只有1M以下查询结果才会被缓存,以免结果数据较大把缓存池覆盖
query_cache_limit = 1M #查看缓冲区大小,用于缓存SELECT查询结果,下一次有同样SELECT查询将直接从缓存池返回结果,可适当成倍增加此值
query_cache_size = 16M

InnoDB参数默认值

#索引和数据缓冲区大小,建议设置物理内存的70%左右
innodb_buffer_pool_size = 128M #缓冲池实例个数,推荐设置4个或8个
innodb_buffer_pool_instances = 1 #关键参数,0代表大约每秒写入到日志并同步到磁盘,数据库故障会丢失1秒左右事务数据。1为每执行一条SQL后写入到日志并同步到磁盘,I/O开销大,执行完SQL要等待日志读写,效率低。2代表只把日志写入到系统缓存区,再每秒同步到磁盘,效率很高,如果服务器故障,才会丢失事务数据。对数据安全性要求不是很高的推荐设置2,性能高,修改后效果明显。
innodb_flush_log_at_trx_commit = 1 #是否共享表空间,5.7+版本默认ON,共享表空间idbdata文件不断增大,影响一定的I/O性能。建议开启独立表空间模式,每个表的索引和数据都存在自己独立的表空间中,可以实现单表在不同数据库中移动。
innodb_file_per_table = OFF #日志缓冲区大小,由于日志最长每秒钟刷新一次,所以一般不用超过16M
innodb_log_buffer_size = 8M

系统配置优化

大多数MySQL都部署在linux系统上,所以操作系统的一些参数也会影响到MySQL性能。

内核配置优化

#TIME_WAIT超时时间,默认是60s
net.ipv4.tcp_fin_timeout = 30 #1表示开启复用,允许TIME_WAIT socket重新用于新的TCP连接,0表示关闭
net.ipv4.tcp_tw_reuse = 1 #1表示开启TIME_WAIT socket快速回收,0表示关闭
net.ipv4.tcp_tw_recycle = 1 #系统保持TIME_WAIT socket最大数量,如果超出这个数,系统将随机清除一些TIME_WAIT并打印警告信息
net.ipv4.tcp_max_tw_buckets = 4096 #进入SYN队列最大长度,加大队列长度可容纳更多的等待连接
net.ipv4.tcp_max_syn_backlog = 4096

打开文件句柄优化

vi /etc/security/limits.conf

* soft nofile 65535
* hard nofile 65535

数据库安全优化

数据库安全是项目中最重要的部分,信息泄露会造成重大事故,所以要重视安全问题,防止信息被盗取、破坏。

具体建议

  1. 数据库使用 mysql 用户启动,权限为700
  2. 登录时在命令行暴漏密码,备份脚本中如果有密码,给设置 700,属主和用户组为 mysql或root
  3. 初始删除无用的用户,只保留 root 127.0.0.1 和 root localhost
  4. 授权用户对应的主机不要用%,权限不要给 all,最小化授权,从库只给
  5. select 权限
  6. 删除默认存在的 test 库
  7. 为mysq用户设置复杂密码
  8. 不要给一个用户管所有的库,尽量专库专用户
  9. 清理 mysql 操作日志文件~/mysql_history
  10. 禁止开发获取到 web 连接的密码,禁止开发连接操作生产对外的库
  11. 服务器禁止设置外网IP
  12. 防 SQL 注入(WEB), php.ini 配置文件里面设置

五.数据库架构扩展

随着业务量越来越大,单台数据库服务器性能已无法满足业务需求,该考虑增加服务器扩展架构了。主要思想是分解单台数据库负载,突破磁盘I/O性能,热数据存放缓存中,降低磁盘I/O访问频率。

增加缓存

给数据库增加缓存系统,把热数据缓存到内存中,如果缓存中有请求的数据就不再去请求MySQL,减少数据库负载。缓存实现有本地缓存和分布式缓存,本地缓存是将数据缓存到本地服务器内存中或者文件中。

分布式缓存可以缓存海量数据,扩展性好,主流的分布式缓存系统:memcached、redis,memcached性能稳定,数据缓存在内存中,速度很快,QPS理论可达8w左右。如果想数据持久化就选择用redis,性能不低于memcached。

主从复制与读写分离

在生产环境中,业务系统通常读多写少,可部署一主多从架构,主数据库负责写操作,并做双机热备,多台从数据库做负载均衡,负责读操作。主流的负载均衡器:LVS、HAProxy、Nginx。

大多数企业是在代码层面实现读写分离,效率高。另一个种方式通过代理程序实现读写分离,企业中应用较少,会增加中间件消耗。主流中间件代理系统有MyCat、Atlas等。

在这种MySQL主从复制拓扑架构中,分散单台负载,大大提高数据库并发能力。如果一台从服务器能处理1500 QPS,那么3台就能处理4500 QPS,而且容易横向扩展。

分库

分库是根据业务将数据库中相关的表分离到不同的数据库中,例如会员库、订单库、咨询库等,每个库单独放到一个实例中。此时可以根据不同功能的压力来购买不同配置的实例,从而减少资金投入。

如果业务量很大,还可将分离后的数据库做主从复制架构,进一步避免单库压力过大。

分表

数据量的日剧增加,数据库中某个表有几百万条数据,导致查询和插入耗时太长,应该考虑把这个表拆分成多个小表,来减轻单个表的压力,提高处理效率,此方式称为分表。

分表技术比较麻烦,要修改程序代码里的SQL语句,还要手动去创建其他表,也可以用merge存储引擎实现分表,相对简单许多。分表后,程序是对一个总表进行操作,这个总表不存放数据,只有一些分表的关系,以及更新数据的方式,总表会根据不同的查询,将压力分到不同的小表上,因此提高并发能力和磁盘I/O性能。

分表分为垂直拆分和水平拆分:

垂直拆分:把原来的一个很多字段的表拆分多个表,解决表的宽度问题。你可以把不常用的字段单独放到一个表中,也可以把大字段独立放一个表中,或者把关联密切的字段放一个表中。

水平拆分:把原来一个表拆分成多个表,每个表的结构都一样,解决单表数据量大的问题。

分区

分区就是把一张表的数据根据表结构中的字段(如range、list、hash等)分成多个区块,这些区块可以在一个磁盘上,也可以在不同的磁盘上,分区后,表面上还是一张表,但数据散列在多个位置,这样一来,多块硬盘同时处理不同的请求,从而提高磁盘I/O读写性能。

六.数据维护

性能状态关键指标

通常使用QPS(Queries Per Second,每秒查询书)和TPS(Transactions Per Second)来查看数据库的效率。

通过show status查看运行状态,会有300多条状态信息记录,其中有几个值可以计算出QPS和TPS,如下:

Uptime:服务器已经运行的实际,单位秒

Questions:已经发送给数据库查询数

Com_select:查询次数,实际操作数据库的

Com_insert:插入次数

Com_delete:删除次数

Com_update:更新次数

Com_commit:事务次数

Com_rollback:回滚次数

基于Questions计算出QPS

mysql> show global status like 'Questions';

mysql> show global status like 'Uptime';

QPS = Questions / Uptime

基于Com_commit和Com_rollback计算出TPS:

mysql> show global status like 'Com_commit';

mysql> show global status like 'Com_rollback';

mysql> show global status like 'Uptime';

TPS = (Com_commit + Com_rollback) / Uptime

数据库备份

备份数据库是最基本的工作,也是最重要的。高频率的备份策略,选用一个稳定快速的工具至关重要。数据库大小在2G以内,建议使用官方的逻辑备份工具mysqldump。

超过2G以上,建议使用percona公司的物理备份工具xtrabackup,否则会很慢。这两个工具都支持InnoDB存储引擎下热备,不影响业务读写操作。

MySQL服务器性能分析

重点关注:

id:CPU利用率百分比,平均小于60%正常,但已经比较繁忙了。

wa:CPU等待磁盘IO响应时间,一般大于5说明磁盘读写量大。

KB_read/s、KB_wrtn/s 每秒读写数据量,主要根据磁盘每秒最高读写速度评估。

r/s、w/s:每秒读写请求次数,可以理解为IOPS(每秒输入输出量),是衡量磁盘性能的主要指标之一。

await:IO平均每秒响应时间,一般大于5说明磁盘响应慢,超过自身性能。

util:磁盘利用率百分比,平均小于60%正常,但已经比较繁忙了。

Mysql实例 数据库优化的更多相关文章

  1. MySQL/Oracle数据库优化总结

    MySQL数据库优化的八种方式 1.选取最适用的字段属性 MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快.因此,在创建表的时候,为了获得更好的性能 ...

  2. MySql的数据库优化到底优啥了都??(1)

    嘟嘟最不愿意做的就是翻招聘信息. 因为一翻招聘信息,工作经历你写低于两年都不好意思,前后端必须炉火纯青融汇贯通,各式框架必须如数家珍不写精通咋的你也得熟练熟练, 对了你是985吗?你是211吗??你不 ...

  3. MySql的数据库优化到底优化啥了都(3)

    嘟嘟在上两个文章里面简单粗糙的讲了讲关于MySql存储引擎的一些特性以及选择.个人感觉如果面试官给我机会的话,至少能说个10分钟了吧.只可惜有时候生活就是这样:骨感的皮包骨头了还在那美呢.牢骚两句,北 ...

  4. MySQL之数据库优化

    Mysql数据库的优化技术 对mysql优化是一个综合性的技术,主要包括 •表的设计合理化(符合3NF) •添加适当索引(index) [四种: 普通索引.主键索引.唯一索引unique.全文索引] ...

  5. MySQL:数据库优化,看这篇就够了

    数据库优化一方面是找出系统的瓶颈,提高MySQL数据库的整体性能,而另一方面需要合理的结构设计和参数调整,以提高用户的相应速度,同时还要尽可能的节约系统资源,以便让系统提供更大的负荷. 1. 优化一览 ...

  6. MySql的数据库优化到底优啥了都??(2)

    嘟嘟在写此篇文章之前心里先默念(简单,通俗,工整)*10 吟唱完了,进入正题 3.Memory存储引擎 除了存放一个表结构相关信息的.frm文件在磁盘上,其他数据都存储在内存中.说白点哪天你数据库死机 ...

  7. Mysql实例参数优化15个主要参数讲解(原创)

    1.innodb_buffer_pool_size 设置物理内存的60%-80%,反应IO吞吐的最大上限2.innodb_thread_concurrency 线程并发,设置为CPU核心数,如果等于0 ...

  8. (转)运维角度浅谈MySQL数据库优化

    转自:http://lizhenliang.blog.51cto.com/7876557/1657465 一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架 ...

  9. 运维角度浅谈MySQL数据库优化(转)

    一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架构才逐渐完善.这篇博文主要谈MySQL数据库发展周期中所面临的问题及优化方案,暂且抛开前端应用不说,大致分 ...

随机推荐

  1. Centos8 Docker部署 .Net6 项目

    .Net6项目发布 1.在VS中发布项目,并编写好Dockerfile文件 Dockerfile文件内容如下: FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS ...

  2. webpack--初试webpack( 核心、体验、资源打包)

    前言 webpack是当前前端项目中最常用的资源构建工具,从本文开始,来总结记录一下关于webpack的学习. 正文 1.webpack简介 webpack官网(https://webpack.doc ...

  3. java中static关键字的解析

    静态的特点: A:随着类的加载而加载 B:优先于对象而存在 C:静态是被所有对象共享的数据 这也是我们来判断是否使用静态的标准 D:静态的出现,让我们的调用方式多了一种 类名.静态的内容 非静态的内容 ...

  4. 如何看待 SAE 在2014 年 3 月 24 日发生的的大面积宕机事故?

    3 月 24 日晚间大约 23 点左右,新浪云 SAE 一处核心机柜掉电,导致 SAE 平台下大量应用无法正常访问,并在 10 小时后才陆续修复.这次事故暴露 SAE 的哪些缺陷?SAE 运维人员又是 ...

  5. 单元测试NUnit,mock组件NSubstitute,信号量SemaphoreSlim,异步lock等例子

    public class LockTest { private IDatabase _database; private readonly Random _random = new Random(); ...

  6. vue3 学习笔记 (五)——vue3 的 setup 如何实现响应式功能?

    setup 是用来写组合式 api ,内部的数据和方法需要通过 return 之后,模板才能使用.在之前 vue2 中,data 返回的数据,可以直接进行双向绑定使用,如果我们把 setup 中数据类 ...

  7. 470. 用 Rand7() 实现 Rand10()

    已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 10 范围内的均匀随机整数. public class Solution { public s ...

  8. 洛谷 P3711 - 仓鼠的数学题(多项式)

    洛谷题面传送门 提供一种不太一样的做法. 假设要求的多项式为 \(f(x)\).我们考察 \(f(x)-f(x-1)\),不难发现其等于 \(\sum\limits_{i=0}^na_ix^i\) 考 ...

  9. CF932F Escape Through Leaf

    CF932F Escape Through Leaf 首先, $ O(n^2) $ dp 是很显然的,方程长这样: \[dp[u] = min\{dp[v] + a_u\times b_v\} \] ...

  10. Codeforces 750E - New Year and Old Subsequence(线段树维护矩阵乘法,板子题)

    Codeforces 题目传送门 & 洛谷题目传送门 u1s1 我做这道 *2600 的动力是 wjz 出了道这个套路的题,而我连起码的思路都没有,wtcl/kk 首先考虑怎样对某个固定的串计 ...