Mysql与Mongodb你会如何选择

Posted by 刘勇(lyonger) on 2020-01-16

概述

背景

  • 数据库几乎是线上业务架构里的核心服务,在海量数据环境下,数据库的读写效率直接影响着一款产品的体验,因此从一开始就选择一款适合适的数据库就显得尤为重要。

  • 为了让大家在一开始选择数据库时有个参考数据,我们对MongoDB 3.0.7MySQL 5.5.40分别在大数据(百万级~千万级)环境下的平均时延、吞吐量进行了研究对比。

image

工具简介

  • sysbench是一个模块化的、跨平台、多线程基准测试工具,主要用于评估测试各种不同系统参数下的数据库负载情况。而ycsb(Yahoo! Cloud Serving Benchmark)则是雅虎开源的一款通用的、针对各类NoSQL产品的性能测试工具。接下来我们简单的介绍一下用于本次研究对比的压测工具:
工具名称 适用数据库 特点
sysbench MySQL/Oracle/PostgreSQL等RDBMS (1) 测试类型可选; (2) 定制的参数选择丰富; (3) 多线程支持;
ycsb MongoDB、HBase、Redis、等NoSQL (1) 多线程支持; (2) 扩展性强; (4) 数据请求支持随机分布; (5) 可灵活定义场景文件;

安装说明

基础环境

  • ycsb工程依赖Javamaven,有maven环境后就可以直接在ycsb根目录下运行package任务。而sysbench编译安装需要automake,于是:
1
2
3
4
5
6
7
#yum -y install mysql-devel automake libtool git wget unzip python-setuptools  java-devel
#easy_install argparse
#wget http://ftp.heanet.ie/mirrors/www.apache.org/dist/maven/maven-3/3.1.1/binaries/apache-maven-3.1.1-bin.tar.gz
#tar xf apache-maven-3.1.1-bin.tar.gz -C /usr/local/
#cd /usr/local && ln -s apache-maven-* maven
# echo "export PATH=/usr/local/maven/bin:\$PATH" >> /etc/profile
# source /etc/profile

下载并安装

sysbench

  • 我们选择最新版的sysbench 0.5版本来测试数据库性能,该版本优化了oltp的lua脚本测试和实时显示功能。sysbench默认支持MySQL,如果需要测试Oracle/PostgreSQL,则在configure时需要加上–with-oracle或者–with-pgsql参数。
1
2
3
4
5
6
7
8
9
# wget -O sysbench-0.5.zip  https://codeload.github.com/akopytov/sysbench/zip/0.5
#unzip sysbench-0.5.zip
# cd sysbench-0.5
#chmod +x autogen.sh
#./autogen.sh
#./configure --prefix=/usr/local/sysbench --with-mysql --with-mysql-includes=/usr/local/mysql/include --with-mysql-libs=/usr/local/mysql/lib
#make && make install
# echo "export PATH=/usr/local/maven/bin:/usr/local/sysbench/bin:\$PATH" >> /etc/profile
# source /etc/profile
  • 检查是否成功

image

ycsb

  • 我们同样选择最新版的ycsb来进行测试并使用mvn编译安装。
1
2
3
4
5
#cd /usr/local/ && git clone https://github.com/brianfrankcooper/YCSB.git
# ln -s YCSB ycsb
#cd ycsb && mvn -pl com.yahoo.ycsb:mongodb-binding -am clean package
#echo "export PATH=/usr/local/ycsb/bin:/usr/local/maven/bin:/usr/local/sysbench/bin:\$PATH" >> /etc/profile
#source /etc/profile
  • 检查是否成功。

image

使用说明

  • 为了更好地理解后面的测试过程,我们先对sysbench和ycsb的使用参数进行详细说明。

sysbench

1
./bin/sysbench --test=<test-name> [options]... <command>
  • options参数
参数名 说明
–test=oltp.lua 选用的lua测试脚本。
–num-threads=15 并发15个线程。
–oltp_tables_count=1 对1个表进行测试。
–oltp-table-size=1000000 每个表产生100w记录。
–report-interval=10 每10s输出一次测试进度报告。
–oltp-dist-type=uniform 指定随机取样类型,可选值有 uniform(均匀分布), Gaussian(高斯分布), special(空间分布),默认是special。
–rand-init=on 是否随机初始化数据,on为开启。
–max-requests=0 压力测试产生请求的总数,如果以下面的max-time来记,这个值设为0。
–oltp-test-mode=nontrx 执行模式,这里是非事务式的。可选值有simple,complex,nontrx,默认complex。
–oltp-nontrx-mode=select 查询类型对于非事务执行模式,可选值有select, update_key, update_nokey, insert, delete,默认select。
–oltp-read-only=on 表示不止产生只读sql,也就是使用oltp.lua时会采用读写混合模式。默认 off,如果设置为on,则不会产生update,delete,insert的sql。
–oltp-skip-trx=off 省略begin/commit语句,默认是off
–mysql-table-engine=innodb 指定MySQL引擎类型
–mysql-db=sysbench_test 指定测试时的数据库名
–max-time=1800 压力测试的最大时间,单位秒。
–mysql-ignore-errors=all 忽略所有错误类型
–oltp-point-selects=N 一个事务中简单select查询数量
–oltp-index-updates=N 一个事务中简单update更新数量
  • command参数
参数名 说明
prepare 数据预热阶段
run 数据测试阶段
cleanup 数据清理阶段

ycsb

1
#./bin/ycsb command database [options]
  • options参数
参数名 说明
recordcount=1000000 测试数据集的记录条数
operationcount=1000000 测试过程中执行的操作总数
fieldcount=4 字段数目
fieldlength=100 每个字段的长度
readallfields=true 查询时是否读取记录的所有字段
writeallfields =false 写入时是否写入所有字段
readproportion=0.7 读操作的百分比
updateproportion=0.1 更新操作的百分比
insertproportion=0.2 插入操作的百分比
insertorder=hashed 数据插入是随机hash
table=ycsb_test 测试表名
Maxexecutiontime=1800 最长执行时间,单位秒。
  • command参数
参数名 说明
load 数据预热阶段
run 数据测试阶段
shell 交互模式

性能评估

测试目的

  • 在大数据环境下,对比MySQL 5.5.40与MongoDB 3.0.7的只读、只更新、只插入,混合模式(读:写=3:7)下的平均时延、吞吐量。

测试环境

1
2
3
4
5
6
7
Dell PowerEdge R410
RAM:64G
Kernel: 2.6.32
MongoDB:3.0.7
MySQL:5.5.40
Linux OS:CentOS release 6.5 x86_64
CPU:Intel(R) Xeon(R) CPU X5650 @2.67GHz / 24 cores

测试过程

  • 测试的数据量变量名假定为size,分别取值为200w、400w、600w、800w、1000w。每次使用单进程对单个表压测,每个表均含有4个字段,每次压测时间不超过30分钟。为了让数据更加符合我们想要的结果,每轮测试完成后,我们都重启mysqld和mongodb实例,并且用下面的方法删除系统cache,释放swap。
1
2
3
#sync                   		   				# 将脏数据刷新到磁盘
#echo 3 > /proc/sys/vm/drop_caches # 清除OS Cache
#swapoff -a && swapon -a

sysbench

准备工作

  1. 创建测试列库

    1
    2
    mysql> drop database if exists sysbench_test;
    mysql> create database if not exists sysbench_test character set utf8 collate utf8_general_ci;
  2. 查看表结构

    1
    2
    3
    4
    5
    6
    7
    CREATE TABLE `sbtest` (
    `id` int(10) unsigned NOT NULL auto_increment,
    `k` int(10) unsigned NOT NULL default '0',
    `c` char(120) NOT NULL default '',
    `pad` char(60) NOT NULL default '',
    PRIMARY KEY (`id`),
    KEY `k` (`k`));

只读测试

  • 测试命令
1
2
3
4
5
6
7
8
# ./bin/sysbench --db-driver=mysql --test=oltp.lua --num-threads=1 \
--oltp_tables_count=1 --oltp-table-size=${size} --report-interval=10 \
--oltp-dist-type=uniform --rand-init=on --max-requests=0 \
--oltp-test-mode=nontrx --oltp-nontrx-mode=select \
--oltp-read-only=on --oltp-skip-trx=on --mysql-table-engine=innodb \
--mysql-socket=/tmp/mysql.sock --mysql-user=root --mysql-host=localhost \
--mysql-password=`cat /data/save/mysql_root` --mysql-port=3306 \
--mysql-db=sysbench_test [ prepare | run | cleanup ]

只更新测试

  • 测试命令
1
2
3
4
5
6
#./bin/sysbench --db-driver=mysql --test=./share/sysbench/update_index.lua \
--num-threads=1 --oltp_tables_count=1 --oltp-table-size=${size} --report-interval=10 \
--oltp-dist-type=uniform --rand-init=on --max-requests=0 \
--oltp-read-only=off --mysql-table-engine=innodb --mysql-socket=/tmp/mysql.sock \
--mysql-user=root --mysql-host=localhost --mysql-password=`cat /data/save/mysql_root` \
--mysql-port=3306 --mysql-db=sysbench_test [ prepare | --max-time=1800 run | cleanup ]

只插入测试

  • 测试命令
1
2
3
4
5
6
#./bin/sysbench --db-driver=mysql --test=./share/sysbench/insert.lua --num-threads=1 \
--oltp_tables_count=1 --oltp-table-size=${size} --report-interval=10 \
--oltp-dist-type=uniform --rand-init=on --max-requests=0 --oltp-read-only=off \
--mysql-table-engine=innodb --mysql-socket=/tmp/mysql.sock --mysql-user=root \
--mysql-host=localhost --mysql-password=`cat /data/save/mysql_root` --mysql-port=3306 \
--mysql-db=sysbench_test [ prepare | --max-time=1800 run | cleanup ]

混合模式测试

  • 测试命令
1
2
3
4
5
6
7
#./bin/sysbench --test=./share/sysbench/oltp.lua --mysql-host=localhost --mysql-port=3306 \
--mysql-user=root --mysql-password=`cat /data/save/mysql_root` \
--mysql-db=sysbench_test --mysql-table-engine=innodb \
--oltp-table-size=${size} --report-interval=10 --rand-init=on --max-requests=0 \
--oltp-read-only=off --oltp-point-selects=$((size/3)) --oltp-point-inserts=$((size/3)) \
--oltp-index-updates=$((size/3)) --num-threads=1 --mysql-ignore-errors=all \
--oltp-skip-trx=off --oltp_tables_count=1 [ prepare | --max-time=1800 run | cleanup ]
  • 参数含义请参考上面的使用说明章节。

ycsb

准备工作

  • 编辑workload文件,分别修改readproportion、updateproportion、insertproportion为相应的值并分别命名为workload_select、workload_update、workload_insert、workload_complex比如只读测试workload_select文件内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
workload=com.yahoo.ycsb.workloads.CoreWorkload
recordcount=${size}
operationcount=${size}
insertstart=0
fieldcount=4
fieldlength=100
readallfields=true
writeallfields=false
fieldlengthdistribution=zipfian
readproportion=1
updateproportion=0
insertproportion=0
requestdistribution=zipfian
table=ycsb_test
maxexecutiontime=1800

只读测试

  • 测试命令
1
2
./bin/ycsb load mongodb -s -threads 1 -P workloads/workload_select
./bin/ycsb run mongodb -s -threads 1 -P workloads/workload_select

只更新测试

  • 测试命令
1
2
./bin/ycsb load mongodb -s -threads 1 -P workloads/workload_update
./bin/ycsb run mongodb -s -threads 1 -P workloads/workload_update

只插入测试

  • 测试命令
1
2
./bin/ycsb load mongodb -s -threads 1 -P workloads/workload_insert
./bin/ycsb run mongodb -s -threads 1 -P workloads/workload_insert

混合模式测试

  • 测试命令
1
2
./bin/ycsb load mongodb -s -threads 1 -P workloads/workload_complex
./bin/ycsb run mongodb -s -threads 1 -P workloads/workload_complex

测试结果

吞吐量

  • 吞吐量:单位时间内的操作数。

image

image

image

image

平均时延

  • 平均时延:每次操作所消耗的平均时间。

image

  • PS:MongoDB在读取的场景有着天生的优势,每秒的Select操作数远远超越MySQL。

image

  • PS:上图看出MongoDB在200W记录的时候,性能较接近MySQL,但是随着数据量越大性能剧降,而MySQL却相对较为稳定。同时MySQL的性能要好于MongoDB 2倍。

image

image

  • PS:以上混合模式是主要模拟了游戏的实际场景(写为主),结合前面的分步测试结果,这里MongoDB比MySQL性能相差至少4倍,会不会感到理所当然?

分析总结

  • 通过以上测试发现MySQL和MongoDB各有优势,因此我们给出如下建议作为参考,具体选择哪款数据库请根据自身业务场景来决定。
  1. 相比较MySQL,MongoDB数据库更适合那些读作业较重的任务模型。MongoDB能充分利用机器的内存资源。如果机器的内存资源丰富的话,MongoDB的查询效率会快很多。
  2. 相比MongoDB,MySQL数据库更适合插入、更新为主的任务模型,其效率相比于MongoDB优势非常明显。
  3. 在带”_id”插入数据的时候,MongoDB的插入效率其实并不高。如果想充分利用MongoDB性能的话,推荐采取不带”_id”的插入方式,然后对相关字段作索引来查询。
  4. MongoDB适合那些对数据库具体数据格式不明确或者数据库数据格式经常变化的需求模型,而且对开发者十分友好。
  5. 稳定性方面,MongoDB不如MySQL,MySQL不愧是一种非常稳定的数据库,无论在指定主键还是在不指定主键插入的情况下,其效率都差不了太多, 其稳定性还是毋庸置疑。
  6. 事务支持方面,MySQL占绝对优势,MongoDB对事务关系支持薄弱,这也是所有NoSQL数据库共同的缺陷,不过NoSQL并不是为了事务关系而设计的,具体应用还是很需求。
  • 结合我们的游戏业务场景来看,选用MySQL无疑是最理想的选择。但是有时候我们的游戏数据分析后台需要做很大量的统计、展示,这对读取性能要求较高,如果同时能够结合MongoDB的来提升读取性能可能更有利于业务的扩展性。主要是把用户主数据写到MySQL,用户日志类数据写到MongoDB,然后游戏数据分析后台直接读取MongoDB做统计分析工作

参考资料



支付宝打赏 微信打赏

赞赏一下