对于mongoDB与mySQL的比较-为什么在某些方面要比另一个好[关闭]感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解mongodb和mysql对比,并且为您提供关于mangodb与my
对于mongoDB与mySQL的比较-为什么在某些方面要比另一个好[关闭]感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解mongodb和mysql对比,并且为您提供关于mangodb与mysql的区别及部署、Mongo db 与mysql 语法比较、Mongodb与MySQL之间的比较分析、MongoDB与MySQL关于写确认的异同的宝贵知识。
本文目录一览:- mongoDB与mySQL的比较-为什么在某些方面要比另一个好[关闭](mongodb和mysql对比)
- mangodb与mysql的区别及部署
- Mongo db 与mysql 语法比较
- Mongodb与MySQL之间的比较分析
- MongoDB与MySQL关于写确认的异同
mongoDB与mySQL的比较-为什么在某些方面要比另一个好[关闭](mongodb和mysql对比)
我真的是数据库新手,并且对一些高级基础知识感兴趣。我已经阅读了这篇精彩的SO文章。在某些情况下,我比别人更好,但不确定为什么。
-
为什么在联接操作中MySQL比MongoDB快?
-
为什么MongoDB在分布式系统中可以更好地扩展?
-
如果我“只是选择一堆表并将所有对象放在一起,这就是大多数人在Web应用程序中所做的事情”,为什么MongoDB会更快?
非常感谢!
mangodb与mysql的区别及部署
一, mangodb与mysql的区别
mangoDB与MYSQL都是开源的数据库,但是mysql是传统的关系型数据库,mangdb则是非关系型数据库,也可以称之为文档型数据库,是一种NoSQL的数据库,两则各自都有各自的优缺点
mysql: 关系型数据库。
优点: 成熟稳定, 源代码的可移植性; 支持的操作系统多 为多种编程语言提供API(接口)
缺点: 关系表的不灵活性;存储引擎混乱;原生json支持的缺乏
mangodb: 非关系型数据库
优点: 01,不存在sql注入:MySQL的是sql注入是一个很严重的缺点,虽然可以使用参数绑定和预处理以及特殊字符转义来处理。但是MongoDB根本不存在这个问题。xss攻击是需要防范。
02,不需要提前创建表:在MySQL中如果想要写入一条数据的话必须要先创建好一张表然后才能写入数据,比如:要在user表里写入id=1,username=‘aaa’,sex=''女'',age=‘20’这条数据,那你就必须在MySQL数据库上提前建好一张user表,并且至少必须有id,username,sex,age这几个字段才能写入成功。但是MongoDB可以直接写入数据,不需要提前创建表
03,字段数据格式自由:在MySQL中,如果id字段是数字的话你写一个字符串进去是会报错的,但是MongoDB不会
04,可以处理json结构:在MongoDB可以存储一个json对象,比如 字段a的值为{"a":11,"b":12,"c":"abc","d":[1,2,3]},你可以直接去读取或设置a字段的b值 a.b,读取a字段d数组的第二个值:a.d.1,可以去删除a字段的a数据$unset:{"a.a":1},就会变成:{"b":12,"c":"abc","d":[1,2,3]}
05,充分利用了计算机内存,所以查询和插入效率要远大于MySQL。我自己测试(400w随机数据)的时候只有在没有索引的情况下MongoDB的查询效率要远大于MySQL,插入效率和MySQL差不多都是8w条左右1分钟。在有索引的时候MySQL的查询要速度要高于MongoDB。
缺点:
Mongodb全局锁机制。
删除数据集合后空间不会自动释放
mysql与 mangodb对比
MySQL
MongoDB
服务器守护进程
mysqld
mongod
客户端工具
mysql
mongo
逻辑备份工具
mysqldump
mongodump
逻辑还原工具
mysql
mongorestore
数据导出工具
mysqldump
mongoexport
数据导入工具
source
mongoimport
新建用户并授权
grant all on *.*
to username@''localhost''
identified by ''passwd'';
db.addUser("user","psw")
db.auth("user","psw")
显示库列表
show databases;
show dbs
进去库
use dbname;
use dbname
显示表列表
show tables;
show collections
查询主从状态
show slave status;
rs.status
创建库
create database name;
无需单独创建,直接use进去
创建表
create table tname(id int);
无需单独创建,直接插入数据
删除表
drop table tname;
db.tname.drop()
删除库
drop database dbname;
首先进去该库,db.dropDatabase()
插入记录
insert into tname(id) value(2);
db.tname.insert({id:2})
删除记录
delete from tname where id=2;
db.tname.remove({id:2})
修改/更新记录
update tname set id=3
where id=2;
db.tname.update({id:2},
{$set:{id:3}},false,true)
查询所有记录
select * from tname;
db.tname.find()
查询所有列
select id from tname;
db.tname.find({},{id:1})
条件查询
select * from tname where id=2;
db.tname.find({id:2})
条件查询
select * from tname where id < 2;
db.tname.find({id:{$lt:2}})
条件查询
select * from tname where id >=2;
db.tname.find({id:{$gte:2}})
条件查询
select * from tname where id=2
and name=''steve'';
db.tname.find({id:2,
name:''steve''})
条件查询
select * from tname where id=2
or name=''steve'';
db.tname.find($or:[{id:2},
{name:''steve''}])
条件查询
select * from tname limit 1;
db.tname.findOne()
模糊查询
select * from tname where name
like "%ste%";
db.tname.find({name:/ste/})
模糊查询
select * from tname where name
like "ste%";
db.tname.find({name:/^ste/})
获取表记录数
select count(id) from tname;
db.tname.count()
获取有条件
的记录数
select count(id) from tname
where id=2;
db.tname.find({id:2}).count()
查询时去掉
重复值
select distinct(last_name)
from tname;
db.tname.distinct(''last_name'')
正排序查询
select *from tname order by id;
db.tname.find().sort({id:1})
逆排序查询
select *from tname
order by id desc;
db.tname.find().sort({id:-1})
取存储路径
explain select * from tname
where id=3;
db.tname.find({id=3}).explain()
二, mangodb安装
01,配置yum地址
1 cat /etc/yum.repos.d/mangodb.repo 2 [mongodb-org] 3 name=MongoDB Repository 4 baseurl=https://mirrors.tuna.tsinghua.edu.cn/mongodb/yum/el$releasever/ 5 gpgcheck=0 6 enabled=1
yum makecache
02,安装mangodb
yum install mongodb-org
等待安装完成
03,配置mangodb
更改限制ip 由于我内网环境,python爬虫使用,设置为所有能访问
启动: service mongod start
停止: service mongod stop
04,使用mangodb
本地连接: mongo 127.0.0.1:27017
文章文字借鉴: https://www.cnblogs.com/syomm/p/5760441.html
https://www.cnblogs.com/fanhuo/p/9822853.html
Mongo db 与mysql 语法比较
mongodb与mysql命令对比
传统的关系数据库一般由数据库(database)、表(table)、记录(record)三个层次概念组成,MongoDB是由数据库(database)、集合(collection)、文档对象(document)三个层次组成。MongoDB对于关系型数据库里的表,但是集合中没有列、行和关系概念,这体现了模式自由的特点。
MySQL
MongoDB
说明
mysqld
mongod
服务器守护进程
mysql
mongo
客户端工具
mysqldump
mongodump
逻辑备份工具
mysql
mongorestore
逻辑恢复工具
db.repairDatabase()
修复数据库
mysqldump
mongoexport
数据导出工具
source
mongoimport
数据导入工具
grant * privileges on *.* to …
Db.addUser()
Db.auth()
新建用户并权限
show databases
show dbs
显示库列表
Show tables
Show collections
显示表列表
Show slave status
Rs.status
查询主从状态
Create table users(a int, b int)
db.createCollection("mycoll", {capped:true,
size:100000}) 另:可隐式创建表。
创建表
Create INDEX idxname ON users(name)
db.users.ensureIndex({name:1})
创建索引
Create INDEX idxname ON users(name,ts DESC)
db.users.ensureIndex({name:1,ts:-1})
创建索引
Insert into users values(1, 1)
db.users.insert({a:1, b:1})
插入记录
Select a, b from users
db.users.find({},{a:1, b:1})
查询表
Select * from users
db.users.find()
查询表
Select * from users where age=33
db.users.find({age:33})
条件查询
Select a, b from users where age=33
db.users.find({age:33},{a:1, b:1})
条件查询
select * from users where age<33
db.users.find({''age'':{$lt:33}})
条件查询
select * from users where age>33 and age<=40
db.users.find({''age'':{$gt:33,$lte:40}})
条件查询
select * from users where a=1 and b=''q''
db.users.find({a:1,b:''q''})
条件查询
select * from users where a=1 or b=2
db.users.find( { $or : [ { a : 1 } , { b : 2 } ] } )
条件查询
select * from users limit 1
db.users.findOne()
条件查询
select * from users where name like "%Joe%"
db.users.find({name:/Joe/})
模糊查询
select * from users where name like "Joe%"
db.users.find({name:/^Joe/})
模糊查询
select count(1) from users
Db.users.count()
获取表记录数
select count(1) from users where age>30
db.users.find({age: {''$gt'': 30}}).count()
获取表记录数
select DISTINCT last_name from users
db.users.distinct(''last_name'')
去掉重复值
select * from users ORDER BY name
db.users.find().sort({name:-1})
排序
select * from users ORDER BY name DESC
db.users.find().sort({name:-1})
排序
EXPLAIN select * from users where z=3
db.users.find({z:3}).explain()
获取存储路径
update users set a=1 where b=''q''
db.users.update({b:''q''}, {$set:{a:1}}, false, true)
更新记录
update users set a=a+2 where b=''q''
db.users.update({b:''q''}, {$inc:{a:2}}, false, true)
更新记录
delete from users where z="abc"
db.users.remove({z:''abc''})
删除记录
db. users.remove()
删除所有的记录
drop database IF EXISTS test;
use test
db.dropDatabase()
删除数据库
drop table IF EXISTS test;
db.mytable.drop()
删除表/collection
db.addUser(‘test’, ’test’)
添加用户
readOnly-->false
db.addUser(‘test’, ’test’, true)
添加用户
readOnly-->true
db.addUser("test","test222")
更改密码
db.system.users.remove({user:"test"})
或者db.removeUser(''test'')
删除用户
use admin
超级用户
db.auth(‘test’, ‘test’)
用户授权
db.system.users.find()
查看用户列表
show users
查看所有用户
db.printCollectionStats()
查看各collection的状态
db.printReplicationInfo()
查看主从复制状态
show profile
查看profiling
db.copyDatabase(''mail_addr'',''mail_addr_tmp'')
拷贝数据库
db.users.dataSize()
查看collection数据的大小
db. users.totalIndexSize()
查询索引的大小
mongodb语法
MongoDB的好处挺多的,比如多列索引,查询时可以用一些统计函数,支持多条件查询,但是目前多表查询是不支持的,可以想办法通过数据冗余来解决多表查询的问题。
MongoDB对数据的操作很丰富,下面做一些举例说明,内容大部分来自官方文档,另外有部分为自己理解。
查询colls所有数据
db.colls.find() //select * from colls
通过指定条件查询
db.colls.find({‘last_name’: ‘Smith’});//select * from colls where last_name=’Smith’
指定多条件查询
db.colls.find( { x : 3, y : “foo” } );//select * from colls where x=3 and y=’foo’
指定条件范围查询
db.colls.find({j: {$ne: 3}, k: {$gt: 10} });//select * from colls where j!=3 and k>10
查询不包括某内容
db.colls.find({}, {a:0});//查询除a为0外的所有数据
支持<, <=, >, >=查询,需用符号替代分别为$lt,$lte,$gt,$gte
db.colls.find({ “field” : { $gt: value } } );
db.colls.find({ “field” : { $lt: value } } );
db.colls.find({ “field” : { $gte: value } } );
db.colls.find({ “field” : { $lte: value } } );
也可对某一字段做范围查询
db.colls.find({ “field” : { $gt: value1, $lt: value2 } } );
不等于查询用字符$ne
db.colls.find( { x : { $ne : 3 } } );
in查询用字符$in
db.colls.find( { “field” : { $in : array } } );
db.colls.find({j:{$in: [2,4,6]}});
not in查询用字符$nin
db.colls.find({j:{$nin: [2,4,6]}});
取模查询用字符$mod
db.colls.find( { a : { $mod : [ 10 , 1 ] } } )// where a % 10 == 1
$all查询
db.colls.find( { a: { $all: [ 2, 3 ] } } );//指定a满足数组中任意值时
$size查询
db.colls.find( { a : { $size: 1 } } );//对对象的数量查询,此查询查询a的子对象数目为1的记录
$exists查询
db.colls.find( { a : { $exists : true } } ); // 存在a对象的数据
db.colls.find( { a : { $exists : false } } ); // 不存在a对象的数据
$type查询$type值为bsonhttp://bsonspec.org/数 据的类型值
db.colls.find( { a : { $type : 2 } } ); // 匹配a为string类型数据
db.colls.find( { a : { $type : 16 } } ); // 匹配a为int类型数据
使用正则表达式匹配
db.colls.find( { name : /acme.*corp/i } );//类似于SQL中like
内嵌对象查询
db.colls.find( { “author.name” : “joe” } );
1.3.3版本及更高版本包含$not查询
db.colls.find( { name : { $not : /acme.*corp/i } } );
db.colls.find( { a : { $not : { $mod : [ 10 , 1 ] } } } );
sort()排序
db.colls.find().sort( { ts : -1 } );//1为升序2为降序
limit()对限制查询数据返回个数
db.colls.find().limit(10)
skip()跳过某些数据
db.colls.find().skip(10)
snapshot()快照保证没有重复数据返回或对象丢失
count()统计查询对象个数
db.students.find({‘address.state’ : ‘CA’}).count();//效率较高
db.students.find({‘address.state’ : ‘CA’}).toArray().length;//效率很低
group()对查询结果分组和SQL中group by函数类似
distinct()返回不重复值
Mongodb与MySQL之间的比较分析
本篇文章给大家带来的内容是关于
在数据库存放的数据中,有一种特殊的键值叫做主键,它用于惟一地标识表中的某一条记录。也就是说,一个表不能有多个主键,并且主键不能为空值。无论是MongoDB还是MySQL,都存在着主键的定义。
对于MongoDB来说,其主键名叫”_id”,在生成数据的时候,如果用户不主动为其分配一个主键的话,MongoDB会自动为其生成一个随机分配的值。
在MySQL中,主键的指定是在MySQL插入数据时指明PRIMARY KEY来定义的。当没有指定主键的时候,另一种工具 —— 索引,相当于替代了主键的功能。索引可以为空,也可以有重复,另外有一种不允许重复的索引叫惟一索引。如果既没有指定主键也没有指定索引的话,MySQL会自动为数据创建一个。
存储速度对比
1、数据库的平均插入速率:MongoDB不指定_id插入 > MySQL不指定主键插入 > MySQL指定主键插入 > MongoDB指定_id插入。
2、MongoDB在指定_id与不指定_id插入时速度相差很大,而MySQL的差别却小很多。
分析:
1、在指定_id或主键时,两种数据库在插入时要对索引值进行处理,并查找数据库中是否存在相同的键值,这会减慢插入的速率。
2、在MongoDB中,指定索引插入比不指定慢很多,这是因为,MongoDB里每一条数据的_id值都是唯一的。当在不指定_id插入数据的时候,其_id是系统自动计算生成的。MongoDB通过计算机特征值、时间、进程ID与随机数来确保生成的_id是唯一的。而在指定_id插入时,MongoDB每插一条数据,都需要检查此_id可不可用,当数据库中数据条数太多的时候,这一步的查询开销会拖慢整个数据库的插入速度。
3、MongoDB会充分使用系统内存作为缓存,这是一种非常优秀的特性。我们的测试机的内存有64G,在插入时,MongoDB会尽可能地在内存快写不进去数据之后,再将数据持久化保存到硬盘上。这也是在不指定_id插入的时候,MongoDB的效率遥遥领先的原因。但在指定_id插入时,当数据量一大内存装不下时,MongoDB就需要将磁盘中的信息读取到内存中来查重,这样一来其插入效率反而慢了。
4、MySQL不愧是一种非常稳定的数据库,无论在指定主键还是在不指定主键插入的情况下,其效率都差不了太多。
插入稳定性分析
插入稳定性是指,随着数据量的增大,每插入一定量数据时的插入速率情况。
在本次测试中,我们把这个指标的规模定在10w,即显示的数据是在每插入10w条数据时,在这段时间内每秒钟能插入多少条数据。
先呈现四张图上来:
1、 MongoDB指定_id插入:
2、 MongoDB不指定_id插入:
3、 MySQL指定PRIMARY KEY插入:
4、 MySQL不指定PRIMARY KEY插入:
总结:
1、整体上的插入速度还是和上一回的统计数据类似:MongoDB不指定_id插入 > MySQL不指定主键插入 > MySQL指定主键插入 > MongoDB指定_id插入。
2、从图中可以看出,在指定主键插入数据的时候,MySQL与MongoDB在不同数据数量级时,每秒插入的数据每隔一段时间就会有一个波动,在图表中显示成为规律的毛刺现象。而在不指定插入数据时,在大多数情况下插入速率都比较平均,但随着数据库中数据的增多,插入的效率在某一时段有瞬间下降,随即又会变稳定。
3、整体上来看,MongoDB的速率波动比MySQL的严重,方差变化较大。
4、MongoDB在指定_id插入时,当插入的数据变多之后,插入效率有明显地下降。在其他三种的插入测试中,从开始到结束,其插入的速率在大多数的时候都固定在一个标准上。
分析:
1、毛刺现象是因为,当插入的数据太多的时候,MongoDB需要将内存中的数据写进硬盘,MySQL需要重新分表。这些操作每当数据库中的数据达到一定量级后就会自动进行,因此每隔一段时间就会有一个明显的毛刺。
2、MongoDB毕竟还是新生事物,其稳定性没有已应用多年的MySQL优秀。
3、MongoDB在指定_id插入的时候,其性能的下降还是很厉害的。
4、在读取的数据规模不大时,MongoDB的查询速度真是一骑绝尘,甩开MySQL好远好远。
5、在查询的数据量逐渐增多的时候,MySQL的查询速度是稳步下降的,而MongoDB的查询速度却有些起伏。
分析:
1、如果MySQL没有经过查询优化的话,其查询速度就不要跟MongoDB比了。MongoDB可以充分利用系统的内存资源,我们的测试机器内存是64GB的,内存越大MongoDB的查询速度就越快,毕竟磁盘与内存的I/O效率不是一个量级的。
2、本次实验的查询的数据也是随机生成的,因此所有待查询的数据都存在MongoDB的内存缓存中的概率是很小的。在查询时,MongoDB需要多次将内存中的数据与磁盘进行交互以便查找,因此其查询速率取决于其交互的次数。这样就存在这样一种可能性,尽管待查询的数据数目较多,但这段随机生成的数据被MongoDB以较少的次数从磁盘中取出。因此,其查询的平均速度反而更快一些。这样看来,MongoDB的查询速度波动也处在一个合理的范围内。
3、MySQL的稳定性还是毋庸置疑的。
结论
1、相比较MySQL,MongoDB数据库更适合那些读作业较重的任务模型。MongoDB能充分利用机器的内存资源。如果机器的内存资源丰富的话,MongoDB的查询效率会快很多。
2、在带”_id”插入数据的时候,MongoDB的插入效率其实并不高。如果想充分利用MongoDB性能的话,推荐采取不带”_id”的插入方式,然后对相关字段作索引来查询。
3、MongoDB适合那些对数据库具体数据格式不明确或者数据库数据格式经常变化的需求模型,而且对开发者十分友好。
4、MongoDB官方就自带一个分布式文件系统,可以很方便地部署到服务器机群上。MongoDB里有一个Shard的概念,就是方便为了服务器分片使用的。每增加一台Shard,MongoDB的插入性能也会以接近倍数的方式增长,磁盘容量也很可以很方便地扩充。
5、MongoDB还自带了对map-reduce运算框架的支持,这也很方便进行数据的统计。
MongoDB的缺陷
1、事务关系支持薄弱。这也是所有NoSQL数据库共同的缺陷,不过NoSQL并不是为了事务关系而设计的,具体应用还是很需求。
2、稳定性有些欠缺,这点从上面的测试便可以看出。
3、MongoDB一方面在方便开发者的同时,另一方面对运维人员却提出了相当多的要求。业界并没有成熟的MongoDB运维经验,MongoDB中数据的存放格式也很随意,等等问题都对运维人员的考验。
以上就是Mongodb与MySQL之间的比较分析的详细内容,更多请关注php中文网其它相关文章!
MongoDB与MySQL关于写确认的异同
云妹导读:
所谓写确认,是指用户将数据写入数据库之后,数据库告知用户写入成功的一个概念。根据数据库的特点和配置,可以在不同的写入程度上,返回给用户,而这其中,就涉及到了不同的性能、数据安全等级以及数据一致性的内容。
不同的写入确认级别或配置,是数据库提供给用户的一种自我控制的能力,用户可以针对自身业务的特点、数据管理的需要、性能的考虑、数据一致性以及服务可用性各种因素进行考虑,选择适合的数据库配置,来实现自身的需要。
首先介绍几个重要的概念,这些概念也是数据库中常识性的知识了,不过是在不同数据库的不同表述。
这些概念主要涉及到写确认的两个重要考量点,一个是本地数据库写操作的不丢失,一个是分布式环境下,数据冗余的一致性。
本地数据库写操作是指数据库在处理用户的写操作后,能够持续化,防止因为意外导致的数据丢失,这个主要涉及到日志,比如MySQL中的redo log和MongoDB中的journal日志。
数据冗余的一致性是指多副本的环境下,比如主从或复制集架构下,数据写入主节点后,如何实现从节点与主节点的数据一致,而主从之间是以另外一个日志实现数据同步的,比如MySQL的binlog和MongoDB中的oplog日志。
另外防止主节点崩溃,数据未能同步到从节点,导致从节点成为新的主节点后,未同步数据丢失,也是写确认中重要的内容,即不但同步数据,而且要让数据安全快速的同步。
redo/journal
MySQL的redo log和MongoDB的journal日志都是数据库存储引擎层面的WAL(Write-Ahead Logging)预写式日志,记录的是数据的物理修改,是提高数据系统持久性的一种技术。
redo log
redo log是MySQL的默认存储引擎innodb事务日志中的核心日志文件之一,俗称重做日志,主要用作前滚的数据恢复。
当我们想要修改MySQL数据库中某一行数据的时候,innodb是把数据从磁盘读取到内存的缓冲池上进行修改。这个时候数据在内存中被修改,与磁盘中相比就存在了差异,我们称这种有差异的数据为脏页。innodb对脏页的处理不是每次生成脏页就将脏页刷新回磁盘,这样会产生海量的io操作,严重影响innodb的处理性能,因此并不是每次有了脏页都立刻刷新到磁盘中。既然脏页与磁盘中的数据存在差异,那么如果在这期间数据库出现故障就会造成数据的丢失。
而redo log就是为了解决这个问题。由于redo log的存在,可以延迟刷新脏页到磁盘的时间,保障了数据库性能的情况下提高了数据的安全。虽然增加了redo log刷新的开销,但是由于redo log采用的顺序io,比数据页的随机io要快很多,这额外的开销可接受。
即,数据库先将数据页的物理修改情况写到刷盘较快的redo log文件中,防止数据丢失。一旦发生故障,数据库重启恢复的时候,可以先从redo log把未刷新到磁盘的已经提交的物理数据页恢复回来。
journal
journal是MongoDB存储引擎层面的概念,MongoDB主要支持的mmapv1、wiredtiger、mongorocks等存储引擎,都⽀持配置journal。MongoDB可以基于journal来恢复因为崩溃未及时写到磁盘的信息。
MongoDB 所有的数据写⼊、读取最终都是调存储引擎层的接⼝来存储、读取数据,journal 是存储引擎存储数据时的一种辅助机制。
在MongoDB的4.0版本以前,用户可以设置是否开启journal日志;从4.0版本开始,副本集成员必须开启journal功能。
以wiredtiger为例,如果不配置journal,写入wiredtiger的数据,并不会立即持久化存储;而是每分钟会做一次全量的checkpoint( storage.syncPeriodSecs配置项,默认为1分钟),将所有的数据持久化。如果中间出现宕机,那么数据只能恢复到最近的一次checkpoint,这样最多可能丢掉1分钟的数据。
所以建议「一定要开启journal」,开启journal后,每次写入会记录一条操作日志(通过journal可以重新构造出写入的数据)。这样即使出现宕机,启动时 Wiredtiger 会先将数据恢复到最近的一次checkpoint的点,然后重放后续的 journal操作日志来恢复数据。
binlog/oplog
MySQL的binlog和MongoDB的oplog都是数据库层面的写操作对应的逻辑日志,主要用于实现数据在主备之间的同步复制以及增量备份和恢复。
binlog
binlog是MySQL数据库层面的一种二进制日志,不管底层使用的什么存储引擎,对数据库的修改都会产生这种日志。binlog记录操作的方法是逻辑性语句,可以通过设置log-bin=mysql-bin来启动该功能。
binlog中记录了有关写操作的执行时间、操作类型、以及操作的具体内容,比如SQL语句(statement)或每行实际数据的变更(row)。
上图是MySQL主从之间是如何实现数据复制的,其中的三个重要过程是:
- 主库(Master)把数据库更改记录到binlog(图中的Binary Log)中;
- 备库(Slave)将主库上的binlog复制到自己的中继日志(Relay log)中;
- 备库读取中继日志中的事件,将其重放(Replay)到备库数据之上。
这样源源不断的复制,实现了数据在数据库节点之间的一致。
oplog
oplog是MongoDB数据库层面的概念,在复制集架构下,主备节点之间通过oplog来实现节点间的数据同步。Primary中所有的写入操作都会记录到MongoDB Oplog中,然后从库会来主库一直拉取Oplog并应用到自己的数据库中。这里的Oplog是MongoDB local数据库的一个集合,它是Capped collection,通俗意思就是它是固定大小,循环使用的。
oplog 在 MongoDB 里是一个普通的 capped collection,对于存储引擎来说,oplog只是一部分普通的数据而已。
只有按复制集架构启动的节点会自动在local库中创建oplog.rs的集合。
oplog中记录了有关写操作的操作时间、操作类型、以及操作的具体内容,几乎保留的每行实际数据的变更(在4.0及以后版本中,一个事务中涉及的多个文档,会写在一条oplog中)。
上图是MongoDB主备之间如何实现数据复制的,其中的四个重要过程是:
- 主库(Primary)把数据库更改记录到oplog(图中的Capped Oplog集合)中;
- 备库(Secondary)把主库上的oplog拉取到自己的回放队列中(Queue)中;
- 备库读取队列中的oplog,批量回放(applyOps)到备库数据中;
- 再将队列中的Oplog写入到备库中的oplog.rs集合中。
这样源源不断的复制,实现了数据在数据库节点之间的一致。
另外MongoDB支持链式复制,即oplog不一定从Primary中获取,还可以从其他Secondary获取。上图是MongoDB主备之间如何实现数据复制的,其中的四个重要过程是:
- 主库(Primary)把数据库更改记录到oplog(图中的Capped Oplog集合)中;
- 备库(Secondary)把主库上的oplog拉取到自己的回放队列中(Queue)中;
- 备库读取队列中的oplog,批量回放(applyOps)到备库数据中;
- 再将队列中的Oplog写入到备库中的oplog.rs集合中。
这样源源不断的复制,实现了数据在数据库节点之间的一致。
另外MongoDB支持链式复制,即oplog不一定从Primary中获取,还可以从其他Secondary获取。
redo与binlog
- redo log是在innodb存储引擎层产生,而binlog是MySQL数据库的上层产生的,并且binlog不仅仅针对innodb存储引擎,MySQL数据库中的任何存储引擎对于数据库的更改都会产生binlog。
- 两种日志记录的内容形式不同。MySQL的binlog是逻辑日志,其记录是对应的SQL语句或行的修改内容。而innodb存储引擎层面的redo log是物理日志。
- 两种日志与记录写入磁盘的时间点不同,binlog只在事务提交完成后进行一次写入。而innodb存储引擎的redo log在事务进行中不断地被写入,并日志不是随事务提交的顺序进行写入的。
- binlog仅在事务提交时记录,并且对于每一个事务,仅在事务提交时记录,并且对于每一个事务,仅包含对应事务的一个日志。而对于innodb存储引擎的redo log,由于其记录是物理操作日志,因此每个事务对应多个日志条目,并且事务的redo log写入是并发的,并非在事务提交时写入,其在文件中记录的顺序并非是事务开始的顺序。
- binlog不是循环使用,在写满或者重启之后,会生成新的binlog文件,redo log是循环使用。
- binlog可以作为恢复数据使用,主从复制搭建,redo log作为异常宕机或者介质故障后的数据恢复使用。
journal与oplog
journal日志是在wiretiger、mmapV1等存储引擎层产生,而oplog是MongoDB数据库的主从复制层面的概念,oplog也与存储引擎无关;
两种日志记录的内容形式不同。MongoDB的oplog是逻辑日志,其记录的是对应的写操作的内容。而journal存储的物理修改;
两种日志与记录写入磁盘的时间点不同。
MongoDB 复制集里写入一个文档时,需要修改如下数据
- 将文档数据写入对应的集合
- 更新集合的所有索引信息
- 写入一条oplog用于同步 最终存储引擎会将所有修改操作应用,并将上述3个操作写⼊到一条 journal 操作日志里。
- journal不是循环使用,在写满或者重启之后,会生成新的journal文件,oplog是循环使用;
- oplog可以作为恢复数据使用,复制集架构,journal作为一场宕机或者介质故障后的数据恢复使用。
写确认
写确认这个概念其实是来自于MongoDB中的write concern,描述的是MongoDB对一个写操作的确认(acknowledge)等级。而MySQL中对应的这个概念,可以理解为,用户在提交(commit)写操作的时候,需要经过哪些操作之后就会告知用户提交成功。
MongoDB
在MongoDB中,数据库支持基于write concern功能使用户配置灵活的写入策略,则不同的策略对应不同的数据写入程度即返回给用户写入成功,用户可以继续操作下一个写请求。
write concern
write concern支持3个配置项:
{ w: , j: , wtimeout: }
其中:
- w,该参数要求写操作已经写入到个节点才向用户确认;
- {w: 0} 对客户端的写入不需要发送任何确认,适用于性能要求高,但不关注正确性的场景;
- {w: 1} 默认的writeConcern,数据写入到Primary就向客户端发送确认;
- {w: "majority"} 数据写入到副本集大多数成员后向客户端发送确认,适用于对数据安全性要求比较高的场景,该选项会降低写入性能;
- j,该参数表示是否写操作要进行journal持久化之后才向用户确认;
- {j: true} 要求primary写操作进行了journal持久化之后才向用户确认;
- {j: false} 要求写操作已经在journal缓存中即可向用户确认;journal后续会将持久化到磁盘,默认是100ms;
- wtimeout,该参数表示写入超时时间,w大于1时有效;当w大于1时,写操作需要成功写入若干个节点才算成功,如果写入过程中节点有故障,导致写操作迟迟不能满足w要求,也就一直不能向用户返回确认结果,为了防止这种情况,用户可以设置wtimeout来指定超时时间,写入过程持续超过该时间仍未结束,则认为写入失败。
副本集下的写确认
下面以一个副本集架构来描述,一个写操作的流程,来认识MongoDB下的写确认。
上面这个写操作,{w:2},需要至少两个节点写成功才可以返回给用户写成功;而每个节点的写入成功可以基于参数{j}来判断,如果{j:true},则每个节点写入操作的journal都刷盘才可以;如果{j:false},则写入操作的journal在缓存中即可以返回成功;
另外,MongoDB的Primary如何知道Secondary是否已经同步成功呢,是基于如下流程:
- Client向Primary发起请求,指定writeConcern为{w: "majority"},Primary收到请求,本地写入并记录写请求到oplog,然后等待大多数节点都同步了这条/批oplog(Secondary应用完oplog会向主报告最新进度);
- Secondary拉取到Primary上新写入的oplog,本地重放并记录oplog。为了让Secondary能在第一时间内拉取到主上的oplog,find命令支持一个awaitData的选项,当find没有任何符合条件的文档时,并不立即返回,而是等待最多maxTimeMS(默认为2s)时间看是否有新的符合条件的数据,如果有就返回;所以当新写入oplog时,备立马能获取到新的oplog;
- Secondary上有单独的线程,当oplog的最新时间戳发生更新时,就会向Primary发送replSetUpdatePosition命令更新自己的oplog时间戳;
- 当Primary发现有足够多的节点oplog时间戳已经满足条件了,向客户端发送确认,这样,Primary即可知道数据已经同步到了。
MySQL
MySQL数据库在所谓写确认或写成功方面可以通过执行事务的commit提交来体现,提交成功则为写成功。因此我主要从事务在执行事务以及commit事务的过程中,涉及的redo log、binlog以及两种日志的刷盘和主从复制的流程来分析MySQL的写成功相关的设置和问题。
MySQL复制架构
目前MySQL较为流量的版本包括5.5、5.6、5.7、8.0,而8.0版本中使用的Group Replication来实现多节点的数据一致性,这种组复制依靠分布式一致性协议(Paxos协议的变体),实现了分布式下数据的最终一致性。
MySQL中有几种常见复制机制:
- 同步复制。当主库提交事务之后,所有的从库节点必须收到、Replay并且提交这些事务,然后主库线程才能继续做后续操作。但缺点是,主库完成一个事务的时间会被拉长,性能降低。
- 异步复制。主库将事务 Binlog 事件写入到 Binlog文件中,此时主库只会通知一下 Dump 线程发送这些新的Binlog,然后主库就会继续处理提交操作,而此时不会保证这些 Binlog 传到任何一个从库节点上。
- 半同步复制。是介于全同步复制与全异步复制之间的一种,主库只需要等待至少一个从库节点收到并且 Flush Binlog 到 Relay Log 文件即可,主库不需要等待所有从库给主库反馈。同时,这里只是一个收到的反馈,而不是已经完全完成并且提交的反馈,如此,节省了很多时间。
- 组复制。由若干个节点共同组成一个复制组,一个事务的提交,必须经过组内大多数节点(N / 2 + 1)决议并通过,才能得以提交。比如由3个节点组成一个复制组,Consensus层为一致性协议层,在事务提交过程中,发生组间通讯,由2个节点决议(certify)通过这个事务,事务才能够最终得以提交并响应。
除了组复制,半同步复制技术是性能和安全相对更好的设计,尤其在5.7版本中,优化了之前版本的半同步复制相关的逻辑,因此我们主要以5.7版本来介绍。
MySQL5.6/5.5半同步复制的原理:提交事务的线程会被锁定,直到至少一个Slave收到这个事务,由于事务在被提交到存储引擎之后才被发送到Slave上,所以事务的丢失数量可以下降到最多每线程一个。因为事务是在被提交之后才发送给Slave的,当Slave没有接收成功,并且Master挂了,会导致主从不一致:主有数据,从没有数据。这个被称为AFTER_COMMIT。
MySQL5.7在Master事务提交的时间方面做了改进,事务是在提交之前发送给Slave(AFTER_SYNC),当Slave没有接收成功,并且Master宕机了,不会导致主从不一致,因为此时主还没有提交,所以主从都没有数据。
不过假如Slave接收成功,并且Master中的binlog未来得及刷盘并且在存储引擎提交之前宕机了,那么很明显这个事务是不成功的,但由于对应的Binlog已经做了Sync操作,从库已经收到了这些Binlog,并且执行成功,相当于在从库上多了数据,也算是有问题的,但多了数据,问题一般不算严重。此时可能就需要8.0版本中的组复制了。
MySQL写确认行为
我们以MySQL的5.7版本的半同步复制的主从架构的为例子,来介绍MySQL各个参数对写确认即commit的不同影响。
上图中,能够体现半同步复制(AFTER SYNC)的过程为:
- 第6步,写binlog;(sync_binlog参数在此起作用)
- 第7步,同步binlog到备库的Relay log;(sync_relay_log参数在此起作用)
- 第8步,返回给主库ack;
- 第9步和第10步,提交事务,将redo log中该事务标记为已提交;(innodb_flush_log_at_trx_commit参数在此起作用)
- 第11步,返回给用户写成功;
上图中,能体现redo log和binlog顺序一致性的过程为:
- 第4步,将redo log置为prepare并刷盘;
- 第6步,写入binlog;(sync_binlog参数在此起作用)
- 第9步和第10步,提交事务,并将redo log置为提交状态;
下面将把上面介绍的与刷盘有关的配置项引入这整个过程,来看写操作不同的行为和风险。
注意:以上是在开始内部两阶段提交的流程,即innodb_support_xa=true,这个时候可以通过判断binlog来恢复会提交的事务,因此innodb_flush_log_at_trx_commit看起来可有可无;如果未开启内部事务的两阶段提交,则更会复杂,只有innodb_flush_log_at_trx_commit = 1 且 sync_binlog = 1 且 sync_relay_log = 1的情况下,可以保证已提交事务的安全,其他情况都有可能导致数据丢失或者主从数据不一致的风险。
但是在innodb_flush_log_at_trx_commit = 1 且 sync_binlog = 1 且 sync_relay_log = 1 的情况下,MySQL的性能相对最低。可以在提高性能的情况下,比如 innodb_flush_log_at_trx_commit = 2 且 sync_binlog = N (N为500 或1000),由于这种情况,redo log和binlog都在系统缓存中,可以使用带蓄电池后备电源的缓存cache,防止系统断电异常。
此外,rpl_semi_sync_master_wait_for_slave_count参数是控制同步到多少个节点的,类似MongoDB中write concern中的 w 参数,如果这个参数设置为0(其实不能,最低1),则变为了纯粹的异步复制;如果这个参数设置为最大(所有从节点个数),则变为了纯粹的同步复制,因此这个地方也可以根据需要来进行调整,来提交数据的安全。
同时,有可能影响同步模式的还包括rpl_semi_sync_master_wait_no_slave参数、影响复制等待超时的参数rpl_semi_sync_master_timeout等。当rpl_semi_sync_master_wait_no_slave为OFF时,只要master发现Rpl_semi_sync_master_clients小于rpl_semi_sync_master_wait_for_slave_count,则master立即转为异步模式;如果为ON时,如果在事务提交阶段(master等待ACK)超时rpl_semi_sync_master_timeout,master会转为异步模式。
对比
配置比较
其他
虽然MongoDB和MySQL在很多方面可以有类似或相似的设置,但是还是存在一些区别,比如:
- MongoDB的journal中包含了oplog的信息;而binlog和redo log是两个相对独立的内容;
- MySQL几乎所有的写操作都是基于事务来提交的;而MongoDB在4.0开始支持多文档的事务,单文档的事务基于内部事务逻辑实现,未直接提供给用户;
- MySQL的写确认是已commit提交成功为标志,MongoDB的普通写操作是返回写入成功为标志;事务写操作也是已commit为标志;
总结
本文章所介绍的写确认的概念,涉及到了MongoDB与MySQL的日志文件(redo log/journal)、同步用日志(binlog/oplog)、刷盘机制和时机、主从同步架构等多个流程和模块,目的就是实现写操作的原子性、持久性、分布式环境下的数据一致性等,对数据的性能和安全都有影响,需要根据数据、业务、压力、安全等客观因素去调整。
由于涉及的内容非常多,未对所有的情况进行测试验证,可能有疏漏或错误,希望大家不吝赐教。也希望本篇内容对于对MySQL和MongoDB都有兴趣的同学可以作为一个总结和参考。
参考资料
高性能MySQL(https://item.jd.com/11220393.html)
MongoDB官方手册(https://docs.mongodb.com/manual/)
深入浅出MongoDB复制(https://mongoing.com/archives/5200)
mysql基于binlog的复制(https://blog.csdn.net/u012548016/article/details/86584293)
MongoDB journal 与 oplog,究竟谁先写入?(https://mongoing.com/archives/3988)
MySQL5.7新特性--官方高可用方案MGR介绍(https://www.cnblogs.com/luoahong/articles/8043035.html)
MongoDB writeConcern原理解析(https://mongoing.com/archives/2916)
mysql日志系统之redo log和bin log(https://www.jianshu.com/p/4bcfffb27ed5)
MySQL 5.7 半同步复制增强【转】(https://www.cnblogs.com/mao3714/p/8777470.html)
MySQL 中Redo与Binlog顺序一致性问题 【转】(https://www.cnblogs.com/mao3714/p/8734838.html)
详细分析MySQL事务日志(redo log和undo log)(https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html)
MySQL 的"双1设置"-数据安全的关键参数(案例分享)(https://www.cnblogs.com/kevingrace/p/10441086.html)
rpl_semi_sync_master_wait_no_slave 参数研究实验(https://www.cnblogs.com/konggg/p/12205505.html)
MySQL5.7新特性半同步复制之AFTER_SYNC/AFTER_COMMIT的过程分析和总结(http://blog.itpub.net/15498/viewspace-2143986/)
以上,Enjoy~
点击【阅读】,可了解更多数据库相关详请
关于mongoDB与mySQL的比较-为什么在某些方面要比另一个好[关闭]和mongodb和mysql对比的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于mangodb与mysql的区别及部署、Mongo db 与mysql 语法比较、Mongodb与MySQL之间的比较分析、MongoDB与MySQL关于写确认的异同等相关知识的信息别忘了在本站进行查找喔。
本文标签: