MongoDB 与 MySQL 对比

MongoDB 与 MySQL 对比

由于公司系统使用MongoDB,虽然之前了解,但并没有深入学习MongoDB。见此机会,参考《MongoDB 权威指南》深入学习,结合对比MySQL,加深对两种不同数据库的理解。特把学习过程记录和大家分享。

一、 表结构对比

表结构对比 MongoDB MySQL
collections tables
documents rows
主键 _id id 与业务无关的值作为主键。如果没有显式地在表定义时指定主键,InnoDB存储引擎会为每一行生成一个6字节的ROWID
主键生成策略 24位的字符串(time + machine + pid + inc),自己指定 UUID, 自增
面向Documents数据库 T F
面向行数据库 F T
约束 主键约束,外键约束

二、 数据类型对比

数据类型对比 MongoDB MySQL
整形 NumberInt("3"),NumberLong("3") TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT
浮点 默认使用64位浮点型数值 FLOAT, DOUBLE, DECIMAL
字符 utf8 字符串 VARCHAR, CHAR
日期/时间 new Date(), 自新纪元依赖经过的毫秒数,不存储时区 DATE, DATETIME, TIMESTAMP
NULL null 不支持(null与null不相等)
布尔类型 true/false 不支持
正则表达式 支持 { "x" : /foobar/i } 不支持
数组 支持 { "x" : ["a", "b", "c"]} 不支持
二进制数据 支持 GridFS BLOB, TEXT
代码片段 { "x" : function() { /... / } } 不支持

三、 SHELL终端对比

对比项 MongoDB MySQL
启动 mongo mysql -u root -p
查看库 show dbs show databases
使用库 use test use test
查看表 show collections show tables

四、 查询对比

查询对比 MongoDB MySQL
检索单列 db.users.find({ "age" : 27 }) SELECT * FROM users WHERE age = 27;
检索多列 db.users.find({ "age" : 27, "username" : "joe" }) SELECT * FROM users WHERE age = 27 and username = 'joe';
指定需要返回的键 db.users.find({}, { "username" : 1, "email" : 1 }) SELECT username, email FROM users;
范围检索 db.users.find({"age" : { "$gte" : 18, "$lte" : 30 }}) $lt, $lte, $gt, $gte 分别对应 <, <=, >, >= SELECT * FROM users WHERE age >= 18 AND age <=30;
不匹配检索 db.users.find({ "username" : { "$ne" : "joe" } }) SELECT * FROM users WHERE username <> 'joe';
IN 操作符 db.raffle.find({ "ticket_no" : { "$in" : [725, 542, 390] } }) $in非常灵活,可以指定不同类型 的条件和值。 例如在逐步将用户的ID号迁移成用户名的过程中, 查询时需要同时匹配ID和用户名 SELECT ticket_no FROM raffles WHERE ticket_no IN (725, 542, 390);
NOT IN 操作符 db.raffle.find({ "ticket_no" : { "$nin" : [725, 542, 390] } }) SELECT * FROM raffles WHERE ticket_no not in (725, 542, 390);
OR 操作符 db.raffle.find({ "$or" : [{ "ticket_no" : 725 }, { "winner" : true }] }) SELECT * FROM raffles WHERE ticket_no = 725 OR winner = 'true';
空值检查 db.c.find({"y" : null}) null不仅会匹配某个键的值为null的文档 ,而且还会匹配不包含这个键的文档。 所以,这种匹配还会返回缺少这个键的所有文档。 如果 仅想要匹配键值为null的文档, 既要检查改建的值是否为null, 还要通过 $exists 条件 判定键值已经存在 db.c.find({ "z" : { "$in" : [null], "$exists" : true }}) SELECT * FROM cs WHERE z is null;
多列排序 db.c.find().sort({ username : 1, age: -1 }) SELECT * FROM cs ORDER BY username ASC, age DESC;
AND操作符 db.users.find({ "$and" : [{ "x" : { "$lt" : 1 }, { "x" : 4 } }] }) 由于查询优化器不会对 $and进行优化, 所以可以改写成下面的 db.users.find({ "x" : { "$lt" : 1, "$in" : [4] } }) SELECT * FROM users WHERE x > 1 AND x IN (4);
NOT 操作符 db.users.find({ "id_num" : { "$not" : { "$mod" : [5,1] } } }) SELECT * FROM users WHERE id_num NOT IN (5,1);
LIKE 操作符(正则匹配) db.blogs.find( { "title" : /post?/i } ) MongoDB 使用Perl兼容的正则表达式(PCRE) 库来匹配正则表达式, 任何PCRE支持表达式的正则表达式语法都能被MongoDB接受 SELECT * FROM blogs WHERE title LIKE "post%";

五、 函数对比

{ "_id" : 1, "item" : "abc", "price" : 10, "quantity" : 2 }
{ "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1 }
{ "_id" : 3, "item" : "xyz", "price" : 5, "quantity" : 5 }
{ "_id" : 4, "item" : "abc", "price" : 10, "quantity" : 10 }
{ "_id" : 5, "item" : "xyz", "price" : 5, "quantity" : 10 }
函数对比 MongoDB MySQL
COUNT db.foo.count() SELECT COUNT(id) FROM foo;
DISTINCT db.runCommand({ "distinct": "people", "key": "age" }) SELECT DISTINCT(age) FROM people;
MIN db.sales.aggregate( [ { $group: { _id: {}, minQuantity: { $min: "$quantity" } } } ]); 结果: { "_id" : { }, "minQuantity" : 1 } SELECT MIN(quantity) FROM sales;
MAX db.sales.aggregate( [ { $group: { _id: {}, maxQuantity: { $max: "$quantity" } } } ]); SELECT MAX(quantity) FROM sales;
AVG db.sales.aggregate( [ { $group: { _id: {}, avgQuantity: { $avg: "$quantity" } } } ]); SELECT AVG(quantity) FROM sales;
SUM db.sales.aggregate( [ { $group: { _id: {}, totalPrice: { $sum: "$price" } } } ]); SELECT SUM(price) FROM sales;

六、 CURD 对比

CURD 对比 MongoDB MySQL
插入数据 post = {"title" : "My Blog Post", "content" : "Here`s my blog post"}; db.blog.insert(post) 如果blog 这个集合不存在,则会创建 INSERT INTO blogs(title, blog_content) VALUES ('My Blog Post', 'Here`s my blog post.')
批量插入 db.blog.batchInsert([{ "title" : "AAA", "content" : "AAA---" }, { "title" : "BBB", "content" : "JJJJ--" }]) 当前版本的MongoDB能接受最大消息长度48MB, 所以在一次批量插入中能插入的文档是有限制的。 并且在执行批量插入的过程中,有一个文档插入失败, 那么在这个文档之前的所有文档都会成功插入到集合中, 而这个文档以及之后的所有文档全部插入失败。 INSERT INTO blogs(title, blog_content) VALUES('AAA', 'AAA---'), ('BBB', 'BBB---');
查询数据 db.blog.find(); db.blog.findOne(); SELECT * FROM blogs; SELECT * FROM blogs LIMIT 1;
更新旧数据 post.blog_content = "十一"; db.blog.update({title: "My Blog Post"}, post) UPDATE set blog_content = "十一" WHERE title = "My Blog Post";
更新新增COLUMN post.comments = "very good"; db.blog.update({title : "My Blog Post"}, post) ALTER table blogs ADD COLUMN comments varchar(200); UPDATE blogs set comments = "very good" WHERE title = 'My Blog Post';
删除数据 db.blog.remove({ title : "My Blog Post" }) DELETE FROM blogs WHERE title = 'My Blog Post'
校验 post.blog_visit = 123; db.blog.update({title : "My Blog Post"}, post); post.blog_visit = "asd.123aaa"; db.blog.update({title : "My Blog Post"}, post) 插入的时候,检查大小。所有的文档都必须小于16MB。 这样做的目的是为了防止不良的模式设计,并且保持性能一直。由于MongoDB只进行最基本的检查,所以插入非法的数据很容易。 类型校验,长度校验。 ALTER table blogs ADD COLUMN blog_visit INT(10); UPDATE blogs SET blog_visit = "asdasd" WHERE id = 1; ERROR 1366 (HY000): Incorrect integer value: 'asdasd' for column 'blog_visit' at row 1
删除表 db.blog.remove({}), db.blog.drop() DELETE from blogs; drop table blogs;

七、有的没的

MongoDB:

  • GridFS

    可以用来存储大文件(>16M), 与MySQL BLOB,TEXT 类似。

  • MapReduce

    MapReduce
    是一种计算模型,简单的说就是将大批量的工作数据分解执行,然后再将结果合并成
    最终结果。MongoDB提供的MapReduce
    非常灵活,对于大规模数据分析也相当实用。

  • 时间有限的集合

    MongoDB 2.2 引入一个新特性--
    TTL集合,TTL集合支持失效时间设置,使用expireAfterSeconds 来实现
    当超过指定时间后,集合自动清除超时的文档,这用来保存一些诸如session会话信息
    的时候非常有用,或者存储数据使用。

    戳这里

  • 无JOIN

    MongoDB 为了更快的读,以及更方便的分布式,抛弃了JOIN操作。
    JOIN开销其实很大。

  • 关于锁

    当资源被代码的多个部分所共享时,需要确定这处资源只能在一个地方被操作。
    就版本的MongoDB(pre
    2.0)拥有一个全局的写入锁。这就意味着贯穿整个服务器只有一个地方做写操作。
    这就可能导致数据库因为某个地方锁定超负载而停滞。这个问题在2.0版本中得到
    了显著的改善,并且在2.2版本中得到了进一步的加强。MongoDB
    2.2使用数据库级别的锁再这个问题上迈进了一大步。

    图中是两个不同版本的MongoDB,写入性能对比。


    戳这里
    戳这里

    MySQL InnoDB使用行级锁,有效提高并发。

  • 无事务

    不像MySQL
    这些多行数据原子操作的传统数据库。MongoDB只支持单个文件的原子修改。
    但这也正是MongoDB可以更快地读的原因,没有事务这些负载的处理。MongoDB
    可以轻松处理TB级别的数据。

  • 磁盘消耗

    MongoDB
    会消耗太多的磁盘空间了。当然,这与它的编码方式有关,因为MongoDB会通过预分配
    大文件空间来避免磁盘碎片问题。它的工作方式是这样:在创建数据库时,系统会创建
    一个名为[db_name].0的文件,当该文件有一半以上被使用时,系统会再次创建一个名
    为[db_namel].1的文件,该文件的大小是方才的两倍。这个情况会持续不断的发生,因此
    256、512、1024、2048大小的文件会被写到磁盘上。

MySQL:

  • 强大的引擎

    InnoDB 引擎: MySQL默认的事务性引擎。它被设计用来处理大量的短期事务,短期
    事务大部分情况是正常提交的,很少会被回滚。InnoDB采用MVCC来支持高并发。

    MyISAM 引擎: 5.1以及之前的默认版本。全文索引,压缩,空间函数,但是MyISAM不支持事务和
    行级锁。MyISAM最整张表加锁,而不是针对行。读取时会对需要读到的所有表加
    共享锁,写入时则对表加排它锁。

    Memory 引擎:比MyISAM表块一个数量级,因为所有的数据都保存在内存中,不需要进行磁盘I/O。数据会丢失

    Infobright 是最有名的面向列的存储引擎。但该引擎不支持索引。

  • 事务

    确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中

    的数据应满足完整性约束。

  • schema

    有利于数据整理,数据存储,并执行正规化的行为。 保证数据的完整性,一致性。

    描述了数据存储的模板,比如创建table。

    校验数据的格式,比如整形的column 就不能存放字符串数据。

八、 MySQL 与 MongoDB 写入对比

options MySQL MongoDB
Time taken for tests: 548.281 seconds 661.318 seconds
Total transferred: 44000000 bytes 44200000 bytes
Requests per second: 182.39 [#/sec] 151.21 [#/sec]
Time per request: 274.141 [ms] 330.659 [ms]
Time per request(across all concurrent requests): 5.483 [ms] 6.613 [ms]
Transfer rate: 78.37 [Kbytes/sec] received 65.27 [Kbytes/sec] received

在本测试例子中,MySQL 写入情况好于 MongoDB

压测参考命令:

   ab -c 50 -n 100000 http://127.0.0.1:6666/deals/mysql_write
   ab -c 50 -n 100000 http://127.0.0.1:6666/deals/mongodb_write

九、 MySQL 与 MongoDB 读取对比

options MySQL MongoDB
Time taken for tests: 1181.881 seconds 606.406 seconds
Failed requests: 2239 0
Non-2xx responses: 2239 0
Total transferred: 359397490 bytes 44100000 bytes
Requests per second: 84.61 [#/sec] 164.91 [#/sec]
Time per request: 590.941 [ms] 303.203 [ms]
Time per request(across all concurrent requests): 11.819 [ms] 6.064 [ms]
Transfer rate: 296.96 [Kbytes/sec] received 71.02 [Kbytes/sec] received

在本测试例子中, MySQL 读取性能没有 MongoDB好

压测参考命令:

   ab -c 50 -n 100000 http://127.0.0.1:6666/deals/mysql_read
   ab -c 50 -n 100000 http://127.0.0.1:6666/deals/mongodb_read

参考

MySQL 外键使用

MongoDB

MongoDB核心贡献者:不是MongoDB不行,而是你不懂!

MongoDB和MySQL性能测试及其结果分析

Apache Benchmark 的使用的个人浅薄经验

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容