生产数据库的配置
普通的Java应用系统部署在机器上能抗多少并发?
通常来说,根据我们的经验值而言,Java应用系统部署的时候常选用的机器配置大致是2核4G和4核8G的较多一些,数据库部署的时候常选用的机器配置最低在8核16G以上,正常在16核32G。
以大量的高并发线上系统的生产经验观察下来而言,一般Java应用系统部署在4核8G的机器上,每秒钟抗下500左右的并发访问量,差不多是比较合适的,当然这个也不一定。因为你得考虑一下,假设你每个请求花费1s可以处理完,那么你一台机器每秒也许只可以处理100个请求,但是如果你每个请求只要花费100ms就可以处理完,那么你一台机器每秒也许就可以处理几百个请求。
所以一台机器能抗下每秒多少请求,往往是跟你每个请求处理耗费多长时间是关联的,但是大体上来说,根据我们大量的经验观察而言,4核8G的机器部署普通的Java应用系统,每秒大致就是抗下几百的并发访问,从每秒一两百请求到每秒七八百请求,都是有可能的,关键是看你每个请求处理需要耗费多长时间。
高并发场景下,数据库应该用什么样的机器
通常推荐的数据库至少是选用8核16G以的机器,甚至是16核32G的机器更加合适一些。
对Java系统自己而言,如果仅仅只是系统内部运行一些普通的业务逻辑,纯粹在自己内存中完成一些业务逻辑,这个性能是极高极高的。
对于Java系统接收到的每个请求,耗时最多的还是发送网络请求到数据库上去,执行大量的增删改查的SQL语句的时候,MySQL数据库需要对内存和磁盘文件进行大量的IO操作,所以数据库往往是负载最高的!
Java系统其实主要的压力和复杂都是集中在MySQL数据库!而Java系统一般并不需要去直接大量的读写本地文件进行耗时的IO操作。
对于16核32G的机器部署的MySQL数据库而言,每秒抗个两三千,甚至三四千的并发请求也都是可以的,但是如果你达到每秒上万请求,那么数据库的CPU、磁盘、IO、内存的负载瞬间都会飙升到很高,数据库也是可能会扛不住宕机的。
对于数据库而言,如果可以的话,最好是采用SSD固态硬盘而不是普通的机械硬盘,因为SSD固态硬盘的随机读写能力比机械硬盘强很多。
IO相关的压测
压测性能指标
(1)
IOPS
:这个指的是机器的随机IO并发处理的能力,比如机器可以达到200 IOPS,意思就是说每秒可以执行200个随机IO读写请求。
这个指标是很关键的,因为在内存中更新的脏数据库,最后都会由后台IO线程在不确定的时间,刷回到磁盘里去,这就是随机IO的过程。如果说IOPS指标太低了,那么会导致内存里的脏数据刷回磁盘的效率就会不高。
(2)吞吐量:
这个指的是机器的磁盘存储每秒可以读写多少字节的数据量
这个指标也是很关键的,因为平时在执行各种SQL语句的时候,提交事务的时候,都是大量的会写
redo log
之类的日志的,这些日志都会直接写磁盘文件。
所以一台机器的存储每秒可以读写多少字节的数据量,就决定了每秒可以把多少redo log之类的日志写入到磁盘里去。一般来说写
redo log
之类的日志,都是对磁盘文件进行顺序写入的,也就是一行接着一行的写,不会说进行随机的读写,那么一般普通磁盘的顺序写入的吞吐量每秒都可以达到200MB左右。所以通常而言,机器的磁盘吞吐量都是足够承载高并发请求的。(3)latency:
这个指标说的是往磁盘里写入一条数据的延迟。
这个指标同样很重要,因为执行SQL语句和提交事务的时候,都需要顺序写
redo log
磁盘文件,所以此时写一条日志到磁盘文件里去,到底是延迟1ms,还是延迟100us,这就对数据库的SQL语句执行性能是有影响的。一般来说,当然是磁盘读写延迟越低,那么数据库性能就越高,执行每个SQL语句和事务的时候速度就会越快。(4)CPU负载:
CPU负载是一个很重要的性能指标,因为假设数据库压测到了每秒处理3000请求了,可能其它的性能指标都还正常,但是此时CPU负载特别高,那么也说明数据库不能继续往下压测更高的QPS了,否则CPU是吃不消的。
(5)网络负载:
这个主要是要看看机器带宽情况下,在压测到一定的QPS和TPS的时候,每秒钟机器的网卡会输入多少MB数据,会输出多少MB数据,因为有可能你的网络带宽最多每秒传输100MB的数据,那么可能你的QPS到1000的时候,网卡就打满了,已经每秒传输100MB的数据了,此时即使其他指标都还算正常,但是也不能继续压测下去了。
(6)内存负载:
这个就是看看在压测到一定情况下的时候,机器内存耗费了多少,如果说机器内存耗费过高了,说明也不能继续压测下去了
数据库压测工具
介绍一个非常好用的数据库压测工具,就是
sysbench
,这个工具可以自动在数据库里构造出来大量的数据,想要多少数据,他就自动构造出来多少条数据。
然后这个工具接着可以模拟几千个线程并发的访数据库,模拟使用各种各样的SQL语句来访问你的数据库,包括模拟出来各种事务提交到数据库里去,甚至可以模拟出几十万的TPS去压测数据库。
- 在linux上安装sysbench工具
curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.rpm.sh | sudo bash sudo yum -y install sysbench sysbench --version
- 数据库压测的测试用例
在数据库里创建一个测试库,叫做
test_db
,同时创建好对应的测试账号,可以叫做test_user
,密码也是test_user
,让这个用户有权限可以访问test_db
。
然后我们将要基于sysbench构建20个测试表,每个表里有100万条数据,接着使用10个并发线程去对这个数据库发起访问,连续访问5分钟,也就是300秒,然后对其进行压力测试。- 基于sysbench构造测试表和测试数据
-db-driver=mysql
:基于mysql的驱动去连接mysql数据库- -
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,sbtest2oltp_read_write
:执行oltp数据库的读写测试-db-ps-mode=disable
:禁止ps模式
sysbench --db-driver=mysql --time=300 --threads=10 --report-interval=1 --mysql-host=127.0.0.1 --mysqlport= 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
参数
- 对数据库进行360度的全方位测试
- 测试数据库的综合读写TPS,使用的是
oltp_read_write
模式 - 测试数据库的只读性能,使用的是
oltp_read_only
模式 - 测试数据库的删除性能,使用的是
oltp_delete
模式: - 测试数据库的更新索引字段的性能,使用的是
oltp_update_index
模式: - 测试数据库的更新非索引字段的性能,使用的是
oltp_update_non_index
模式: - 测试数据库的更新非索引字段的性能,使用的是
oltp_update_non_index
模式: - 测试数据库的插入性能,使用的是
oltp_insert
模式: - 测试数据库的写入性能,使用的是
oltp_write_only
模式: - 执行下面的cleanup命令,清理数据
sysbench --db-driver=mysql --time=300 --threads=10 --report-interval=1 --mysql-host=127.0.0.1 --mysqlport= 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
sysbench --db-driver=mysql --time=300 --threads=10 --report-interval=1 --mysql-host=127.0.0.1 --mysqlport= 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
sysbench --db-driver=mysql --time=300 --threads=10 --report-interval=1 --mysql-host=127.0.0.1 --mysqlport= 3306 --mysql-user=test_user --mysql-password=test_user --mysql-db=test_db --tables=20 -- table_size=1000000 oltp_delete --db-ps-mode=disable run
sysbench --db-driver=mysql --time=300 --threads=10 --report-interval=1 --mysql-host=127.0.0.1 --mysqlport= 3306 --mysql-user=test_user --mysql-password=test_user --mysql-db=test_db --tables=20 -- table_size=1000000 oltp_update_index --db-ps-mode=disable run
sysbench --db-driver=mysql --time=300 --threads=10 --report-interval=1 --mysql-host=127.0.0.1 --mysqlport= 3306 --mysql-user=test_user --mysql-password=test_user --mysql-db=test_db --tables=20 -- table_size=1000000 oltp_update_non_index --db-ps-mode=disable run
sysbench --db-driver=mysql --time=300 --threads=10 --report-interval=1 --mysql-host=127.0.0.1 --mysqlport= 3306 --mysql-user=test_user --mysql-password=test_user --mysql-db=test_db --tables=20 -- table_size=1000000 oltp_update_non_index --db-ps-mode=disable run
sysbench --db-driver=mysql --time=300 --threads=10 --report-interval=1 --mysql-host=127.0.0.1 --mysqlport= 3306 --mysql-user=test_user --mysql-password=test_user --mysql-db=test_db --tables=20 -- table_size=1000000 oltp_insert --db-ps-mode=disable run
sysbench --db-driver=mysql --time=300 --threads=10 --report-interval=1 --mysql-host=127.0.0.1 --mysqlport= 3306 --mysql-user=test_user --mysql-password=test_user --mysql-db=test_db --tables=20 -- table_size=1000000 oltp_write_only --db-ps-mode=disable run
sysbench --db-driver=mysql --time=300 --threads=10 --report-interval=1 --mysql-host=127.0.0.1 --mysqlport= 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
- 压测结果分析
thds: 10
,有10
个线程在压测tps: 380.99
,每秒执行了380.99
个事务qps: 7610.20
,每秒可以执行7610.20
个请求(r/w/o: 5132.99/1155.86/1321.35)
,在每秒7610.20个请求中,有5132.99个请求是读请求,1155.86个请求是写请求,1321.35个请求是其他的请求,就是对QPS进行了拆解lat (ms, 95%): 21.33
,95%的请求的延迟都在21.33毫秒以下err/s: 0.00 reconn/s: 0.00
,每秒有0个请求是失败的,发生了0次网络重连
按照上面的命令,每隔1秒都会输出一次压测报告的,此时每隔一秒会输出类似下面的一段东西:
[ 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
首先说的这是第22s输出的一段压测统计报告,然后是其他的一些统计字段:
在完成压测之后,最后会显示一个总的压测报告:
SQL statistics: queries performed: read: 1480084 // 在300s的压测期间执行了148万多次的读请求 write: 298457 // 在压测期间执行了29万多次的写请求 other: 325436 // 在压测期间执行了30万多次的其他请求 total: 2103977 // 一共执行了210万多次的请求 transactions: 105180( 350.6 per sec. )// 一共执行了10万多个事务,每秒执行350多个事务 queries: 2103977 ( 7013.26 per sec. )//一共执行了210万多次的请求,每秒执行7000+请求 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): min: 4.32 // 请求中延迟最小的是4.32ms avg: 13.42 // 所有请求平均延迟是13.42ms max: 45.56 // 延迟最大的请求是45.56ms 95th percentile: 21.33 // 95%的请求延迟都在21.33ms以内
数据库的压测中,观察机器性能
- 为什么在不停增加线程数量的时候,要密切关注机器性能?
如果一直不停的在压测过程中增加
sysbench
的线程数量,然后数据库此时勉强抗到了每秒5000的QPS了,但是这个时候发现机器的CPU已经满负荷运行了,内存使用率特别高,内存都快要不够了,网络带宽几乎被打满,磁盘IO的等待时间特别长,这个时候说明机器已经到了极致了,再搞下去,机器都快挂了。
这个时候压测出来的5000 QPS是没什么代表性的,因为在生产环境根本不可能让数据库抗下这么高的QPS,因为到这么高的QPS就说明数据库几乎已经快要挂掉了,这是不现实的。- 压测时如何观察机器的CPU负载情况?
top命令监测linux机器性能
top - 15:52:00 up 42:35, 1 user, load average: 0.15, 0.05, 0.01
这行信息是最直观可以看到机器的cpu负载情况的,首先
15:52:00
指的是当前时间,up 42:35
指的是机器已经运行了多长时间,1 user就是说当前机器有1个用户在使用。
最重要的是load average: 0.15, 0.05, 0.01
这行信息,他说的是CPU在1分钟、5分钟、15分钟内的负载情况CPU负载
如果CPU负载是1,那说明4核CPU中有一个核已经被使用的比较繁忙了,另外3个核还是比较空闲一些。
要是CPU负载是1.5,说明有一个核被使用繁忙,另外一个核也在使用,但是没那么繁忙,还有2个核可能还是空闲的。
如果你的CPU负载是4,那说明4核CPU都被跑满了,如果你的CPU负载是6,那说明4核CPU被繁忙的使用还不够处理当前的任务,很多进程可能一直在等待CPU去执行自己的任务。
是如果压测的过程中,发现4核CPU的load average已经基本达到3.5,4了,那么说明几个CPU基本都跑满了,在满负荷运转,那么此时你就不要再继续提高线程的数量和增加数据库的QPS了,否则CPU负载太高是不合理的。
- 观察机器的内存负载情况
在你执行top命令之后,中间我们跳过几行内容,可以看到如下一行内容:
Mem: 33554432k total, 20971520k used, 12268339 free, 307200k buffers
这里说的就是当前机器的内存使用情况,这个其实很简单,明显可以看出来就是总内存大概有32GB,已经使用了20GB左右的
内存,还有10多G的内存是空闲的,然后有大概300MB左右的内存用作OS内核的缓冲区。
对于内存而言,同样是要在压测的过程中紧密的观察,一般来说,如果内存的使用率在
80%
以内,基本都还能接受,在正常范
围内,但是如果你的机器的内存使用率到了70%~80%
了,就说明有点危险了,此时就不要继续增加压测的线程数量和QPS
了,差不多就可以了。- 观察机器的磁盘IO情况
使用
dstat -d
命令-dsk/total - read writ 103k 211k 0 11k
存储的IO吞吐量是每秒钟读取103kb的数据,每秒写入211kb的数据,像这个存储IO吞吐量基本上都不算多的,因为普通的机械硬盘都可以做到每秒钟上百MB的读写数据量。
使用命令:
dstat -r
--io/totalread writ 0.25 31.9 0 253 0 39.0
读IOPS和写IOPS分别是多少,也就是说随机磁盘读取每秒钟多少次,随机磁盘写入每秒钟执行多少次,一般来说,随机磁盘读写每秒在两三百次都是可以承受的。
所以在这里,我们就需要在压测的时候密切观察机器的磁盘IO情况,如果磁盘IO吞吐量已经太高了,都达到极限的每秒上百MB了,或者随机磁盘读写每秒都到极限的两三百次了,此时就不要继续增加线程数量了,否则磁盘IO负载就太高了
- 观察网卡的流量情况
dstat -n
命令-net/totalrecv send 16k 17k
每秒钟网卡接收到流量有多少kb,每秒钟通过网卡发送出去的流量有多少kb,通常来说,如果机器使用的是千兆网卡,那么每秒钟网卡的总流量也就在100MB左右,甚至更低一些。
所以在压测的时候也得观察好网卡的流量情况,如果网卡传输流量已经到了极限值了,那么此时再怎么提高
sysbench
线程数量,数据库的QPS也上不去了,因为这台机器每秒钟无法通过网卡传输更多的数据了。数据库可视化监控平台
基于
Prometheus+Grafana
来搭建生产环境数据库的可视化监控平台Prometheus
其实就是一个监控数据采集和存储系统,他可以利用监控数据采集组件(比如mysql_exporter
)从你指定的MySQL数据库中采集他需要的监控数据,然后他自己有一个时序数据库,他会把采集到的监控数据放入自己的时序数据库中,其实本质就是存储在磁盘文件里采集到了MySQL的监控数据还不够,现在我们还要去看这些数据组成的一些报表,所以此时就需要使用
Grafana
了,Grafana就是一个可视化的监控数据展示系统,他可以把Prometheus
采集到的大量的MySQL监控数据展示成各种精美的报表,让我们可以直观的看到MySQL的监控情况。
其实不光是对数据库监控可以采用Prometheus+Grafana
的组合,对你开发出来的各种Java系统、中间件系统,都可以使用这套组合去进行可视化的监控,无非就是让Prometheus
去采集你的监控数据,然后用Grafana
展示成报表而已安装和启动Prometheus
http://cactifans.hi-www.com/prometheus/
下载到下面两个压缩包,这里
prometheus
就是用来部署监控系统自己的,然后node_exporter
是用来采集MySQL数据库所在机器的CPU、内存、网络、磁盘之类的监控数据的:prometheus-2.1.0.linux-amd64.tar.gz
, node_exporter-0.15.2.linux-amd64.tar.gz
下载第三个压缩包:
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.linuxamd64. tar.gz mkdir /data mkdir /root tar xvf prometheus-2.1.0.linux-amd64.tar -C /data tar xf node_exporter-0.15.2.linux-amd64.tar -C /root tar xf mysqld_exporter-0.10.0.linux-amd64.tar.gz -C /root cd /data mv prometheus-2.1.0.linux-amd64/ prometheus cd /prometheus
接下来修改
prometheus
的配置文件:vi prometheus.yml
,,其实主要是在scrape_configs
下面加入一大段自定义的配置,因为需要去采集MySQL数据库本身和MySQL所在机器的监控数据:scrape_configs: - file_sd_configs: -files: - host.yml job_name: Host metrics_path: /metrics relabel_configs: - source_labels: [__address__] regex: (.*) target_label: instance replacement: $1 - source_labels: [__address__] regex: (.*) target_label: __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
在
/data/prometheus
目录中,去执行启动命令/data/prometheus/prometheus --storage.tsdb.retention=30d &
这里的30d是说你的监控数据保留30天的。启动之后,就可以在浏览器中访问9090端口号去查看
prometheus
的主页了。部署Grafana
下载
grafana-4.6.3.linux-x64.tar.gz
,然后一步一步的执行下面的命令,完成他的启动https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.6.3.linux-x64.tar.gz tar xf grafana-4.6.3.linux-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 Sources
,点击里面的一个按钮是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。
接着执行一系列的命令去安装grafana-dashboard
组件。
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监控仪表盘,等等。添加MySQL所在机器的监控
在MySQL机器上解压缩和启动
node_exporter
,这启动之后是个linux进程,他会自动采集这台linux机器上的CPU、磁盘、内存、网络之类的各种监控数据,其实本质你可以理解为通过我们之前讲解的那些linux命令,就可以采集到一切你想要的linux机器的监控数据。tar xf node_exporter-0.15.2.linux-amd64.tar mv node_exporter-0.15.2.linux-amd64 node_exporter cd node_exporter nohup ./node_exporter &
到这一步为止,我们就在MySQL所在的机器上启动了一个
node_exporter
了,他就会自动采集这台机器的CPU、磁盘、内存、网络的监控数据,但是此时还不够,因为Prometheus
上还没加入对这台机器的监控。
此时我们应该还记得,之前在Prometheus
的yml配置文件中,我们已经定义了一个host监控项,他就是用来监控机器的,他的配置文件是host.yml
,此时我们可以编辑一下这个host.yml
配置文件,加入mysql所在机器的地址就可以了vi host.yml - labels: service: test targets: - 127.0.0.1
接着
Prometheus
就会跟MySQL机器上部署的node_exporter
进行通信,源源不断的获取到这台机器的监控数据,写入自己的
时序数据库中进行存储。接着我们就可以打开Grafana
的页面,此时你就可以看到这台机器的相关性能监控了添加MySQL数据库的监控
接着我们同样需要在MySQL所在机器上再启动一个mysqld_exporter的组件,他负责去采集MySQL数据库自己的一些监控数据,我们看下面的指令就可以了。
tar xf mysqld_exporter-0.10.0.linux-amd64.tar mv mysqld_exporter-0.10.0.linux-amd64 mysqld_exporter
接着需要配置一些环境变量,去设置
mysqld_exporter
要监控的数据库的地址信息,看下面配置了账号、密码以及地址和端口号export DATA_SOURCE_NAME='root:root@(127.0.0.1:3306)/' echo "export DATA_SOURCE_NAME='root:root@(127.0.0.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.perf_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
才能看到对应的报表。vi /data/prometheus/mysql.yml - labels: service: mysql_test targets: - 127.0.0.1
接着我们在Grafana中就可以看到MySQL的各种监控数据了。
RAID存储架构
MySQL数据库底层用磁盘来存储数据,基于内存来提升数据读写性能,然后设计了复杂的数据模型,帮助我们高效的存储和管理数据。
MySQL运行过程中,他需要使用CPU、内存、磁盘和网卡这些硬件,但是不能直接使用,都是通过调用操作系统提供的接口,依托于操作系统来使用和运行的,然后linux操作系统负责操作底层的硬件。
很多数据库部署在机器上的时候,存储都是搭建的RAID存储架构,RAID就是一个磁盘冗余阵列, 用来管理机器里的多块磁盘的一种磁盘阵列技术。
有了RAID这种多磁盘阵列技术之后,可以在一台服务器里加多块磁盘,扩大磁盘存储空间。往磁盘里写数据的时候,通过RAID技术可以帮助选择一块磁盘写入,在读取数据的时候,我们也知道从哪块磁盘去读取。
除此之外,RAID技术很重要的一个作用,就是他还可以实现数据冗余机制。同样一份数据,在两块磁盘上都写入的,这样可以让两块磁盘上的数据一样,作为冗余备份,然后当一块磁盘坏掉的时候,可以从另外一块磁盘读取冗余数据出来,这一切都是RAID技术自动管理的。
RAID还可以分成不同的技术方案,比如RAID 0、RAID 1、RAID 0+1、RAID2,等等,一直到RAID 10,很多种不同的多磁盘管理技术方案。
RAID存储架构的电池充放电原理
服务器使用多块磁盘组成的RAID阵列的时候,一般会有一个RAID卡,这个RAID卡是带有一个缓存的,这个缓存不是直接用服务器的主内存的那种模式,它是一种跟内存类似的SDRAM。
然后我们可以把RAID的缓存模式设置为
write back
,这样的话,所有写入到磁盘阵列的数据,先会缓存在RAID卡的缓存里,后续慢慢再写入到磁盘阵列里去,这种写缓冲机制,可以大幅度提升我们的数据库磁盘写的性能。RAID卡一般都配置有自己独立的锂电池或者是电容,如果服务器突然断电,RAID卡自己是基于锂电池来供电运行的,然后它会把缓存里的数据写入到阵列中的磁盘上
去。
但是锂电池是存在性能衰减问题的,所以一般来说锂电池都是要配置定时充放电的,也就是说每隔30天~90天(不同的锂电池厂商是不一样的),就会自动对锂电池充放电一次,这可以延长锂电池的寿命和校准电池容量。
在锂电池充放电的过程中,RAID的缓存级别会从
write back
变成write through
,我们通过RAID写数据的时候,IO就直接写磁盘了,如果写内存的话,性能也就是0.1ms这个级别,但是直接写磁盘,就性能退化10倍到毫秒级 导致数据库服务器的RAID存储定期的性能出现几十倍的抖动,间接导致数据库每隔一段时间就会出现性能几十倍的抖动!RAID 10
所谓的RAID 10,就是RAID 0 + RAID 1组合起来,就是说当时生产环境的服务器部署,我们有6块磁盘组成了一个RAID 10的阵列,那么其实就是每2块磁盘组成一个RAID 1互为镜像的架构,存放的数据是冗余一样的,一共有3组RAID 1,然后对于每一组RAID 1写入数据的时候,是用RAID 0的思路,就是不同组的磁盘的数据是不一样的,但是同一组内的两块磁盘的数据是冗余一致的
于这样的一个使用了RAID 10架构的服务器,他必然内部是有一个锂电池的,然后这个锂电池的厂商设定的默认是30天进行一次充放电,每次锂电池充放电就会导致RAID写入时不经过缓存,性能会急剧下降,所以我们发现线上数据库每隔30天就会有一次剧烈性能抖动,数据库性能下降了10倍。
充放电导致线上数据库的性能定期抖动
使用linux命令查看了RAID硬件设备的日志,这个具体什么命令不说了,因为你用不同的厂商的RAID设备,这个命令实际上是不一样的,发现RAID就是每隔30天有一次充放电的日志,所以就是由于这个定期的充放电导致了线上数据库的性能定期抖动!
对于RAID锂电池充放电问题导致的存储性能抖动,一般有三种解决方案:
- 给RAID卡把锂电池换成电容,电容是不用频繁充放电的,不会导致充放电的性能抖动,还有就是电容可以支持透明充放电,就是自动检查电量,自动进行充电,不会说在充放电的时候让写IO直接走磁盘,但是更换电容很麻烦,而且电容比较容易老化,这个其实一般不常用
- 手动充放电,这个比较常用,包括一些大家知道的顶尖互联网大厂的数据库服务器的RAID就是用了这个方案避免性能抖动,就是关闭RAID自动充放电,然后写一个脚本,脚本每隔一段时间自动在晚上凌晨的业务低峰时期,脚本手动触发充放电,这样可以避免业务高峰期的时候RAID自动充放电引起性能抖动
- 充放电的时候不要关闭
write back
,就是设置一下,锂电池充放电的时候不要把缓存级别从write back
修改为write through
,这个也是可以做到的,可以和第二个策略配合起来使用