一.机器配置规划

1.普通场景

根据历史生产经验来说,对于较为简单的Java应用部署来说,机器配置大约是2核4G或者4核8G;对于数据库部署来说,机器配置最低在8核16G,正常在16核32G。
如果Java应用系统部署在4核8G的机器上,每秒抗下500左右的并发访问量差不多比较合适,当然这个很难说。假设每个请求花费1s,那么每秒只可以处理100个请求;如果每个请求只花费100ms,此时每秒就可以处理每秒七八百请求。所以一台机器能抗下每秒多少请求和请求处理耗时是关联的。

2.高并发场景

对于简单的Java应用系统来说请求主要耗费在具体代码逻辑处理中,如果请求需要访问数据库资源还需要还需要算上与数据库之间的网络通信上,另外数据库还需要执行SQL指令,还需要对内存或者磁盘文件进行大量的操作,所以在高并发环境下,对于数据库的负载是相对较高的。所以对于数据库部署来说,机器配置最低在8核16G,正常在16核32G。
生产经验告诉我们一般8核16G配置的数据库每秒抗一两千请求基本上没有太大问题。但是如果系统需要能够抗住大上千的并发会比较困难,因为数据库的CPU,磁盘IO,内存的负载会很高,甚至可能导致宕机。
对于16核32G配置的数据库来说,每秒抗两三千,甚至三四千并发基本没问题,但是如果应用要求每秒能够抗下上万并发就是比较困难了。
所以对于数据库而言,一般选用的机器配置还要结合业务要求的并发量去考虑的。最好采用SSD固态硬盘而不是机械硬盘,因为数据库作为持久化存储需要磁盘IO,在使用SSD情况下每秒能够抗住比机械硬盘稍高一些的并发。

二.性能压测

当申请到一台机器之后,一般会由DBA去安装署和启动MySQL。DBA会按照历史生产经验用自己的MySQL生产调优参数模板去启动这个数据库。接着DBA正常启动这台机器上的数据库之后,一般就会交给研发使用,你会拿到用户名和密码,理论上Java系统就可以直接连接上去进行开发工作。
一般来说研发需要对申请的数据库进相关性能压测,这样做的目的是了解当前数据库的并发能力,这样的能力是否符合业务需要,这样当系统上线时做到心中有数。

1. 性能指标

1.1 QPS

QPS(Query Per Second)对于数据库来说代表每秒可以处理多少个请求。这里的一次请求可以理解为一条SQL指令的执行,也就是说这个数据库每秒可以处理多少个SQL指令的执行。

1.2 TPS

TPS(Transaction Per Second)代表每秒可以处理的事务量。对于数据库而言表示每秒会处理多少次事务提交或者回滚。

1.3 IO指标

(1)IOPS:代表机器的随机IO并发处理能力。比如机器可以达到200 IOPS,意思就是每秒可以执行200个随机IO读写请求。
对于MySQL数据库来说这个指标非常重要。因为在内存中更新的脏数据页,最后都会由后台的IO线程在不确定的时间刷回到磁盘里去,这个就是随机IO的过程。如果这个指标很低代表内存里的脏数据刷回磁盘的效率不高。

(2)吞吐量:表示机器的磁盘存储每秒可以读写多少字节的数据量。
MySQL执行各种SQL指令提交事务时,会大量的写redo log之类的日志的,这些日志都会直接写磁盘文件。所以一台机器的存储每秒可以读写多少字节的数据量,就决定了每秒可以把多少redo log之类的日志写入到磁盘里去。
一股来说写redo log之类的日志都是对磁盘文件进行顺序写入。一般普通磁盘的顺序写入的吞吐量每秒都可以达到20OMB左右。所以通常而言,机器的磁盘吞吐量都是足够承载高并发请求的。

(3)latency:代表向磁盘写入一条数据的延时。
这个指标也非常重要,因为执行SQL语句和提交事务时都需要顺序写入redo log到磁盘文件,所以此时写一条日志到磁盘文件里的延时对数据库的SQL语句执行性能是有影响的。 一般来说,磁盘读写延迟越低,代表数据库性能越高,执行每个SQL语句和事务时的速度就越快。

1.4负载指标

(1)CPU负载:假设数据库压测到每秒处理3000请求,但此时CPU负载很高。那么说明数据库不能继续压测更高的QPS了,否则CPU会无法承受。

(2)网络负载:表示在压测到一定的QPS和TPS时,每秒钟机器网卡会输入和输出多少MB数据。有可能当前网络带宽最多每秒传输100MB数据时,可能QPS到1000时,网卡就打满了。虽然已经每秒传输100 MB数据了,此时即使其他指标正常,但是此时也不能继续压测了。

(3)内存负载:在压测到一定情况下时,当前机器耗费了多少内存。如果机器内存耗费太多,也不能继续进行压测。

2. 通过sysbench进行性能压测

Sysbench可以帮助我们在数据库中构造出大量的数据,也可以模拟几千个线程并发访问数据库,也可以模拟各种SQL语句进行压测,甚至可以模拟出各种事务提交到数据库中压测TPS指标。

2.1 linux环境下安装

curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.rpm.sh | sudo bash
sudo yum -y install sysbench
sysbench —version


执行最后一条命令,看到sysbench版本号,说明安装成功。

2.2 压测案例

我们准备在数据库中创建好一个测试库test_db,同时创建好对应的测试账号和密码。然后基于sysbench构建20个测试表,每个表中构建100万条数据,使用10个并发线程对数据路连续访问5分钟,对其进行压力测试。
步骤一:准备

sysbench \
—db-driver=mysql \
—time=300 \
—threads=10 \
—report-interval=1 \
—mysql-host=127.0.0.1 \
—mysql-port=3306 \
—mysql-user=test_user \
—mysql-password=test_user \
—mysql-db=test_db \
—tables=20 \
—table_size= 1000000 \
oltp_read_write \
—db-ps-mode=disable \
prepare

参数解释
①-db-driver=mysql:基于mysql驱动连接mysql数据库。如果是oracle,sqlserver自然是其他数据库的驱动。
②—time=300:表示连续访问300秒。
③—threads=10:表示用10个线程模拟并发访问。
④—report-interval=1:表示每隔1秒输出压测情况。
⑤mysql-host=127.0.0.1 —mysql-port=3306 —mysql-user=test_user —mysql-password=test_user: 表示连接到哪台机器的哪个端口上的MySQL库,用户名和密码是什么。
⑥—mysql-db=test_db —tables=20 —table_size= 1000000:
表示在test_db库构造20个测试表,每个测试表构造100万条测试数据,测试表的名字会是类似于sbtest1, sbtest2。
⑦oltp_read_write:表示执行oltp数据库的读写测试。
⑧—db-ps-mode=disable:表示禁止ps模式。

prepare指令代表构造出来我们需要的数据库里的数据会自动创建20个测试表,每个表创建100万条测试数据。

步骤二:测试数据库的综合读写TPS,使用oltp_read_write模式,执行run命令代表进行压测。指令如下:

sysbench \
—db-driver=mysql \
—time=300 \
—threads=10 \
—report-interval=1 \
—mysql-host=127.0.0.1 \
—mysql-port=3306 \
—mysql-user=test_user \
—mysql-password=test_user \
—mysql-db=test_db \
—tables=20 \
—table_size= 1000000 \
oltp_read_write \
—db-ps-mode=disable \
run


如果使用oltp_read_only模式代表测试数据库的只读性能,命令如下:

sysbench \
—db-driver=mysql \
—time=300 \
—threads=10 \
—report-interval=1 \
—mysql-host=127.0.0.1 \
—mysql-port=3306 \
—mysql-user=test_user \
—mysql-password=test_user \
—mysql-db=test_db \
—tables=20 \
—table_size= 1000000 \
oltp_read_only \
—db-ps-mode=disable \
run


其他命令的参数都基本类似,主要有如下几种测试模式,总结如下:
(1)oltp_read_write:读写测试。
(2)oltp_read_only:只读测试。
(3)oltp_delete:删除测试。
(4)oltp_updae_index:测试更新索引字段性能。
(5)oltp_update_non_index:测试更新非索引字段性能。
(6)oltp_insert:测试插入性能。
(7)oltp_write_only:只测试写入性能。

步骤三:通过cleanup命令清理数据,指令如下:

sysbench \
—db-driver=mysql \
—time=300 \
—threads=10 \
—report-interval=1 \
—mysql-host=127.0.0.1 \
—mysql-port=3306 \
—mysql-user=test_user \
—mysql-password=test_user \
—mysql-db=test_db \
—tables=20 \
—table_size= 1000000 \
oltp_read_write \
—db-ps-mode=disable \
cleanup

2.3 压测结果分析

在压测过程中需要观察日志输出,这里截取了一段sysbench压测第22秒的日志输出,如下:

[22s] thds: 10 tps: 380.99 qps: 7312.66 (r/w/o: 5132.99/1155.86/1321.35) lat (ms, 95%): 21.33 err/s: 0.00 reconn/s:0.00

指标说明
(1)thds: 10
表示当前有10个线程正在进行压测。
(2)tps: 380.99
表示当前每秒执行了380.99个事务。
(3)qps: 7312.66
表示每秒可以执行7312.66个请求。
(4)(r/w/o: 5132.99/1155.86/1321.35)
表示在每秒7312.66个请求中,有5132.99个请求是读请求;1155.86个请求是写请求;1321.35个请求是其他的请求。
(5) lat (ms, 95%):21.33
表示95%的请求延迟都在21 . 33毫秒以下。
(6)err/s: 0.00 reconn/s:0.00
表示每秒有0个请求是失败的,发生了0次网络重连。

在压力测试完成之后会生成一个总的压测报告,内容如下:

SQL statistics:
queries performed:
// 表示300s压测期间执行了148万多次读请求
read: 1480084
// 表示300s压测期间执行了29万多次写请求
write: 29845711
// 表示300s压测期间执行了30万多次其他请求
other 325436
// 表示300s压测期间共执行了210万多次请求
total: 2103977
// 表示一共执行了10万多个事务,每秒执行了350多个事务
transactions: 105180( 350.6 per sec.)
// 表示一共执行了210万多次的请求,每秒执行7000+请求
queries: 2103977 (7013.26 per sec.)
ignored errors: 0 (0.00 per sec.)
reconnects: 0 (0.00 per sec.)

// 表示一共执行了300s的压测,执行了10万+的事务
General staticstics:
total time: 300.0052s
total number of events: 105180

Latency (ms):
// 请求中延时最小为4.32ms
min: 4.32
// 所有请求平均延时为13.42ms
avg: 13.42
// 延时最大请求为45.56ms
max: 45.56
// 95%的请求延时都在21.33ms以内
95th percentile: 21.33

三.性能监控

1.如何观察机器性能

在压测时,我们需要不停的增加线程数去让数据库承载更高的QPS,不断的去观察数据库到底最高可以承载多少QPS。所以此时需要不断观察机器性能指标,包括CPU负载、内存负载、网络负载、磁盘IO负载是否都在正常范围内,如果负载相对较高一些,但还没达到这些硬件的极限,那么我们可以认为这台数据库在高峰期是可以承受期望QPS。一般发现硬件资源性能指标达到极限之后就不能再进行压测了。下面将介绍如何观察机器的性能指标。

1.1 CPU负载

这里需要借助linux的top命令去进行查看,我们一般会看到如下信息:

top - 17:56:00 up 41:01, 1 user, load average: 0.15, 0.05, 0.01

参数说明
(1)17:56:00
表示当前时间。
(2)up 41:0
表示机器已经运行了多长时间。
(3)1 user
表示当前有一个用户在使用。
(4)load average: 0.15, 0.05, 0.01
表示CPU在1分钟,5分钟,15分钟内的负载情况。

什么是CPU负载?如果有一个4核的CPU机器,假设CPU负载为0.15表示CPU中一个核都没有用满,代表当前比较空闲。
如果CPU负载是1,表示有一个核已经被使用的比较繁忙了,另外3个核比较空闲一。 如果CPU负载是1.5,表示有一个核被使用繁忙,另外一个核也在使用,但是不繁忙,还有另外2个核可能还是空闲的。
如果CPU负载是4,表示4核都被跑满了。
如果CPU负载是6,表示4核被繁忙的使用,已经不够处理当前的任务,很多进程可能一直在等待CPU去执行自己的任务。

所以top命令中看到的load average表示CPU在最近1分钟,5分钟,15分钟内的平均负载数值,当前0.15说明CPU根本没怎么用,比较空闲。
但是如果在压测过程中,发现load average已经高达3.5或4,说明CPU基本被跑满了,并且满负荷运转,此时不要再提高线程数或者增加数据库QPS了。

1.2 内存负载

内存负载在top命令中会有如下显示:

Mem : 33554432k total . 20971520k used 12268339 free 307200k buffers

这里显示了当前机器的内存使用情况,可以看出总内存大概有32GB,已经使用20GB左右的内存,还有10G多的内存是空闲的;此外有大概300MB左右的内存用作操作系统内核缓冲区。
一般来说,如果内存的使用率在60%以内,属于在正常范围内。但是如果机器内存使用率高达80%多,此时需要注意了,最好不要继续增加压测线程数和QPS了。

1.3 磁盘IO

磁盘IO情况需通过dstat命令去观察。

dstat -d

显示如下信息:

dsk / total
read writ
103k 211k
0 11k

这表示存储的IO吞吐量是每秒读取103kb的数据,每秒写入211kb的数据,这个存储IO吞吐量基本上都不算多,因为普通的机械硬盘能达到每秒上百MB的读写数据量。

dstat -r

显示如下信息:

io / total
read writ
0.25 31.9
0 253
0 39.0

这里表示读IOPS和写IOPS分别是多少,代表了随机磁盘读取每秒多少次,随机磁盘写入每秒执行多少次。一般来说,随机磁需读写每秒在两三百次都是可以承受的。
如果磁盘IO吞吐量太高,比如达到每秒上百MB了,或者随机磁盘读写每秒都到极限的两三百次了,此时不要再继续增加线程数量进行压测了。

1.4 网卡流量

网卡流量可以通过dstat -n命令进行查看,显示如下:

net / total
recv sen
16k 17k

这里表示每秒钟网卡接收到流量有多少kb,每秒钟通过网卡发送出去的流量有多少 kb。通常来说,如果机器使用的是千兆网卡,那么每秒钟网卡的总流量大概就在100MB左右,甚至更低一些。
所以在进行压测时,如果网卡传输流量已经到了极限值,不要继续提高线程数进行压测了,因为此时无法通过网卡传输更多的数据了。

2.如何观察数据库性能指标

一般而言为了更加全面的,实时的观察线上数据库的各种指标,需要搭建一个统一的可视化监控平台,目前用的比较多的就是Prometheus和Grafana。
Prometheus是一个监控数据采集和存储系统,他可以利用监控数据采集组件(比如mysql_exporter)从指定的MySQL数据库中采集需要的监控数据,然后把采集到的监控数据放入自己的时序数据库中。
通常采集到了MySQL的监控数据还不够。我们需要利用Grafana展示一个可视化的监控数据系统,他可以把Prometheus采集的大量的MySQL监控数据展示成各种报表,这样可以直观的看到MySQL的监控情况。

1.Prometheus

首先需要下载(http://cactifans.hi-www.com/prometheus)如下2个压缩包:

prometheus-2.1.0.linux-amd64.tar.gz
node_exporter-0 15.2.linux-amd64.tar.gz

第一个压缩包是用来部署监控系统的;第二个压缩包是用于采集MySQL数据库所在机器的CPU,内存,网络,磁盘之类的监控数据的。
接下来还需要下载mysqld_exporter-0.10.0.linux-amd64.tar.gz;这个mysqld_exporter是用来采集MySQL数据库自己的一些监控数据的,比如SQL性能,连接数之类的指标。这个包需要通过如下链接进行下载:

https://github.com/prometheus/mysqld_exporter/releases/download/v0.10.0/mysqld_exporter-0.10.0.Iinux- amd64.tar.gz

接着需要解压上面的包,具体操作如下:

mkdir /data
mkdir /root
tar xvi prometheus-2.1.0.Iinux-amd64.tar -c /data
tar xf node_exporter-0.15.2.Iinux-amd64.tar -C /root
tar xf mysqld_exporter-0.10.0.Iinux-amd64.tar.gz -C /root
cd /data
my prometheus-2.1.0.Iinux-amd64/ prometheus
cd /prometheus

这里需要修改prometheus的配置文件prometheus.yml,主要在scrape-configs下面加入如下自定义配置,主要配置了采用MySQL数据库本身和MySQL所在机器的监控数据的相关信息:

scrapeconflgs:
- filesdconfigs:
-files:
- host.yml
jobname: Host
metricspath: /metrics
relabelconfigs:
- sourcelabels: [address]
regex: (.)
targetlabel: instance
replacement: $1
- sourcelabels: [__address
]
regex: (.
)
targetlabel: __address

replacement: $1:9100
- file_sd_configs:
- files:
- mysql.yml
job_name: MySQL
metrics_path: /metrics
relabel_configs:
- source_labels: [__address
]
regex: (.*)
target_label: instance
replacement: $1
- source_labels: [__address
]
regex: (.*)
target_label: __address

replacement: $1:9104

- job_name: prometheus
static_configs:
- targets:
- localhost: 9090

配置完毕之后就启动prometheus,注意启动之后需要在/data/prometheus目录中执行如下命令:

/data/prometheus/prometheus -storage.tsdb.retention=30d &

这里的30d表示监控数据保留30天,成功启动之后就在浏览器访问9090端口查看prometheus主页了。

2.Grafana

通过如下链接下载grafana-4.6.3.linux-x64.tar.gz:

https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.6.3.Iinux-x64.tar.gz

接着执行如下命令:

tar xf grafana-4.6.3.Iinux-x64.tar.gz -C /data/prometheus
cd /data/prometheus
mv grafana-4.6.3 grafana
cd /data/prometheus/grafana
./bin/grafana-server &

这样完成了grafana的启动,通过浏览器访问3000端口,默认用户名密码为admin/admin。在grafana左侧菜单栏的Data Source点击Add data source添加一个数据源。
在界面中输入数据源名字为Prometheus,类型为Prometheus,HTTP URL地址为http://127.0.0.1:9090,其他都采用默认配置,接下来grafana会自动从Prometheus里获取监控数据并且进行展示。

接着需要安装grafana的仪表盘组件,首先需要下载grafana-dashboards-1.6.1.tar.gz,可以从如下链接进行下载:

https://github.com/percona/grafana-dashboards/archive/v1.6.1.tar.gz

执行如下命令进行安装:

tar xvf grafana-dashboards-1.6.1.tar.gz
cd grafana-dashboards-1.6.1
updatedb
locate json | grep dashboards/

这个时候会看到一大堆划JSON文件,就是各种不同的仪表盘对应的json配置文件。你可以把这些json配置文件通过WinSCP之类的工具从linux机器上拖到你的windows电脑上来,因为需要通过浏览器上传他们。
接看在grafana页面中可以看到最上面有一个Home按钮,点击进入一个界面,你会看到Import Dashboard按钮,然后可以导入一些仪表盘,这个时候就是要导入刚才看到的一大堆JSON文件。点击Upload json file按钮就会出现一个界面让你上传一个一个的json文件,然后依次上传。接着grafana中就会出现一大堆的仪表盘了,比如机器的CPU使用率仪表盘,磁盘性能仪表盘,磁盘空间仪表盘,MySQL监控仪表盘等。

如果想要让Prometheus去采集MySQL机器的监控数据让Grafana可以展示出来,那么就必须先添加Prometheus对MySQL机器的监控。
首先必须要在MySQL机器上解压缩和启动node_exporter。启动之后是个linux进程,他会自动采集这台机器上的各种监控数据。操作如下:

tar xf node_exporter-0.15.2.Iinux-amd64.tar
mv node_exporter-0.15.2.Iinux-amd64 node_exporter
cd node_exporter
nohup ./node_exporter &

到这里就在MySQL所在的机器上启动了一个node_exporter,他就会自动采集这台机器的CPU、磁盘、内存、网络等监控数据,但是这还不够,因为prometheus上还没加入对这台机器的监控。此时我们需要在之前的prometheus的yml文件中已经定义了一个host监控项,他就是用来监控机器的,他的配置文件是host.yml。此时需要编辑这个文件加入MySQL所在的机器即可:

vi host.yml
- labels:
service: test
targets:
- 127.0.0.1

接着prometheus就会跟MySQL机器上部署的node_expporter进行通信,并获取到这台机器的监控数据,写入自己的时序数据库中进行存储。接着我们就在grafana上看到这台机器相关的性能监控了。

接着我们同样需要在MySQL所在的机器上再启动一个mysqld_exporter组件,他负责去采集MySQL数据库自己的监控数据,操作如下:

tar xf mysqld_exporter-0.10.0.Iinux-amd64.tar.gz
mv mysqld_exporter-0.10.0.Iinux-amd64 mysqld_exporter

然后去配置一些环境变量,主要设置mysqld_exporter要监控的数据库的地址信息,如下:

export DATA_SOURCE_NAME = ‘root:root@(127.0.0.1:3306)/!’
echo “export DATA_SOURCE_NAME= ‘root:root@(127.O.O.1:3306)/’” >> /etc/profile

接着启动mysqld_exporter

cd mysqld_exporter

nohup ./mysqld_exporter \
—collect.info_schema.processlist \
—collect.info_schema.innodb_tablespaces \
—collect.info_schema.innodb_metrics \
—collect.perf_schema.tableiowaits \
—collect.pert_schema.indexiowaits \
—collect.perf_schema.tablelocks \
—collect.engine_innodb_status \
—collect.perf_schema.file_events \
—collect.info_schema.processlist \
—collect.binlog_size \
—collect.info_schema.clientstats \
—collect.perf_schema.eventswaits &

上面的启动命令指定了大量的选项去开启一些监控的采集。
接着mysqld_exporter这个进程就会自动采集MySQL自己的监控数据了,然后还需要在 Prometheus里配置一下它和mysqld_exporter 通信获取数据以及存储,然后Grafana才能看到对应的报表。编辑/data/prometheus/mysq.yml配置文件,内容如下:

- labels:
service: mysql_test
targets:
- 127.0.0.1

这样就是可以在grafana中看到MySQL的各种监控数据了。