天天看点

mysql+mycat压力测试一例

前言

有很多人担心生产系统上新东西的程序怕压力跟不上和稳定性不行,是的,大家都怕,所以领导要求做一次压力测试,我个人也觉得是有必要的.

如果按原理来说,mycat如果不做分片,纯粹只是代理的话,他所做的事情,其实更多的是数据转发,而这个转发能力,当然就是看他能有多强.

既然理论上转发能力很强,那么是不是就可以忽略不计呢,当然不是,所以需要用直连mysql的测试数据来做对比.

测试前准备

服务器配置为32核cpu(虚拟化后的数值,算上超线程),120G内存,16000iops的存储设备,具体分配情况:

10.21.128.156:mycat1.6.5,sysbench0.4

10.21.128.157:mysql5.7.20主库

10.21.128.158:mysql5.7.20从库1

10.21.128.159:mysql5.7.20从库2

简单说明拓扑关系:

第一步当然是安装好mysql,这里就不详细介绍怎么安装了,但是,my.cnf的参数是有些变化的,主要原因是要适应高并发压测环境,不然就被参数限制,然后程序退出.当然了,如果你想尽量模拟线上环境,那这些限制你得思考在内,更改就需要谨慎一些,我这里只是给出一例来参考.

<code>#首先,就是要把系统的连接数和打开文件数搞起来</code>

<code>ulimit</code> <code>-SHn 65535</code>

<code>#想永久生效就要这样</code>

<code>echo</code> <code>"</code>

<code>* soft nofile 65535</code>

<code>* hard nofile 65535</code>

<code>root soft nofile 65535</code>

<code>root hard nofile 65535</code>

<code>" &gt;&gt; </code><code>/etc/security/limits</code><code>.conf</code>

然后,更改mysql配置文件参数,其他buffer_pool什么的就不列出来了,请各自看情况设置,这里只说明涉及压测相关的参数.

<code>#打开配置文件</code>

<code>vim </code><code>/usr/local/mysql/my</code><code>.cnf</code>

<code>#其他参数我们暂时忽略,只看这些关乎压测的参数</code>

<code>[mysqld]</code>

<code>#每台机都需要不一样的ID,主从环境下</code>

<code>server-</code><code>id</code> <code>= 128157</code>

<code>#全局最大打开文件数,不可以超过系统设定的最大文件数,不然无效</code>

<code>open_files_limit = 65530</code>

<code>#innodb引擎限制一次打开表空间文件.ibd的文件描述符数量</code>

<code>innodb_open_files = 65530</code>

<code>#允许存到缓存里的表的句柄数量,压测自然要打开很多表,设大一点就对了,受系统limit和上面两个参数一起限制</code>

<code>table_open_cache = 65000</code>

<code>#实例可用最大连接数,必须设置足够大,不然连接数超出限制,测着报错不通就麻烦了,最少也比你实际要用到的多三分一</code>

<code>max_connections=30000</code>

<code>#最大每用户连接数,限制每个用户最大连接数,一定要比上面少一点</code>

<code>max_user_connections=20000</code>

<code>#最大数据包大小,做压测改大一点还是有必要</code>

<code>max_allowed_packet = 128M</code>

<code>#限制了同一时间在mysqld上所有session中prepared 语句的上限,平时不用理这个参数,但是压测就需要调大一点</code>

<code>max_prepared_stmt_count=100000</code>

其他参数我就不一一列举,自己看情况来设置就行,然后,重启生效待命.

软件安装

先说说压测工具的选择问题,在MySQL协议上Mycat不兼容tpcc,所以放弃tpcc。然后sysbench1.0对mycat兼容也比较欠佳,不明原因压测失败次数多,所以也只能放弃.

最后选定sysbench0.4和0.5来使用,可以顺利测出结果,而且从压测的原理来说也比较客观.

所以,我们需要安装的软件有:mysql(默认已装),mycat,sysbench0.4或0.5

mysql怎么安装和授权什么的,这里就不细说了,还请各位自己搭建好,配置文件就上面提到的要加上一下.

安装mycat:

1)搭建jdk环境

由于mycat是java程序,所以需要安装JDK,版本至少要jdk1.6以上,

<code>#下载好安装包后,解压创建软连接</code>

<code>tar</code> <code>xf jdk-8u144-linux-x64.</code><code>tar</code><code>.gz</code>

<code>mv</code> <code>jdk1.8.0_144/ </code><code>/usr/local/</code>

<code>ln</code> <code>-sf jdk1.8.0_144/ jdk</code>

<code>#创建环境变量</code>

<code>vim </code><code>/etc/profile</code><code>.d</code><code>/java</code><code>.sh</code>

<code>export</code> <code>JAVA_HOME=</code><code>/usr/local/jdk</code>

<code>export</code> <code>JRE_HOME=</code><code>/usr/local/jdk/jre</code>

<code>export</code> <code>CLASSPATH=.:$JAVA_HOME</code><code>/lib/dt</code><code>.jar:$JAVA_HOME</code><code>/lib/tools</code><code>.jar:$JRE_HOME</code><code>/lib</code>

<code>export</code> <code>PATH=$PATH:$JAVA_HOME</code><code>/bin</code>

<code>#重载环境变量</code>

<code>source</code> <code>/etc/profile</code>

<code>#验证安装</code>

<code>java -version</code>

<code>java version </code><code>"1.8.0_144"</code>

<code>Java(TM) SE Runtime Environment (build 1.8.0_144-b01)</code>

<code>Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)</code>

安装完成,可以使用.

2)配置mycat

了解java程序的同学应该知道,java程序只要配置好,直接启动就能用,所以没有安装的概念,直接将mycat配置做好就可以用.

因为sysbench的测试没有涉及分库分表,所以mycat只需要设置server.xml和schema.xml即可,具体mycat存放路径没规定,我将他放在/usr/local了.

    ps:有意向做分库分表压测的就要把数据做一些处理,按照sysbench原理来说是可行的,他测试的表的数量是可控的,你把带数字编号的表通过逻辑库处理可以集合成一个新的逻辑表.

先看基本环境设置server.xml,大部分设置都可以不动,注意添加修改的是&lt;system&gt;标签下面配置就可以了,每创建一个用户&lt;user&gt;是要另外起标签:

vim /usr/local/mycat/conf/server.xml

<code>&lt;</code><code>system</code><code>&gt;</code>

<code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"idleTimeout"</code><code>&gt;2880000&lt;/</code><code>property</code><code>&gt; </code><code>&lt;!--设置超时时间为28800秒 --&gt;</code>

<code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"maxPacketSize"</code><code>&gt;134217728&lt;/</code><code>property</code><code>&gt; </code><code>&lt;!--设置最大网络包为128M --&gt;</code>

<code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"charset"</code><code>&gt;utf8&lt;/</code><code>property</code><code>&gt; </code><code>&lt;!--设置默认字符集为utf8 --&gt;</code>

<code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"txIsolation"</code><code>&gt;2&lt;/</code><code>property</code><code>&gt; </code><code>&lt;!--设置隔离级别为RC --&gt;</code>

<code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"sqlExecuteTimeout"</code><code>&gt;600&lt;/</code><code>property</code><code>&gt; </code><code>&lt;!--设置sql执行的超时时间为600秒 --&gt;</code>

<code>&lt;/</code><code>system</code><code>&gt;</code>

<code>&lt;!--下面是设置mycat的用户名/密码和权限控制,和mysql的用户名密码无关 --&gt;</code>

<code>&lt;</code><code>user</code> <code>name</code><code>=</code><code>"root"</code> <code>defaultAccount</code><code>=</code><code>"true"</code><code>&gt;</code>

<code>                </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"password"</code><code>&gt;mycat123&lt;/</code><code>property</code><code>&gt;</code>

<code>                </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"schemas"</code><code>&gt;sbtest,testppp&lt;/</code><code>property</code><code>&gt;</code>

<code>&lt;/</code><code>user</code><code>&gt;</code>

<code>&lt;</code><code>user</code> <code>name</code><code>=</code><code>"sysbench"</code><code>&gt;</code>

<code>                </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"password"</code><code>&gt;sb123&lt;/</code><code>property</code><code>&gt;</code>

<code>                </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"schemas"</code><code>&gt;sbtest&lt;/</code><code>property</code><code>&gt;</code>

<code>        </code><code>&lt;/</code><code>user</code><code>&gt;</code>

<code>&lt;</code><code>user</code> <code>name</code><code>=</code><code>"test"</code><code>&gt;</code>

<code>                </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"password"</code><code>&gt;test&lt;/</code><code>property</code><code>&gt;</code>

<code>                </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"readOnly"</code><code>&gt;true&lt;/</code><code>property</code><code>&gt;</code>

<code>                </code><code>&lt;</code><code>privileges</code> <code>check</code><code>=</code><code>"false"</code><code>&gt;</code>

<code>                        </code><code>&lt;</code><code>schema</code> <code>name</code><code>=</code><code>"sbtest"</code> <code>dml</code><code>=</code><code>"0001"</code> <code>&gt;</code>

<code>                        </code><code>&lt;</code><code>table</code> <code>name</code><code>=</code><code>"sbtest11"</code> <code>dml</code><code>=</code><code>"1111"</code><code>&gt;&lt;/</code><code>table</code><code>&gt;</code>

<code>                        </code><code>&lt;/</code><code>schema</code><code>&gt;</code>

<code>                </code><code>&lt;/</code><code>privileges</code><code>&gt;</code>

设置了一些相关压测的项目参数,和创建了三个用户root,sysbench,test.这三个用户和数据库的用户没有关联,是独立的,即使这个用户密码被破解,数据库的密码依然安全.其中root有完全控制权限,sysbench只能控制sbtest库,test也只能控制sbtest库,而且限制了读写权限.

然后设置逻辑库配置文件schema.xml,这里改动比较多,所以直接贴上整个文件:

<code>&lt;?</code><code>xml</code> <code>version</code><code>=</code><code>"1.0"</code><code>?&gt;</code>

<code>&lt;!DOCTYPE mycat:schema SYSTEM "schema.dtd"&gt;</code>

<code>&lt;</code><code>mycat:schema</code> <code>xmlns:mycat</code><code>=</code><code>"http://io.mycat/"</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>schema</code> <code>name</code><code>=</code><code>"sbtest"</code> <code>checkSQLschema</code><code>=</code><code>"false"</code> <code>sqlMaxLimit</code><code>=</code><code>"100"</code> <code>dataNode</code><code>=</code><code>"dn1"</code><code>&gt;</code>

<code>        </code><code>&lt;/</code><code>schema</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>schema</code> <code>name</code><code>=</code><code>"testppp"</code> <code>checkSQLschema</code><code>=</code><code>"false"</code> <code>sqlMaxLimit</code><code>=</code><code>"100"</code> <code>dataNode</code><code>=</code><code>"dn2"</code><code>&gt;</code>

<code>        </code><code>&lt;!-- &lt;dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"</code>

<code>                </code><code>/&gt; --&gt;</code>

<code>        </code><code>&lt;</code><code>dataNode</code> <code>name</code><code>=</code><code>"dn1"</code> <code>dataHost</code><code>=</code><code>"10.21.128.157"</code> <code>database</code><code>=</code><code>"sbtest"</code> <code>/&gt;</code>

<code>        </code><code>&lt;</code><code>dataNode</code> <code>name</code><code>=</code><code>"dn2"</code> <code>dataHost</code><code>=</code><code>"10.21.128.157"</code> <code>database</code><code>=</code><code>"testppp"</code> <code>/&gt;</code>

<code>        </code><code>&lt;!--&lt;dataNode name="dn4" dataHost="sequoiadb1" database="SAMPLE" /&gt;</code>

<code>         </code><code>&lt;dataNode name="jdbc_dn1" dataHost="jdbchost" database="db1" /&gt;</code>

<code>        </code><code>&lt;dataNode       name="jdbc_dn2" dataHost="jdbchost" database="db2" /&gt;</code>

<code>        </code><code>&lt;dataNode name="jdbc_dn3"       dataHost="jdbchost" database="db3" /&gt; --&gt;</code>

<code>        </code><code>&lt;</code><code>dataHost</code> <code>name</code><code>=</code><code>"10.21.128.157"</code> <code>maxCon</code><code>=</code><code>"3000"</code> <code>minCon</code><code>=</code><code>"20"</code> <code>balance</code><code>=</code><code>"0"</code>

<code>                          </code><code>writeType</code><code>=</code><code>"0"</code> <code>dbType</code><code>=</code><code>"mysql"</code> <code>dbDriver</code><code>=</code><code>"native"</code> <code>switchType</code><code>=</code><code>"-1"</code>  <code>slaveThreshold</code><code>=</code><code>"100"</code><code>&gt;</code>

<code>                </code><code>&lt;</code><code>heartbeat</code><code>&gt;select user()&lt;/</code><code>heartbeat</code><code>&gt;</code>

<code>                </code><code>&lt;!-- can have multi write hosts --&gt;</code>

<code>                </code><code>&lt;</code><code>writeHost</code> <code>host</code><code>=</code><code>"hostM1"</code> <code>url</code><code>=</code><code>"10.21.128.157:3306"</code> <code>user</code><code>=</code><code>"root"</code> <code>password</code><code>=</code><code>"128157"</code><code>&gt;</code>

<code>                        </code><code>&lt;!-- can have multi read hosts --&gt;</code>

<code>                        </code><code>&lt;</code><code>readHost</code> <code>host</code><code>=</code><code>"hostS2"</code> <code>url</code><code>=</code><code>"10.21.128.158:3306"</code> <code>user</code><code>=</code><code>"root"</code> <code>password</code><code>=</code><code>"128157"</code> <code>/&gt;</code>

<code>                        </code><code>&lt;</code><code>readHost</code> <code>host</code><code>=</code><code>"hostS3"</code> <code>url</code><code>=</code><code>"10.21.128.159:3306"</code> <code>user</code><code>=</code><code>"root"</code> <code>password</code><code>=</code><code>"128157"</code> <code>/&gt;</code>

<code>                </code><code>&lt;/</code><code>writeHost</code><code>&gt;</code>

<code>                </code><code>&lt;!-- &lt;writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/&gt; --&gt;</code>

<code>        </code><code>&lt;/</code><code>dataHost</code><code>&gt;</code>

<code>&lt;/</code><code>mycat:schema</code><code>&gt;</code>

设置了两个逻辑库sbtest和testppp(和真实库同名,其实可以不同),指向的真实数据库的主从环境dn1和dn2,里面有一主两从三台真实服务器地址,并开启读写分离.但是要注意的是,事务只走主库,所以读写分离最优方案还是由程序做好一点,用中间件做难免就有点鸡肋了,毕竟现在很多开发框架都走事务的.

3) 启动mycat,

<code>#启动mycat</code>

<code>Mycat start</code>

<code>#重启mycat</code>

<code>Mycat restart</code>

<code>#关闭mycat</code>

<code>Mycat stop</code>

<code>#而Mycat默认的连接端口是8066,管理端口是9066,可以在server.xml修改.</code>

<code>ss -ntplu |</code><code>grep</code> <code>java</code>

<code>tcp    LISTEN     0      100      :::9066                 :::*                   </code><code>users</code><code>:((</code><code>"java"</code><code>,pid=115779,fd=168))</code>

<code>tcp    LISTEN     0      100      :::8066                 :::*                   </code><code>users</code><code>:((</code><code>"java"</code><code>,pid=115779,fd=172))</code>

<code>#看到端口起来了,就可以使用了,因为mycat支持mysql原生协议,所以连上8066端口是不会和一般mysql有什么区别,直接就能使用了.</code>

<code>#如果更改了任何配置文件,可以登录进管理端口执行下列命令实时热刷新配置,相当方便.</code>

<code>mysql -uroot -pmycat123 -h10.21.128.156 -P9066 </code>

<code>#更改server的配置可以</code>

<code>Mysql&gt;reload @@config</code>

<code>#如果改了schema的配置,需要这个命令</code>

<code>Mysql&gt;reload @@config_all</code>

加载完成就可以使用新配置了,哪怕是改了登录用户名也能热加载.

搭建sysbench

下载下来后,只要有c运行库就能编译安装,但是要创建mysql库文件的软连接,不然会报错找不到库文件,

<code>#先安装依赖包</code>

<code>yum </code><code>install</code> <code>-y automake libtool</code>

<code>#先创建mysql库文件的软连接,不然编译会报错的</code>

<code>ln</code> <code>-sf </code><code>/usr/local/mysql/lib/libmysql</code><code>*  </code><code>/usr/lib64</code>

<code>ln</code> <code>-sf </code><code>/usr/local/mysql/lib/libmysqlclient</code><code>.so  </code><code>/usr/lib64/libmysqlclient_r</code><code>.so</code>

<code>#然后执行:</code>

<code>tar</code> <code>xf sysbench-0.4.12-1.1.tgz</code>

<code>cd</code> <code>sysbench-0.4.12-1.1</code>

<code>.</code><code>/autogen</code><code>.sh</code>

<code>.</code><code>/configure</code> <code>--with-mysql-includes=</code><code>/usr/local/mysql/include</code> <code>--with-mysql-libs=</code><code>/usr/local/mysql/lib</code>

<code>Make</code>

<code>#如果 make 没有报错,就会在 sysbench 目录下生成二进制命令行工具 sysbench</code>

<code>cd</code> <code>/tmp/sysbench-0</code><code>.4.12-1.1</code><code>/sysbench</code>

<code>ls</code> <code>-l sysbench</code>

<code>-rwxr-xr-x 1 root root 3293186 Sep 21 16:24 sysbench</code>

在此,环境就搭建完毕了.

开始测试

环境准备好了,就开始测试了,测试前要先准备测试数据,需要使用命令来制造出来,要比较长的时间(可能大半天),重点是要关注硬盘空间是否足够:

<code>#避免不必要的错误,直连数据库操作</code>

<code>mysql -uroot -p128157 -h10.21.128.157 -P3306</code>

<code>#创建测试数据库,sysbench默认测试库名是sbtest,也可以指定库名</code>

<code>mysql&gt; create database sbtest;</code>

<code>#进入sysbench程序目录</code>

<code>#开始造数据,</code>

<code>.</code><code>/sysbench</code> <code>--mysql-host=10.21.128.157 --mysql-port=3306 --mysql-user=root --mysql-password=128157 --</code><code>test</code><code>=tests</code><code>/db/oltp</code><code>.lua --oltp_tables_count=15 --oltp-table-size=40000000 --mysql-table-engine=innodb --rand-init=on prepare</code>

<code>#再次强调,时间可能很长,需要耐心等待</code>

参数解析:

--test=tests/db/oltp.lua    表示调用 tests/db/oltp.lua 脚本进行 oltp 模式测试,oltp是啥就不解析了

--oltp_tables_count=15    表示会生成 15 个测试表,数量越多,自然花费时间越长

--oltp-table-size=40000000    表示每个测试表填充数据量为 40000000行 ,数量越多也是越时间长

--rand-init=on    表示每个测试表都是用随机数据来填充的,这样才客观

--mysql-table-engine=innodb    表示表的引擎是innodb

prepare    用于准备测试需要的数据,准备完后执行run来测试,测试完成后如果需要清理数据就用cleanup来清除测试数据

所以这里创造了15个表,里面每个表有4000万行的数据,数据为随机输入,这个数据量大概需要150G硬盘空间,估计可以涵盖大部分情况了吧,当然,你也可以创建更多数据.

然后就可以进行正式测试了,我们先进行测试普通主从架构:

<code>#执行测试命令</code>

<code>.</code><code>/sysbench</code> <code>--mysql-host=10.21.128.157 --mysql-port=3306 --mysql-user=root --mysql-password=128157 --</code><code>test</code><code>=tests</code><code>/db/oltp</code><code>.lua --oltp_tables_count=15 --oltp-table-size=40000000 --mysql-table-engine=innodb --num-threads=1024 --oltp-</code><code>read</code><code>-only=off --report-interval=10 --rand-</code><code>type</code><code>=uniform --max-</code><code>time</code><code>=3600 --max-requests=0 --percentile=99 run &gt;&gt; </code><code>/tmp/sysbench_oltpX_32_20171113-1</code><code>.log</code>

<code>#我们设定的是3600秒,在此之前终止,也就是测试失败,需要分析报错</code>

--num-threads=1024    表示发起1024个并发连接

--oltp-read-only=off    表示不要进行只读测试,也就是会采用读写混合模式测试

--report-interval=10    表示每10秒输出一次测试进度报告

--rand-type=uniform    表示随机类型为固定模式,其他几个可选随机模式:uniform(固定),gaussian(高斯),special(特定的),pareto(帕累托)

--max-time=3600    表示最大执行时长为3600秒,测试将在这个时间后结束

--max-requests=0    表示总请求数为 0,因为上面已经定义了总执行时长,所以总请求数可以设定为 0;也可以只设定总请求数,不设定最大执行时长

--percentile=99    表示设定采样比例,默认是 95%,即丢弃1%的长请求,在剩余的99%里取最大值

根据上面的解析,最后输出到一个文件,毕竟需要记录下来.

然后到mycat代理环境:

<code>.</code><code>/sysbench</code> <code>--mysql-host=10.21.128.156 --mysql-port=8066 --mysql-user=sysbench --mysql-password=sb123 --</code><code>test</code><code>=tests</code><code>/db/oltp</code><code>.lua --oltp_tables_count=15 --oltp-table-size=40000000 --mysql-table-engine=innodb --num-threads=1024 --oltp-</code><code>read</code><code>-only=off --report-interval=10 --rand-</code><code>type</code><code>=uniform --max-</code><code>time</code><code>=3600 --max-requests=40000000 --percentile=99 run &gt;&gt; </code><code>/home/logs/sysbench_oltpX_32_20171113-2</code><code>.log</code>

和上面一样,设定了并发为1024个线程,测试时间为3600秒即1小时,同时也是用到刚才制造出来的15个4000万行的表,设定取值采样平均值为99%的数据,输出到另一个log文件.

阅读测试报告

测试完了,就来看结果了,我们拿其中一个结果来解析说一下:

<code>#忽略掉一些每10秒统计值,只看最后的统计值</code>

<code>vim </code><code>/tmp/sysbench_oltpX_32_20171113-1</code><code>.log</code>

<code>sysbench 0.5:  multi-threaded system evaluation benchmark</code>

<code>Running the </code><code>test</code> <code>with following options:</code>

<code>Number of threads: 1024</code>

<code>Report intermediate results every 10 second(s)</code>

<code>Random number generator seed is 0 and will be ignored</code>

<code>Threads started!</code>

<code>-- 每10秒钟报告一次测试结果,tps、每秒读、每秒写、99%以上的响应时长统计</code>

<code>[  10s] threads: 1024, tps: 364.69, reads</code><code>/s</code><code>: 6324.66, writes</code><code>/s</code><code>: 1765.66, response </code><code>time</code><code>: 4292.46ms (99%)</code>

<code>[  20s] threads: 1024, tps: 475.80, reads</code><code>/s</code><code>: 6037.91, writes</code><code>/s</code><code>: 1640.10, response </code><code>time</code><code>: 4088.05ms (99%)</code>

<code>[  30s] threads: 1024, tps: 439.40, reads</code><code>/s</code><code>: 6349.45, writes</code><code>/s</code><code>: 1808.89, response </code><code>time</code><code>: 3248.44ms (99%)</code>

<code>[  40s] threads: 1024, tps: 487.70, reads</code><code>/s</code><code>: 6438.46, writes</code><code>/s</code><code>: 1879.72, response </code><code>time</code><code>: 4385.98ms (99%)</code>

<code>[  50s] threads: 1024, tps: 395.70, reads</code><code>/s</code><code>: 6498.99, writes</code><code>/s</code><code>: 1849.00, response </code><code>time</code><code>: 3845.88ms (99%)</code>

<code>    </code><code>.</code>

<code>[3560s] threads: 1024, tps: 385.80, reads</code><code>/s</code><code>: 4949.60, writes</code><code>/s</code><code>: 1503.80, response </code><code>time</code><code>: 19827.73ms (99%)</code>

<code>[3570s] threads: 1024, tps: 249.70, reads</code><code>/s</code><code>: 3679.90, writes</code><code>/s</code><code>: 1009.40, response </code><code>time</code><code>: 12016.58ms (99%)</code>

<code>[3580s] threads: 1024, tps: 328.90, reads</code><code>/s</code><code>: 4511.40, writes</code><code>/s</code><code>: 1301.40, response </code><code>time</code><code>: 7419.06ms (99%)</code>

<code>[3590s] threads: 1024, tps: 196.40, reads</code><code>/s</code><code>: 3058.90, writes</code><code>/s</code><code>: 815.30, response </code><code>time</code><code>: 12092.35ms (99%)</code>

<code>[3600s] threads: 1024, tps: 386.60, reads</code><code>/s</code><code>: 5282.74, writes</code><code>/s</code><code>: 1537.78, response </code><code>time</code><code>: 13614.18ms (99%)</code>

<code>OLTP </code><code>test</code> <code>statistics:</code>

<code>    </code><code>queries performed:</code>

<code>        </code><code>read</code><code>:                            16913554        -- 读总数</code>

<code>        </code><code>write:                           4832444        -- 写总数</code>

<code>        </code><code>other:                           2416222        -- 其他操作总数(SELECT、INSERT、UPDATE、DELETE之外的操作,例如COMMIT等)</code>

<code>        </code><code>total:                           24162220        -- 全部总数</code>

<code>    </code><code>transactions:                        1208111 (335.29 per sec.)        -- 总事务数(每秒事务数)</code>

<code>    </code><code>deadlocks:                           0      (0.00 per sec.)        -- 发生死锁总数</code>

<code>    </code><code>read</code><code>/write</code> <code>requests:                 21745998 (6035.29 per sec.)        -- 读写总数(每秒读写次数)</code>

<code>    </code><code>other operations:                    2416222 (670.59 per sec.)        -- 其他操作总数(每秒其他操作次数)</code>

<code>General statistics:</code>

<code>    </code><code>total </code><code>time</code><code>:                          3603.1388s        -- 总耗时</code>

<code>    </code><code>total number of events:              1208111        -- 共发生多少事务数</code>

<code>    </code><code>total </code><code>time</code> <code>taken by event execution: 3688348.3797s        -- 所有事务耗时相加(不考虑并行因素)</code>

<code>    </code><code>response </code><code>time</code><code>:</code>

<code>         </code><code>min:                                 28.41ms        -- 最小耗时</code>

<code>         </code><code>avg:                               3052.99ms        -- 平均耗时</code>

<code>         </code><code>max:                              48667.93ms        -- 最长耗时</code>

<code>         </code><code>approx.  99 percentile:           12708.40ms        -- 超过99%平均耗时</code>

<code>Threads fairness:</code>

<code>    </code><code>events (avg</code><code>/stddev</code><code>):           1179.7959</code><code>/29</code><code>.07</code>

<code>    </code><code>execution </code><code>time</code> <code>(avg</code><code>/stddev</code><code>):   3601.9027</code><code>/1</code><code>.01</code>

所以,每秒事务数Tps达335.29,每秒查询数Qps达6035.29,平均延时3052.99ms.

看完解析,来看结果,下面是直接测主从环境的结果:

<code>        </code><code>read</code><code>:                            19215238</code>

<code>        </code><code>write:                           5490068</code>

<code>        </code><code>other:                           2745034</code>

<code>        </code><code>total:                           27450340</code>

<code>    </code><code>transactions:                        1372517 (381.03 per sec.)</code>

<code>    </code><code>deadlocks:                           0      (0.00 per sec.)</code>

<code>    </code><code>read</code><code>/write</code> <code>requests:                 24705306 (6858.48 per sec.)</code>

<code>    </code><code>other operations:                    2745034 (762.05 per sec.)</code>

<code>    </code><code>total </code><code>time</code><code>:                          3602.1538s</code>

<code>    </code><code>total number of events:              1372517</code>

<code>    </code><code>total </code><code>time</code> <code>taken by event execution: 3688254.2686s</code>

<code>         </code><code>min:                                 18.33ms</code>

<code>         </code><code>avg:                               2687.22ms</code>

<code>         </code><code>max:                              55386.15ms</code>

<code>         </code><code>approx.  99 percentile:           12444.89ms</code>

<code>    </code><code>events (avg</code><code>/stddev</code><code>):           1340.3486</code><code>/33</code><code>.85</code>

<code>    </code><code>execution </code><code>time</code> <code>(avg</code><code>/stddev</code><code>):   3601.8108</code><code>/0</code><code>.44</code>

每秒事务数Tps达381.03,每秒查询数Qps达6858.48,平均耗时2687.22ms,毕竟总数据量是6亿,还是应该要接受.

下面是通过mycat代理的结果

<code>        </code><code>read</code><code>:                            18078326</code>

<code>        </code><code>write:                           5165236</code>

<code>        </code><code>other:                           2582618</code>

<code>        </code><code>total:                           25826180</code>

<code>    </code><code>transactions:                        1291309 (358.40 per sec.)</code>

<code>    </code><code>read</code><code>/write</code> <code>requests:                 23243562 (6451.19 per sec.)</code>

<code>    </code><code>other operations:                    2582618 (716.80 per sec.)</code>

<code>    </code><code>total </code><code>time</code><code>:                          3602.9883s</code>

<code>    </code><code>total number of events:              1291309</code>

<code>    </code><code>total </code><code>time</code> <code>taken by event execution: 3687715.5739s</code>

<code>         </code><code>min:                                 22.45ms</code>

<code>         </code><code>avg:                               2855.80ms</code>

<code>         </code><code>max:                              50326.08ms</code>

<code>         </code><code>approx.  99 percentile:           13264.21ms</code>

<code>    </code><code>events (avg</code><code>/stddev</code><code>):           1261.0439</code><code>/33</code><code>.56</code>

<code>    </code><code>execution </code><code>time</code> <code>(avg</code><code>/stddev</code><code>):   3601.2847</code><code>/0</code><code>.96</code>

每秒事务数Tps达358.40,每秒查询数Qps达6451.19,平均耗时2855.80ms,同样是总数据量6亿.

从结果对比计算,使用mycat后,tps,qps,和耗时都损耗了6%-7%,在我个人看来还是可以接受,因为使用了mycat做代理层,可以很方便的管理后端数据库,任何切换都可以手动来秒切,使用上触发脚本后就是一个HA框架了.

题外说明

测试结果示例说明的例子,其实是加上了高可用keepalived的测试结果,每秒事务数Tps达335.29,每秒查询数Qps达6035.29,平均延时3052.99ms,总的来说算好,也还只是比纯mysql主从损耗10%范围内,可以接受,毕竟可用性高了很多,而且后续压力增大也可以随时增加mycat数量来填补.

功能多了,机器也要多了一些,需要特别说明的是,因为涉及网络数据包转发关系,keepalived和后端mycat不能在同一台服务器,所以就必须独立开来.

10.21.128.208:keepalived主

10.21.128.209:keepalived备

10.21.128.200:mycat1

10.21.128.201:mycat2

10.21.128.199:vip

需要多搭建一个mycat,不过这里就不细说了,直接复制一份配置到其他机器再启动就ok了.

然后搭建一套keepalived集群:

大多数情况下,大伙使用keepalived只做HA功能,而LVS功能则交给其他软件实现.但是实际上keepalived+ipvsadm既能实现HA功能,也能实现LVS功能,非常方便.

我们在10.21.128.208和10.21.128.209上安装keepalived和修改配置.

个人不想纠结功能和版本问题,而这些也是比较常态功能性的软件,绝大部分yum源都配备,所以直接用yum安装就很方便,有额外兴趣的可以慢慢编译

<code>#安装需要的软件</code>

<code>yum </code><code>install</code> <code>-y keepalived ipvsadm nc </code><code>rsync</code> <code>telnet tcpdump wget</code>

安装很快,我们直接看配置,yum安装默认的配置文件在/etc/keepalived/

<code>#修改配置文件,注意注释位置</code>

<code>! Configuration File </code><code>for</code> <code>keepalived</code>

<code>#全局配置</code>

<code>global_defs {</code>

<code>   </code><code>notification_email {</code>

<code>        </code><code>root@localhost    </code><code>#定义收件人邮件地址</code>

<code>   </code><code>}</code>

<code>   </code><code>notification_email_from root@localhost    </code><code>#定义发件人</code>

<code>   </code><code>smtp_server 127.0.0.1    </code><code>#如果要使用第三方smtp服务器,在现实中几乎没有意义(需要验证的原因),设为本地就可以了</code>

<code>   </code><code>smtp_connect_timeout 30    </code><code>#smtp超时时间</code>

<code>   </code><code>router_id LVS_HA_MYCAT1    </code><code>#此服务器keepalived的ID,随便改,注意不同服务器不一样就行</code>

<code>}</code>

<code>#vrrp配置(HA配置)</code>

<code>vrrp_instance VI_1 {    </code><code>#定义虚拟路由,VI_1 为虚拟路由的标示符,自己定义名称</code>

<code>    </code><code>state MASTER    </code><code>#指定当前节点为主节点 备用节点上设置为BACKUP即可 </code>

<code>    </code><code>interface eth0    </code><code>#绑定虚拟IP的网络接口,注意内外网</code>

<code>    </code><code>virtual_router_id 19    </code><code>#VRRP组名,两个节点的设置必须一样,以指明各个节点属于同一VRRP组,0-255随便你用,这个ID也是虚拟MAC最后一段的来源</code>

<code>    </code><code>priority 100    </code><code>#初始优先级,取值1-254之间,主节点一定要最大,其他从节点则看情况减少</code>

<code>    </code><code>advert_int 1    </code><code>#组播信息发送间隔,两个节点设置必须一样</code>

<code>    </code><code>authentication {    </code><code>#设置验证信息,两个节点必须一致</code>

<code>        </code><code>auth_type PASS</code>

<code>        </code><code>auth_pass 199200</code>

<code>    </code><code>}</code>

<code>    </code><code>virtual_ipaddress {</code>

<code>        </code><code>10.21.128.199    </code><code>#指定VIP,两个节点设置必须一样,虚拟ip最好和真实ip在同一网段。</code>

<code>#负载均衡配置(LVS配置)</code>

<code>virtual_server 10.21.128.199 8066  {    </code><code>#指定VIP和端口,vip就是上面设置那个</code>

<code>    </code><code>delay_loop 6    </code><code>#延迟多少个周期再启动服务,做服务检测</code>

<code>    </code><code>lb_algo rr    </code><code>#负载均衡调度算法</code>

<code>    </code><code>lb_kind DR    </code><code>#负载均衡类型选择,可选DR|NAT|TUN,DR性能比较高</code>

<code>    </code><code>nat_mask 255.255.255.0    </code><code>#vip的掩码</code>

<code>    </code><code>persistence_timeout 0    </code><code>#会话保持时间,一定时间之内用户无响应则下一次用户请求时需重新路由,一般设为0,不需要.</code>

<code>    </code><code>protocol TCP    </code><code>#使用的协议,一般就TCP</code>

<code>    </code><code>real_server 10.21.128.200 8066  {    </code><code>#定义后端realserver的真实服务器属性,ip和端口</code>

<code>        </code><code>weight 1    </code><code>#负载均衡权重,数值越大,就负担更多连接</code>

<code>        </code><code>MISC_CHECK {    </code><code>#定义心跳检测的方法,因为不是web,而且用tcp_check健康检测后面的mycat会报错,所以需要misc_check的方式做心跳检测</code>

<code>        </code><code>misc_path</code><code>"/etc/keepalived/shell/check_mycat_status.sh 10.21.128.200 8066"</code>    <code>#自定义心跳检测shell脚本的路径、检测的服务器ip、检测的端口。(引号必须要)</code>

<code>        </code><code>misc_timeout 3    </code><code>#脚本执行超时时间</code>

<code>        </code><code>}</code>

<code>    </code><code>real_server 10.21.128.201 8066 {    </code><code>#同上</code>

<code>        </code><code>weight 2</code>

<code>        </code><code>MISC_CHECK {</code>

<code>        </code><code>misc_path</code><code>"/etc/keepalived/shell/check_mycat_status.sh 10.21.128.201 8066"</code>

<code>        </code><code>misc_timeout 3</code>

<code>    </code><code>real_server 10.21.128.156 8066 {    </code><code>#同上</code>

<code>        </code><code>misc_path</code><code>"/etc/keepalived/shell/check_mycat_status.sh 10.21.128.156 8066"</code>

里面涉及一个检测心跳的脚本,如下所示:

<code>cat</code> <code>/etc/keepalived/shell/check_mycat_status</code><code>.sh</code>

<code>#!/bin/bash</code>

<code>result=`nc -</code><code>v</code> <code>-z $1 $2`</code>

<code>flag=</code><code>"succeeded"</code>

<code>if</code> <code>[[ $result =~ $flag ]]</code>

<code>then</code>

<code>         </code><code>exit</code> <code>0</code>

<code>else</code><code>;</code>

<code>         </code><code>exit</code> <code>1</code>

<code>fi</code>

<code>#记得要给该脚本赋予执行权限:</code>

<code>chmod</code> <code>755 check_mycat_status.sh</code>

意思很简单,就是通过nc命令检测真实服务器的mycat的端口通不通,如果不通的话,keepalived就把这个mycat剔除出集群.

然后,在后端真实服务器上需要做一个操作,绑定vip创建ipvs规则:

在10.21.128.200,10.21.128.201,10.21.128.156上创建并执行下面的脚本,

<code>#创建规则脚本</code>

<code>vim </code><code>/shell/realserver</code> 

<code>SNS_VIP=10.21.128.199</code>

<code>#/etc/rc.d/init.d/functions</code>

<code>case</code> <code>"$1"</code> <code>in</code>

<code>start)</code>

<code>        </code><code>ifconfig</code> <code>lo:0 $SNS_VIP broadcast $SNS_VIP netmask 255.255.255.255 up</code>

<code>        </code><code>sleep</code> <code>5</code>

<code>        </code><code>/sbin/route</code> <code>add ${SNS_VIP}</code><code>/32</code> <code>dev lo:0</code>

<code>        </code><code>echo</code> <code>"1"</code><code>&gt;</code><code>/proc/sys/net/ipv4/conf/lo/arp_ignore</code>

<code>        </code><code>echo</code> <code>"2"</code><code>&gt;</code><code>/proc/sys/net/ipv4/conf/lo/arp_announce</code>

<code>        </code><code>echo</code> <code>"1"</code> <code>&gt;</code><code>/proc/sys/net/ipv4/conf/all/arp_ignore</code>

<code>        </code><code>echo</code> <code>"2"</code><code>&gt;</code><code>/proc/sys/net/ipv4/conf/all/arp_announce</code>

<code>        </code><code>sysctl -p &gt;</code><code>/dev/null</code> <code>2&gt;&amp;1</code>

<code>        </code><code>echo</code> <code>"RealServer Start OK"</code>

<code>;;</code>

<code>stop)</code>

<code>        </code><code>ifconfig</code> <code>lo:0 down</code>

<code>        </code><code>/sbin/route</code> <code>del -net $SNS_VIP netmask 255.255.255.255 dev lo:0</code>

<code>        </code><code>echo</code> <code>"0"</code> <code>&gt;</code><code>/proc/sys/net/ipv4/conf/lo/arp_ignore</code>

<code>        </code><code>echo</code> <code>"0"</code><code>&gt;</code><code>/proc/sys/net/ipv4/conf/lo/arp_announce</code>

<code>        </code><code>echo</code> <code>"0"</code><code>&gt;</code><code>/proc/sys/net/ipv4/conf/all/arp_ignore</code>

<code>        </code><code>echo</code> <code>"0"</code><code>&gt;</code><code>/proc/sys/net/ipv4/conf/all/arp_announce</code>

<code>        </code><code>echo</code> <code>"RealServer Stoped"</code>

<code>restart)</code>

<code>/sbin/route</code> <code>del -net $SNS_VIP netmask 255.255.255.255 dev lo:0</code>

<code>/sbin/route</code> <code>add ${SNS_VIP}</code><code>/32</code> <code>dev lo:0</code>

<code>*)</code>

<code>        </code><code>echo</code> <code>"Usage: $0 {start|stop|restart}"</code>

<code>        </code><code>exit</code> <code>1</code>

<code>esac</code>

<code>exit</code> <code>0</code>

<code>#赋予执行权限</code>

<code>chmod</code> <code>755 </code><code>/shell/realserver</code>

<code>#执行一下</code>

<code>/shell/realserver</code> <code>start</code>

<code>#然后查看下网卡状态</code>

<code>ifconfig</code> <code>lo:0</code>

<code>lo:0      Link encap:Local Loopback  </code>

<code>          </code><code>inet addr:10.21.128.199  Mask:255.255.255.255</code>

<code>          </code><code>UP LOOPBACK RUNNING  MTU:16436  Metric:1</code>

需要注意,每次服务器重启都必须启动这个绑定,不然lvs就不生效了,例如把他放到/etc/rc.d/rc.local

<code>echo</code> <code>"/shell/realserver start"</code> <code>&gt;&gt;</code><code>/etc/rc</code><code>.d</code><code>/rc</code><code>.</code><code>local</code>

万事俱备,只欠东风,那就启动keepalived吧.

在10.21.128.208和10.21.128.209上执行

<code>#因为是yum安装,所以有linux服务</code>

<code>/etc/init</code><code>.d</code><code>/keepalived</code> <code>start</code>

<code>Starting keepalived:                                       [  OK  ]</code>

<code>#看看vip,因为主是208,所以vip只会在208出现,除非208挂了</code>

<code>ip addr </code>

<code>1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 16436 qdisc noqueue state UNKNOWN </code>

<code>    </code><code>link</code><code>/loopback</code> <code>00:00:00:00:00:00 brd 00:00:00:00:00:00</code>

<code>    </code><code>inet 127.0.0.1</code><code>/8</code> <code>scope host lo</code>

<code>    </code><code>inet6 ::1</code><code>/128</code> <code>scope host </code>

<code>       </code><code>valid_lft forever preferred_lft forever</code>

<code>2: eth0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc pfifo_fast state UP qlen 1000</code>

<code>    </code><code>link</code><code>/ether</code> <code>22:87:35:77:ef:38 brd ff:ff:ff:ff:ff:ff</code>

<code>    </code><code>inet 10.21.128.208</code><code>/24</code> <code>brd 10.21.128.255 scope global eth0</code>

<code>    </code><code>inet 10.21.128.199</code><code>/32</code> <code>scope global eth0    </code><code>#-------VIP在这</code>

<code>    </code><code>inet6 fe80::2087:35ff:fe77:ef38</code><code>/64</code> <code>scope link </code>

<code>#然后看看当前LVS状态</code>

<code>ipvsadm -L</code>

<code>IP Virtual Server version 1.2.1 (size=4096)</code>

<code>Prot LocalAddress:Port Scheduler Flags</code>

<code>  </code><code>-&gt; RemoteAddress:Port           Forward Weight ActiveConn InActConn</code>

<code>TCP  10.21.128.199:8066 rr</code>

<code>  </code><code>-&gt; 10.21.128.156:8066           Route   2      0          0         </code>

<code>  </code><code>-&gt; 10.21.128.200:8066           Route   1      0          0         </code>

<code>  </code><code>-&gt; 10.21.128.201:8066           Route   2      0          0</code>

一切都正常,那么,我们可以压测了.

<code>#和之前命令差不多,只是ip不一样</code>

<code>.</code><code>/sysbench</code> <code>--mysql-host=10.21.128.199 --mysql-port=8066 --mysql-user=sysbench --mysql-password=sb123 --</code><code>test</code><code>=tests</code><code>/db/oltp</code><code>.lua --oltp_tables_count=15 --oltp-table-size=40000000 --mysql-table-engine=innodb --num-threads=1024 --oltp-</code><code>read</code><code>-only=off  --report-interval=10 --rand-</code><code>type</code><code>=uniform --max-</code><code>time</code><code>=3600 --max-requests=40000000 --percentile=99 run &gt;&gt; </code><code>/home/logs/sysbench_oltpX_32_20171128-3</code><code>.log</code>

我们来看看lvs状态

<code>#查看链接状态</code>

<code>  </code><code>-&gt; 10.21.128.156:8066           Route   2      341        340       </code>

<code>  </code><code>-&gt; 10.21.128.200:8066           Route   1      341        340       </code>

<code>  </code><code>-&gt; 10.21.128.201:8066           Route   2      342        340       </code>

<code>#加上另一个参数</code>

<code>ipvsadm -L --stats</code>

<code>Prot LocalAddress:Port               Conns   InPkts  OutPkts  InBytes OutBytes</code>

<code>  </code><code>-&gt; RemoteAddress:Port</code>

<code>TCP  10.21.128.199:8066               2045  3658545        0  326481K        0</code>

<code>  </code><code>-&gt; 10.21.128.156:8066                682  1131691        0  114278K        0</code>

<code>  </code><code>-&gt; 10.21.128.200:8066                681  1266405        0  106344K        0</code>

<code>  </code><code>-&gt; 10.21.128.201:8066                682  1260449        0  105858K        0</code>

为什么会没有out?是不是很奇怪?因为我们负载均衡类型选择的是DR模式,这个模式的特点就是当客户端和真实服务器建立链接后,真实服务器会直接把数据发送给客户端,不再需要keepalived来中转,所以就没有out的流量了,也就是为什么说效率就更高的原因了.

然后来看看压测的结果如何?

<code>        </code><code>read</code><code>:                            17043978</code>

<code>        </code><code>write:                           4869708</code>

<code>        </code><code>other:                           2434854</code>

<code>        </code><code>total:                           24348540</code>

<code>    </code><code>transactions:                        1217427 (338.00 per sec.)</code>

<code>    </code><code>read</code><code>/write</code> <code>requests:                 21913686 (6084.05 per sec.)</code>

<code>    </code><code>other operations:                    2434854 (676.01 per sec.)</code>

<code>    </code><code>total </code><code>time</code><code>:                          3601.8265s</code>

<code>    </code><code>total number of events:              1217427</code>

<code>    </code><code>total </code><code>time</code> <code>taken by event execution: 3687506.3802s</code>

<code>         </code><code>min:                                 30.17ms</code>

<code>         </code><code>avg:                               3028.93ms</code>

<code>         </code><code>max:                              75102.41ms</code>

<code>         </code><code>approx.  99 percentile:           13757.56ms</code>

<code>    </code><code>events (avg</code><code>/stddev</code><code>):           1188.8936</code><code>/33</code><code>.40</code>

<code>    </code><code>execution </code><code>time</code> <code>(avg</code><code>/stddev</code><code>):   3601.0804</code><code>/0</code><code>.40</code>

每秒事务数Tps达338.00,每秒查询数Qps达6084.05,平均延时3028.93ms,和一开始相差无几,基本符合现实.

     本文转自arthur376 51CTO博客,原文链接:http://blog.51cto.com/arthur376/2045596,如需转载请自行联系原作者