对象模型,快速响应业务变化:
快速的开发:
原生的高可用:
横向扩展能力:
tar -xvf dump.tar.gz
mongorestore --uri="mongodb://root:root@10.130.0.12/?&authMechanism=SCRAM-SHA-1"
// 插入
db.fruit.insertOne({name: "apple"})
db.fruit.insertMany([
{name: "apple"},
{name: "pear"},
{name: "orange"},
])
// 查询
db.customers.find({username: "fmiller", name: "Elizabeth Ray"})
db.customers.find({username: /^f/})
db.customers.find({$or: [{username: /^f/}, {name: /^E/}]})
a <> 1
{a: {$ne: 1}}
a > 1
{a: {$gt: 1}}
a >= 1
{a: {$gte: 1}}
a < 1
{a: {$lt: 1}}
a <= 1
{a: {$lte: 1}}
a=1 OR b=1
{$or: [{a: 1}, {b: 1}]}
a IS NULL
{a: {$exists: false}}
a IS (1,2,3)
{a: {$in: [1, 2, 3]}}
// 同时满足
{$eleMatch: {"city": "Rome", "country": "USA"}}
// 投影 projection
// like select
db.customers.find({username: /^f/}, {name: 0, email: 0})
db.customers.find({username: /^f/}, {_id: 1, name: 1})
// remove
db.customers.remove({username: "abrown"})
// update
db.customers.updateOne({username: "fmiller"}, {$set: {from: "China"}})
db.customers.updateMany({username: "fmiller"}, {$set: {from: "China"}})
// $set $unset
// $push $pushAll $pop 数组操作
// $pull $pullAll 如果匹配,从数组中删除相应的对象
// $addToSet 如果不存在则增加一个值到数组
// drop
db.fruit.drop()
show collections
db
db.dropDatabase()
show dbs
Aggregation Framework
pipeline = [stage1, stage2, ...]
db.<collection>.aggregate(
pipeline,
{ option }
)
// 计算总合计
db.orders.aggregate([
{
$group: {
_id: null,
total: {$sum: "$total"}
}
}
])
// {
// "_id": null,
// "total": NumberDecimal("44019609")
// }
// 查询 2019 第一季度,已完成订单(completed)总金额(金额+运费)和订单总数
db.orders.aggregate([
{
$match: {
status: "completed",
orderDate: { $gte: ISODate("2019-01-01"), $lt: ISODate("2019-04-01") }
}
},
{
$group: {
_id: null,
total: { $sum: "$total" },
shippingFee: { $sum: "$shippingFee" },
count: { $sum: 1 }
}
},
{
$project: {
grandTotal: { $add: ["$total", "$shippingFee"] },
count: 1,
_id: 0
}
}
])
// {
// "count": 5875,
// "grandTotal": NumberDecimal("2636376")
// }
由3个以上具有投票权的节点组成:
数据是如何复制的:
通过选举完成故障恢复:
影响选举的因素:
复制集节点有以下常见的选配项:
复制集注意事项:
mkdir -p runtime/data_db{1,2,3} && \
mkdir -p runtime/data_configdb{1,2,3}
hostname -f
rs.status()
概念模型 CDM -> 逻辑模型 LDM -> 物理模型 PDM 对象 -> 实体、属性、关系 -> 表结构、字段列表、主外建
无模式的由来:可以省略无论建模的具体过程,物理模型可省。
设计原则:
集合、字段、基础形状 -> 引用及关联 -> 最终模式
业务需求及逻辑模型 –逻辑导向-> 基础建模 —> 集合、字段、基础形状
一个文档 16MB max.
内嵌为主。
技术需求、读写比例、方式及数据 –技术导向-> 工况细化 —> 引用及关联
引用模式 $lookup:
db.contacts.aggregate([
$lookup: {
form: "groups",
localField: "groups_ids",
foreignField: "groups_id",
as: "groups",
}
])
使用引用方式:
使用引用的设计:
经验和学习 –模式导向-> 套用设计模式 -> 优化的模型
时序数据,分桶设计:利用文档内嵌组,将一个时间段的数据聚合到一个文档里。大量减少文档数据量,大量减少索引占用空间。
大文档,很多字段,很多索引。列转行。列数据变化为数组。多语言多国家属性,类似字段需要建立很多索引。转化为数组,一个索引解决所有查询问题。
模型灵活了,如何管理文档不同版本?增加一个版本字段。schema_version。
统计网页点击流量。近似计算。if random(0,9)==0 increment by 10。
业绩排名、游戏排名等精确统计。消耗资源多,聚合计算时间长。用预聚合字段。模型中直接增加统计字段,每次更新数据时同时更新统计值。
{ w: <value>, j: <boolean>, wtimeout: <number> }
rs.status();
db.test.drop()
db.test.insert({count:2}, {writeConcern: {w: "majority"}})
db.test.insert({count:2}, {writeConcern: {w: 3}})
db.test.insert({count:2}, {writeConcern: {w: 1}})
// WriteResult({ "nInserted" : 1, "writeConcernError" : [ ] })
db.test.insert({count:2}, {writeConcern: {w: 4}})
// [Error] 100 - Not enough data-bearing nodes
db.test.find()
// 配置延迟节点,模拟网络延迟
conf=rs.conf()
// {
// "_id": 3,
// "host": "mongo3:27017",
// "arbiterOnly": false,
// "buildIndexes": true,
// "hidden": false,
// "priority": 1,
// "tags": { },
// "slaveDelay": NumberLong("0"),
// "votes": 1
// }
conf.members[2].slaveDelay=5
// 没有选举权
conf.members[2].priority=0
rs.reconfig(conf)
// {
// "_id": 3,
// "host": "mongo3:27017",
// "arbiterOnly": false,
// "buildIndexes": true,
// "hidden": false,
// "priority": 0,
// "tags": { },
// "slaveDelay": NumberLong("5"),
// "votes": 1
// }
db.test.insert({count:2}, {writeConcern: {w: 3}})
// Result Time 5s
// 3s 超时
db.test.insert({count:5}, {writeConcern: {w: 3, wtimeout:3000}})
// writeResult({
// "nInserted" : 1,
// "writeConcernError" : {
// "code" : 64,
// "codeName" : "WriteConcernFailed",
// "errmsg" : "waiting for replication timed out",
// "errInfo" : {
// "wtimeout" : true,
// "writeConcern" : {
// "w" : 3,
// "wtimeout" : 3000,
// "provenance" : "clientSupplied"
// }
// }
// }
// })
配置:
实验:
// 主节点
db.test.drop()
db.test.insert({count:2}, {writeConcern: {w: 3}})
db.test.find({})
// 1 line
db.test.find({}).readPref("secondary")
// 1 line
// 从节点 1、2
db.test.find({})
// 1 line
db.fsyncLock()
// now locked against writes, use db.fsyncUnlock() to unlock 锁住写入
// 主节点
db.test.insert({count:3})
db.test.find({})
// 2 line
db.test.find().readPref("secondary")
// 1 line
// 从节点 1、2
db.fsyncUnlock()
// 主节点
db.test.find().readPref("secondary")
enableMajorityReadConcern: true
// 从节点 1、2
db.fsyncLock()
// 主节点
db.test.insert({count:3})
db.test.find().readConcern("local")
// 如果在一个写操作到达大多数节点前读取了这个写操作,然后因为系统故障该操作回滚了,则发生了脏读
// {readConcern: "majority"} 可以避免脏读
db.test.find().readConcern("majority")
majority 约为:Read Committed。
实验启用事务后的隔离性:
db.tx.drop();
db.tx.insertMany([{x:1},{x:2}]);
var session = db.getMongo().startSession();
session.startTransaction();
var coll = session.getDatabase('test').getCollection('tx');
coll.updateOne({x:2}, {$set: {y:1}});
// {
// acknowledged: true,
// insertedId: null,
// matchedCount: 1,
// modifiedCount: 1,
// upsertedCount: 0
// }
coll.find();
// [
// { _id: ObjectId("64478074fcfac90fb90f1a65"), x: 1 },
// { _id: ObjectId("64478074fcfac90fb90f1a66"), x: 2, y: 1 }
// ]
db.tx.find();
// [
// { _id: ObjectId("64478074fcfac90fb90f1a65"), x: 1 },
// { _id: ObjectId("64478074fcfac90fb90f1a66"), x: 2 }
// ]
session.commitTransaction();
db.tx.find();
实验可重复读 Repeatable Read:
db.tx.drop();
db.tx.insertMany([{x:1},{x:2}]);
var session = db.getMongo().startSession();
session.startTransaction({
readConcern: {"level": "snapshot"},
writeConcern: {"w": "majority"}
});
var coll = session.getDatabase('test').getCollection('tx');
coll.findOne({x: 1});
// 模拟事务之外的操作
db.tx.updateOne({x:1}, {$set: {y:1}});
db.tx.find();
// [
// { _id: ObjectId("644782c3fcfac90fb90f1a69"), x: 1, y: 1 },
// { _id: ObjectId("644782c3fcfac90fb90f1a6a"), x: 2 }
// ]
coll.findOne({x: 1});
// { _id: ObjectId("644782c3fcfac90fb90f1a69"), x: 1 }
session.abortTransaction();
实验写冲突:
db.tx.drop();
db.tx.insertMany([{x:1},{x:2}]);
// shell 1、2
var session = db.getMongo().startSession();
session.startTransaction({
readConcern: {"level": "snapshot"},
writeConcern: {"w": "majority"}
});
var coll = session.getDatabase('test').getCollection('tx');
// shell 1
coll.updateOne({x:1}, {$set: {y:1}});
// ok
// shell 2
coll.updateOne({x:1}, {$set: {y:1}});
// MongoServerError: WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.
// session.abortTransaction();
类似触发器。
基于 oplog 实现。被追踪的变更事件主要包括:
可重复读。未开启 majority readConcern 的集群无法使用 Change Stream。当集群无法满足 {w: “majority”} 时,不会触发 Change Stream。
可以使用集合管道的过滤步骤过滤事件。
分片集群数据发布方式
片键:
组合片键。{user_id:1, time:1}
# 1 配置域名解析
# 2 准备分片目录
# 3 创建第1个分片复制集 member1:27010 member3:27010 member5:27010
mongod --bind_ip_all --replSet shard1 --shardsvr --wiredTigerCacheSizeGB 1
# --shardsvr 标注为分片节点
# --wiredTigerCacheSizeGB 1 缓存大小
# 4 初始化分片复制集
# 5 创建 config server 复制集 member1:27019 member3:27019 member5:27019
mongod --bind_ip_all --replSet config --configsvr --wiredTigerCacheSizeGB 1
# --configsvr 标注为配置节点
# 6 创建 config server 复制集
# 7 搭建 mongos member1:27017
mongos --bind_ip_all --configdb config/member1:27019,member3:27019,member5:27019,
# 连接到 mongos 添加分片
sh.addShard("shard1/member1:27010,member3:27010,member5:27010")
# 8 创建分片表
# 连接到 mongos member1:27017
sh.enableSharding("foo");
sh.shardCollection("foo.bar", {_id: "hashed"});
sh.status();
# 插入测试数据
use foo
for(var i=0;1<10000;i++) {
db.bar.insert({i:i))
}
# 9 创建第2个分片复制集 member2:27011 member4:27011 member6:27011
# 10 初始化第2个分片复制集
# 11 加入第2个分片
# 连接到 mongos 添加分片
sh.addShard("shard2/member2:27011,member4:27011,member6:27011")
如何获取监控数据:
db.createRole({
role: "readWriteRole",
privileges: [
{
resource: { db: "myDatabase", collection: "sample" },
actions: [ "find", "insert", "update", "remove" ]
}
],
roles: [{
role: 'read',
db: 'sampledb',
}]
})
db.createUser({
user: 'sampleUser',
pwd: 'pwd',
roles: [{
role: 'readWriteRole',
db: 'admin',
}]
})
# 禁止脚本执行
--noscripting
# 必须鉴权
--auth
B-树和B+树都是常见的多路搜索树结构,常用于数据库索引和文件系统中。它们的主要区别在于如何存储和检索数据。
B-树是一种自平衡的搜索树,其中每个节点可以存储多个键和对应的值,并支持在O(log n)时间内进行搜索、插入和删除操作。B-树的每个节点都包含了一个子节点数组,可以用来搜索和遍历树。在B-树中,所有节点都可以存储键和值,而非仅仅是叶子节点。
B+树与B-树非常相似,但是只有叶节点包含了所有的键和值,而且所有叶节点都通过指针链接在一起。这意味着在B+树上进行查找只需要搜索一条从根节点到叶节点的路径,而在B-树中可能需要搜索多个节点。B+树的非叶子节点只包含键,而不包含值,这使得B+树在维护索引时更加高效。
因此,B+树比B-树更适用于存储和检索大量数据,尤其是数据库和文件系统中的索引。B+树的叶子节点形成了一个有序链表,可以方便地进行区间查找和遍历。而B-树则更适合内存较小的情况下,例如缓存。
.explain(true) 查看:
executionTimeMillis
totalDocsExamined
executionStages.docsExamined
executionStages.inputStage.stage
组合索引 ESR 原则,Equal(最前) Sort(中间) Range(最后)
db.member.createIndex({city:1}, {background: true})
mongostat 了解 MongoDB 运行状态的工具。
mongotop 了解集合压力状态
优点:
从基于关系型数据库应用迁移到 MongoDB 的理由:
– EOF –