项目过程分解

通过本次消息中间件系统的全程参与,对于软件项目从无到有的生产过程有了较深入的了解,尤其对于架构设计的决策过程和依据,有了全面的认识。整个项目过程从无到有依次可以分解为九个过程,下面一一道来。

(一)背景分析

主要是要分析清楚三个问题:

  1. 这个项目的诉求是在什么业务现状下产生的?
  2. 最终要解决什么问题?
  3. 有什么价值?

为什么要做背景分析呢?

  1. 更透彻的分析需求
    所有的需求分析过程都要围绕这上述三个问题来,不能偏离。
  2. 更主动的做事;
    当你做一件事情的时候,如果不理解为什么要做,只是单纯的做事的时候,其实是缺少主动性的
  3. 获得上级的支持。
    有了上级的资源(人力、时间)支持,项目能够更加的顺利。

(二)需求收集和分析

需求分为两类:

  1. 功能性需求
    功能性需求一般是项目组内分析得出的该系统需要具备的一些必备特性。对于消息中间件系统来说,如:发布消息、拉取消息、订阅……
  2. 业务性需求
    业务性需求一般是从潜在的系统用户(周边项目组)那收集到的一些业务特性和使用习惯,针对这些业务特性和使用习惯,分析我们的系统是否能满足。对于消息中间件系统来说,如:业务系统习惯主动推送消息,不希望拉取消息;业务系统熟悉MySQL,希望底层存储使用MySQL;业务系统现有方案的数据格式是JSON,不希望换数据格式。

对于业务性的需求,我们要有所区分和取舍,不一定所有的业务特性都能够同时满足。

(三)业界方案研究

基于背景分析和需求收集分析,收集类似的实现或方案。通过对这些实现或方案的研究,了解其关键特性和主要适用场景。最终决定是自研,还是在某个业界实现上做二次定制开发。

(四)架构设计

架构设计就是是一系列的系统关键环节的技术决策。如:

  • 通信协议
    通信协议使用TCP还是HTTP?
    传输格式采用JSON还是XML?
  • 持久化
    数据存储使用MySQL、SQLite还是Redis?
    数据存储是否需要支持多种持久化方案?
  • 交互方式
    客户端和服务端采用同步交互还异步交互?
    采用消息推送还是消息拉取模式?
  • 日志方案
    是否记录Binlog?
    Binlog是否实现主从同步?

每一个架构设计决策之间是互相影响甚至可能是互斥的关系,这个时候要有取舍,取舍的标准就是先看对与错,后看好与坏。对与错取决于方案的功能属性,好与坏取决于方案的质量属性。
方案的功能属性是指其基本功能点;而方案的质量属性是指:

  1. 性能
  2. 成本(硬件成本)
  3. 可扩展性/可伸缩性
  4. 可靠性
  5. 复杂性
  6. 运维
  7. 其他 如:合规性、安全性……

很多架构设计决策在后续的项目环节要进行验证和实现,经过进一步的验证和分析,很可能推翻之前的决策。

(五)需求管理

分析需求的优先级和依赖关系,规划版本,确定时间点。最好使用需求管理工具,便于跟踪。

(六)功能设计和开发

理清楚各个功能之间的逻辑关系,设计功能的关键逻辑,编写代码实现,最后自测验证。
对外提供的API要特别注意其易用性,对外暴露的越少越好,否则内部变动很容易影响使用方。

(七)功能测试

(八)性能测试

要特别重视性能测试,通过性能测试能够发现很多我们程序的关键Bug。

(九)资料编写

包括系统设计的关键决策过程、开发指南、部署指南、配置说明以及技术沉淀总结。
资料编写的过程也是重新审视整个项目的过程。

改进和提升

本次项目开发过程采用敏捷流程,从实践效果来看,总体效果不错。

  1. 简短的项目晨会,却能够很清晰的反映项目组成员的工作进度和遇到的难题,及早暴露问题。
  2. 每周一个冲刺版本,这样小版本迭代能够快速试错,也保证了开发和测试的同步进行,提升了效率。
  3. 需求依赖分析和优先级分类,能够保证我们开发出来的版本总是一个可用的版本。
  4. 每个冲刺版本的代码Review和评审会议效果非常好,不但能够让其他项目组成员熟悉代码,关键时刻快速补位;而且能够避免由于对架构设计的理解不一致,导致功能逻辑问题。
  5. 发布版本的FMEA评估效果也非常好,能够发现很多异常场景下的逻辑漏洞,对于提升版本质量很有帮助。

改进点:

  1. 对于每次项目会议,最好都有专人负责会议记录,避免一些关键决策过程遗失。
  2. 项目组最好都是用统一的代码模板,这样看代码效率能提升不少。
  3. 对于废弃的代码,坚决删除,避免保留和注释,这样能保证代码的清晰。
  4. 在项目设计和开发之前,就要做好框架的知识储备,否则很容易因为不熟悉开发框架而延迟开发进度。本次项目就因为对于Netty没有提前熟悉,导致SDK第一个冲刺版本延迟。
  5. 在设计和开发过程中,对于一些关键决策最好和项目组其他成员讨论评审一下,否则很容易返工。
  6. 自测联调时,不能只关注是否调用成功,还需要检查数据有效性和业务逻辑性,否则会浪费测试人员时间,导致重新打包。
  7. 性能测试要做好测试计划,提前熟悉测试工具,避免做很多无用测试。
  8. 项目管理工具UAPD最好能够提供需求任务列表,能够让开发人员一眼看到自己负责的任务和完成时间。
  9. 对外API在设计接口时,最好先和业务使用方进行沟通,尽量满足其使用场景;如果实在无法满足某个场景,可以采用高层接口和底层接口相结合的方式,即:高层接口更加便利,但是只满足部分场景;底层接口能够支持更加灵活的定制,使用起来相对麻烦一点。

##(一)成员变量

1
2
3
4
5
6
7
8
9
public class DataServerHandler extends SimpleChannelHandler {
// 成员变量
private boolean loggedIn;

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
...
}
}

  • 如果只是想在当前连接内共享数据,那么需要针对不同的Channel创建不同的ChannelHandler实例,避免共享范围扩大至所有连接。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 所有Channel共用一个ChannelHandler实例 
public class DataServerPipelineFactory implements ChannelPipelineFactory {

private static final DataServerHandler SHARED = new DataServerHandler();

public ChannelPipeline getPipeline() {
return Channels.pipeline(SHARED);
}
}
// 每一个Channel创建一个ChannelHandler实例
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
ChannelPipeline pipeline = pipeline();
pipeline.addLast("handler", new TestHandler());
return pipeline;
}
});

##(二)ChannelHandlerContext

  • ChannelHandlerContext是在Pipline注册ChannelHandler的时候和其绑定的,因此一个被多次注册(无论是否是同一个Pipline)的ChannelHandler会同时拥有多个ChannelHandlerContext。
  • 对于ChannelHandlerContext一个常见的误解是以为其可以在同一个Pipline的各个Handler之间传递数据。如果真要这样做,应该使用MessageEvent或者ChannelLocal。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Sharable
public class DataServerHandler extends SimpleChannelHandler {

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
Channel ch = e.getChannel();
Object o = e.getMessage();
if (o instanceof LoginMessage) {
authenticate((LoginMessage) o);
// 设置共享数据
ctx.setAttachment(true);
}
}
}
  • @Sharable注解只是起到一个标示的作用,说明一个该ChannelHandler实例可以被多次注册到一个或者多个Pipline中,而且不会导致不同的Channel共享数据出现竞争条件。
  • 基于ChannelHandlerContext同ChannelHandler的绑定时机和机制,很容易理解在本例中是怎么保证@Sharable的。

##(三)Channel

  • 该方法类似ChannelLocal,只是共享数据是直接存储在Channel实例中的。
1
2
3
4
5
6
7
8
9
@Sharable
public class TestHandler extends SimpleChannelHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) throws Exception {
// 通过任意一种方式获取到当前Channel,然后设置共享数据
ctx.getChannel().setAttachment(true);
event.getChannel().setAttachment(true);
}
}
  • 很明显,本例中的ChannelLocal也是符合@Sharable要求的。

##(四)ChannelLocal

  • 如果想在当前连接的其他ChannelHandler或者外部Handler中共享数据,则可以使用ChannelLocal。其设计思想类似于JDK中的ThreadLocal,其内部是一个Channel做Key,共享数据做Value的结构。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final class DataServerState {

public static final ChannelLocal<Boolean> loggedIn = new ChannelLocal<>() {
protected Boolean initialValue(Channel channel) {
return false;
}
}
}

@Sharable
public class DataServerHandler extends SimpleChannelHandler {

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
Channel ch = e.getChannel();
Object o = e.getMessage();
if (o instanceof LoginMessage) {
authenticate((LoginMessage) o);
// 设置共享数据
DataServerState.loggedIn.set(ch, true);
}
}
}
  • 很明显,本例中的ChannelLocal也是符合@Sharable要求的。

需求描述

实现基于Netty的“请求-响应”同步通信机制。

设计思路

Netty提供了异步IO和同步IO的统一实现,但是我们的需求其实和IO的同步异步并无关系。我们的关键是要实现请求-响应这种典型的一问一答交互方式。要实现这个需求,需要解决两个问题:

  1. 请求和响应的正确匹配。

    当服务端返回响应结果的时候,怎么和客户端的请求正确匹配起来呢?解决方式:通过客户端唯一的RequestId,服务端返回的响应中需要包含该RequestId,这样客户端就可以通过RequestId来正确匹配请求响应。
    
  2. 请求线程和响应线程的通信。

    因为请求线程会在发出请求后,同步等待飞鸽服务端的返回。因此,就需要解决,Netty在接受到响应之后,怎么通知请求线程结果。
    
解决方式是利用Java中的CountDownLatch类来实现同步Future。具体过程是:客户端发送请求后将<请求ID,Future>的键值对保存到一个全局的Map中,这时候用Future等待结果,挂住请求线程;当Netty收到服务端的响应后,响应线程根据请求ID寸全局Map中取出Future,然后设置响应结果到Future中。这个时候利用CountDownLatch的通知机制,通知请求线程。请求线程从Future中拿到响应结果,然后做业务处理。

通过对以上两个问题的解决,又引出了一个新的问题:全局Map在并发较大,网络环境较差的情况下,有可能爆满。目前SDK通过一个定时任务,来定时清理该全局Map中超时的请求。

同步Future的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class SyncFuture<T> implements Future<T> {
// 因为请求和响应是一一对应的,因此初始化CountDownLatch值为1。
private CountDownLatch latch = new CountDownLatch(1);
// 需要响应线程设置的响应结果
private T response;
// Futrue的请求时间,用于计算Future是否超时
private long beginTime = System.currentTimeMillis();
public SyncFuture() {
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
if (response != null) {
return true;
}
return false;
}
// 获取响应结果,直到有结果才返回。
@Override
public T get() throws InterruptedException {
latch.await();
return this.response;
}
// 获取响应结果,直到有结果或者超过指定时间就返回。
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException {
if (latch.await(timeout, unit)) {
return this.response;
}
return null;
}
// 用于设置响应结果,并且做countDown操作,通知请求线程
public void setResponse(T response) {
this.response = response;
latch.countDown();
}
public long getBeginTime() {
return beginTime;
}
}

服务器性能剖析

  • 第一原则:性能即响应时间。
  • 第二原则:无法测量就无法有效的优化
    1. 时间花在哪?
    2. 时间为什么花在那?

完成任务的时间分为:执行时间和等待时间。

  • 性能剖析分为两步:

    1. 测量任务所花费的时间;
    2. 对结果进行统计和排序,将耗费昂贵的任务排在前边。
  • 性能剖析分类:

    1. 基于执行时间的分析;
      研究什么任务的执行时间最长?
    2. 基于等待时间的分析。
      判断任务在什么地方被阻塞的时间最长?
  • 优化查询注意两点:

    1. 一些只占总响应时间5%的查询是不值得优化的;
      阿姆达尔定律:对一个占总响应时间不超过5%的查询进行优化,无论如何努力,收益也不会超过5%。
    2. 如果优化的成本大于收益,就应当停止优化。
  • 优化查询分两种场景:

    1. 查询次数很多,但是每次查询耗时较少;
      优先考虑降低查询次数。
    2. 查询次数很少,但是每次查询耗时很多。
      优化查询

性能剖析遵循自上而下的原则:应用程序-数据库-操作系统。

剖析工具:New Relic、xhprof(For PHP)

MySQL慢查询日志,如果设置long_query_time=0表示捕获所有查询,时间单位可以精确到微秒级别。一般只在收集负载样本时开启慢查询日志,如果一直开启,日志量会很大。
MyQL慢查询日志分析工具:pt-query-digest(分析tcpdump)

在MySQL5.6,profile已经过期了,取而代之的是performance_schema。

  • 诊断间歇性问题:
    1. 确认是单条查询问题还是服务器问题?
      • 使用Show Global Status
      • 使用shwo processlist
      • 使用查询日志
    2. 捕获诊断数据
  • 解决他人提出的技术问题的步骤:
    1. 弄清楚问题是什么?一定要能清晰的描述出来。
    2. 为了解决这个问题,已经做过什么操作了。

Schema与数据类型优化

数据库设计数据:Beginning Databse Design

  • 数据库设计原则:
    1. 更小的通常更好
      尽量使用能正确存储数据的最小数据类型。
    2. 简单就好
      整形比字符型简单。
    3. 尽量避免Null
      通常最好指定列为非Null。

Timestamp只使用DateTime一半的存储空间,并且是带时区的,允许的时间范围要小得多。

Varchar类型是变长字符串,其需要用1或2个字节存储字符串长度,如果字符串长度小于或等于255,则使用1个字节表示长度;否则用2个字节。如:varchar(10)占用11个字节;varchar(1000)占用1002个字节。
使用变长是为了节省空间。但是变长字符串在做更新操作时,可能会遇到比原来长度更长的情况,这个时候需要做些额外处理,Innodb是分裂页。

Varchar适用场景:字符串的最大长度比平均长度要大得多;列的更新很少;使用了像UTF-8这种复杂的字符集,每个字符都是用不同的字节进行存储。

Char类型是定长字符串,非常适合存储MD5值这种定长的字段。

Blob类型家族是二进制存储,Text类型家族是字符串存储。

max_heap_table_size和tmp_table_size控制内存临时表的大小,超过这个大小则转为磁盘临时表。

DateTime与时区无关,表示范围:1001-9999年,精度为秒;使用8个字节。
TimeStatmp和时区相关,表示范围:1970-2038年,精度为秒;使用4个字节存储。
Mysql默认会设置第一个TimeStamp为当前时间,更新的时候也会默认更新第一个Timestamp的值为当前时间。

  • Schema设计陷阱:
    1. 太多的列
      因为Mysql服务器转换来自存储引擎的行缓冲为行数据格式的CPU开销很大,而转换的代价依赖于列数。
  1. 关联太多的表
    Mysql支持一个查询最多关联61张表,但根据经验,如果考虑并发性和查询速度,最好不要超过12张表。

范式化的schema设计的一个缺点是会导致一个查询会有很多关联。
如果不需要关联表,则对大部分最坏的情况:表没有使用索引或者是全表扫描,当数据比内存大的时候,全表扫描可能比关联很多表要更快,因为避免了随机IO。

全表扫扫描一般是顺序IO,依赖于具体的存储引擎。

提升读性能的手段:缓存表、汇总表、影子表、物化视图、计数器表


创建高性能索引

复合索引字段的顺序很重要,因为MySQL只能高效的使用索引最左前缀列。

  • 索引类型:
  1. B-Tree索引
    实际上大部分存储引擎使用的是B+Tree数据结构,比如InnoDB。
  2. 哈希索引
    SHA1和MD5是强加密函数,设计目标是最大限度消除冲突。
  3. R-Tree索引
  4. 全文索引
  • 索引的有点:
    1. 索引大大减少了服务器需要扫描的数据量;
    2. 索引可以帮助服务器避免排序和临时表;
    3. 索引可以将随机IO变为顺序IO。

对于B-Tree索引,需要注意索引的顺序。索引列排序是从左到右进行排序。一般将选择性最高的列放于最左边。

如果数据都是放在内存中,那么聚族索引并没有优势。

按索引顺序读取数据比顺序的全表扫描慢。

Limit会限制mysql查询的数据集大小。

查询性能优化

缓存很重要

  • 衡量mysql性能的最简单的三个指标:
    响应时间
    扫描的行数
    返回的行数
  • 使用where的三种方式好坏依次为:

    1. 在索引中使用where条件来过滤不匹配的行,这时在存储引擎层来完成的。
    2. 使用索引覆盖扫描(在执行计划的Extra中出现using index)来返回记录,直接从索引中过滤不需要的记录并返回命中的结果。这时在MySQL服务器层来完成的,无须再回表查询记录。
    3. 从数据表中返回数据,然后过滤不满足条件的记录(在执行计划的Extra中出现Using where)。这时在MySQL服务器层来完成的,MySQL需要从数据表读取记录然后过滤。
  • MySQL的性能指标参考:

    一个通用服务器可能也能运行每秒10W的查询。
    一个千兆网卡能轻松满足每秒2000次查询。
    MySQL每秒能够扫描内存中上百万行数据。
    一次删除1W行数据一般来说是一个比较高效的的做法。

  • 重构查询的方式

    1. 将复杂查询分为多个简单查询。
    2. 切分为小查询,每个查询只负责一部分数据的查询。
    3. 分解关联查询,将合并动作放在应用程序中去做,好处如下:
      1. 让缓存更高效;
      2. 减少锁竞争;
      3. 在应用层做关联,更容易对数据库进行拆分和扩展;
      4. 查询本身效率也会有所提升;
      5. 相当于用哈希关联来替代MySQL的嵌套循环关联。
      

MySQL查询原理基础

  1. MySQL客户端和服务端的通信是半双工的。这意味着,当客户端发送数据给服务端时,服务端只能等待,不能干别的,并且客户端发送给服务端的一般是一个查询SQL的包;同理,服务端发送结果给客户端,一般是由多个包组成,客户端必须等待接收完全部数据,除非主动断开连接。这就是Limit存在的意义所在。
  2. 对于一个MySQL连接,或者说一个线程来说,任何时刻都有一个状态,该状态表明了MySQL当前正在干什么。状态如下:
    1. Sleep
    2. Query
    3. Locked
    4. Analyzing and statistics
    5. Coping to tmp table[on disk]
    6. Sorting result
    7. Seneing data
    

#MySQL分区表

sysbench介绍

SysBench是一个模块化的、跨平台、多线程基准测试工具,主要用于评估测试各种不同系统参数下的数据库负载情况。它主要包括以下几种方式的测试:

  1. cpu性能
  2. 磁盘io性能
  3. 线程调度性能
  4. 互斥锁性能
  5. 数据库性能(OLTP基准测试)
  6. 内存性能

目前sysbench主要支持 MySQL、pgsq、Oracle 这3种数据库。
sysbench在github的版本分为两个分支:0.4和0.5。0.4的最新版本是0.4.12,也是我使用的版本。
github:https://github.com/akopytov/sysbench

安装环境

操作系统:VMware虚拟CentOS 7
数据库:MySQL 5.6.24

安装步骤

  1. github下载sysbench的源码zip包:sysbench-0.4.zip到本地目录,如:/home/mahj/software目录下;
  2. 在/home/mahj/software目录下运行命令:unzip sysbench-0.4.zip,解压zip包,会生成sysbench-0.4目录;
  3. 进入sysbench-0.4目录,运行命令:./autogen.sh。这一步可能会报错:automake 1.10.x (aclocal) wasn’t found, exiting。这说明你的操作系统没有安装automake,运行命令:yum install automake.noarch,即可安装。然后再运行./autogen.sh命令,又报错:libtoolize 1.4+ wasn’t found, exiting。说明你的操作系统没有安装libtool,运行命令:yum install libtool,即可安装。继续运行
  4. 运行make命令,可能会报错:drv_mysql.c:35:19: fatal error: mysql.h: No such file or directory。这是因为,你本机没有安装mysql的开发lib库导致的,运行命令:yum install mysql-community-devel.x86_64,即可安装。然后运行make命令,即可正确。
  5. 运行make install;
  6. 运行sysbench –help测试安装是否正常。

命令运行过程如下:

1
[root@bogon sysbench-0.4]# sh autogen.sh
automake 1.10.x (aclocal) wasn't found, exiting
[root@bogon sysbench-0.4]# yum install automake.noarch
[root@bogon sysbench-0.4]# sh autogen.sh
libtoolize 1.4+ wasn't found, exiting
[root@bogon sysbench-0.4]# yum install libtool
[root@bogon sysbench-0.4]# sh autogen.sh
[root@bogon sysbench-0.4]# make
Making all in doc
make[1]: Entering directory `/home/mahj/software/sysbench-0.4/doc'
Making all in xsl
make[2]: Entering directory `/home/mahj/software/sysbench-0.4/doc/xsl'
make[2]: Nothing to be done for `all'.
make[2]: Leaving directory `/home/mahj/software/sysbench-0.4/doc/xsl'
make[2]: Entering directory `/home/mahj/software/sysbench-0.4/doc'
touch manual.html
make[2]: Leaving directory `/home/mahj/software/sysbench-0.4/doc'
make[1]: Leaving directory `/home/mahj/software/sysbench-0.4/doc'
Making all in sysbench
make[1]: Entering directory `/home/mahj/software/sysbench-0.4/sysbench'
Making all in drivers
make[2]: Entering directory `/home/mahj/software/sysbench-0.4/sysbench/drivers'
Making all in mysql
make[3]: Entering directory `/home/mahj/software/sysbench-0.4/sysbench/drivers/mysql'
gcc -DHAVE_CONFIG_H -I. -I../../../config  -I/usr/include/mysql -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches   -m64 -fPIC  -g -fabi-version=2 -fno-omit-frame-pointer -fno-strict-aliasing -DMY_PTHREAD_FASTMUTEX=1 -I../../../sysbench -D_XOPEN_SOURCE=500 -D_GNU_SOURCE  -W -Wall -Wextra -Wpointer-arith -Wbad-function-cast   -Wstrict-prototypes -Wnested-externs -Winline   -funroll-loops  -Wundef -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wcast-align        -pthread -O2 -ggdb3  -MT libsbmysql_a-drv_mysql.o -MD -MP -MF .deps/libsbmysql_a-drv_mysql.Tpo -c -o libsbmysql_a-drv_mysql.o `test -f 'drv_mysql.c' || echo './'`drv_mysql.c
drv_mysql.c:35:19: fatal error: mysql.h: No such file or directory
 #include <mysql.h>
                   ^
compilation terminated.
make[3]: *** [libsbmysql_a-drv_mysql.o] Error 1
make[3]: Leaving directory `/home/mahj/software/sysbench-0.4/sysbench/drivers/mysql'
make[2]: *** [all-recursive] Error 1
make[2]: Leaving directory `/home/mahj/software/sysbench-0.4/sysbench/drivers'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/home/mahj/software/sysbench-0.4/sysbench'
make: *** [all-recursive] Error 1
[root@bogon sysbench-0.4]# yum install mysql-community-devel.x86_64
[root@bogon sysbench-0.4]# make
[root@bogon sysbench-0.4]# make install
[root@bogon sysbench-0.4]# sysbench --help

常用测试命令

  1. 帮助信息
    sysbench --help
    
  2. CPU测试
    sysbench --test=cpu --cpu-max-prime=2000 run
    说明:通过计算2000以内的素数来测试CPU性能。
    
  3. 文件IO测试
    • 准备文件数据
      sysbench --test=fileio --num-threads=16 --file-total-size=10G prepare
      
    • 运行测试
      sysbench --test=fileio --num-threads=16 --init-rng=on --file-total-size=5G --file-test-mode=rndrw run
      
    • 读写模式(file-test-mode)包括:
      seqwr 顺序写入
      seqrewr 顺序重写
      seqrd 顺序读取
      rndrd 随机读取
      rndwr 随机写入
      rndrw 混合随机读/写
      
    • 清理测试准备的文件
      sysbench --test=fileio --num-threads=16 --file-total-size=10G cleanup
      
  4. 线程调度测试
    sysbench --test=threads --num-threads=64 --thread-yields=100 --thread-locks=2 run
    
  5. 互斥锁测试
    sysbench --test=mutex --num-threads=16 --mutex-num=1024 --mutex-locks=10000 --mutex-loops=5000 run
    说明:模拟所有线程在同一时刻并发运行,并都短暂请求互斥锁。
    
  6. 内存测试
    sysbench --test=memory --num-threads=16 --memory-block-size=8192 --memory-total-size=1G run
    说明:测试内存的连续读写性能。
    
  7. MySQL数据库测试
  • 准备表
    sysbench --test=oltp --oltp-table-size=1000000 --mysql-db=app-db --mysql-user=appuser prepare
    说明:用appuser登陆,并在app-db数据库创建sbtest表,其中包含100000万条记录。
    
  • 运行测试
    sysbench --test=oltp --oltp-table-size=1000000 --mysql-db=app-db --mysql-user=appuser --max-time=60 --oltp-read-only=on --num-threads=8 run
    说明:起8个线程,开启只读模式,执行60秒的测试。