MongoDB基础使用

什么是MongoDB

MongoDB是一个NoSQL的非关系型数据库,支持海量数据存储,高性能读写。

在使用过程中,需要搞清楚MongoDB的体系结构:

  1. Mongo中的集合相当于MySQL中的

  2. Mongo中的文档相当于MySQL中的

  3. Mongo中的相当于MySQL中的字段/列

Mongo与MySQL、Redis对比:

特性/维度 MySQL MongoDB Redis
数据模型 关系型数据库,使用预定义的表、行、列;数据模式固定 文档型数据库,采用 BSON/JSON 格式,模式灵活,无需事先定义 Schema 键值型数据库,支持字符串、哈希、列表、集合、有序集合等多种数据结构
存储方式 数据存储在硬盘上,依赖索引与缓存优化查询 结合内存映射与磁盘存储,常将热点数据加载到内存中;支持分片和副本集 数据主要存储在内存中,通过 RDB/AOF 方式实现持久化,适合高速读写
性能与扩展性 性能稳定,复杂查询表现优异,但水平扩展较为复杂 高扩展性,适合大数据量场景,支持自动分片,能快速响应热点数据 响应速度极快,适合高并发场景;扩展依赖内存容量,支持集群分片管理
事务与数据一致性 完整支持 ACID 事务,保证强一致性 近期开始支持多文档事务,但跨分片时可能采用最终一致性模型 仅支持简单事务(MULTI/EXEC),不支持回滚,主要用于缓存及非关键场景
适用场景 适用于结构化数据存储、复杂关联查询及对数据一致性要求较高的系统(如金融、ERP) 适用于非结构化或半结构化数据、大数据量、内容管理和日志存储等互联网应用 适用于缓存、会话管理、排行榜、消息队列和实时数据处理等对速度要求极高的场景

Docker安装MongoDB

  1. 拉去镜像

    docker pull mongo
  2. 创建容器

    docker run --restart=always --name mongo \
    -p 27017:27017 \
    -e TZ=Asia/Shanghai \
    -v /data/mongo/data:/data/db \
    -v /data/mongo/log:/data/log \
    --privileged=true \
    -e MONGO_INITDB_ROOT_USERNAME=mongo \
    -e MONGO_INITDB_ROOT_PASSWORD=mongo \
    -d mongo

PS:MONGO_INITDB_ROOT_USERNAMEMONGO_INITDB_ROOT_PASSWORD是初始的用户名密码

  1. 连接MongoDB连接MongoDB除了可以用官网提供的GUI工具,还可以使用VsCode插件进行连接,在VsCode插件库中搜索MongoDB for VS Code安装,然后输入连接地址即可。如果你选择使用VsCode连接Mongo,你可以使用JavaScript的形式来操作数据库,但是无法直接使用Shell,还是需要下载MongoSh

MongoDB基础使用

--查看当前数据库
show databases
show dbs
--切换数据库
use 数据库名

PS:use可以使用一个并不存在的数据库,这是MongoDB的特点,当插入数据时,如果数据库不存在,会自动创建

--插入数据
db.users.insertOne({name: "laoyang"})
db.users.insertOne({name: "zhangsan", age: 18})
db.users.insertMany([{name: "lisi"},{name: "wangwu"}])

acknowledged表述是否插入成功

insertedId数据全局唯一ID

插入的数据字段不一定要相同符合

--查寻全部数据
db.users.find()

--只查询一条数据
db.users.find().limit(1)

--查询条件,查询level为3的数据,这里要注意level为数字,如果你传入字符串则查询失败
db.users.find({level: 3})

--设置返回字段,查询时只返回name和email,如果设置为0 则不返回字段
db.users.find({level: 3},{name: 1, email: 1}})

--查询level大于3的数据
db.users.find({level: {$gt: 3}})

--类似于Sql中的in
db.users.find({level: {$in: [1,3}})

--查询存在email字段的数据,也可以传入1表示true 0表示false
--注意:$exists只能判断字段是否存在,不能用于字段中的值是否存在
db.users.find({email: {$exists: true}})

--查询level大于等于3和小于等于5的数据,Sql中的And,两个语句同样的效果
db.users.find({level: {$gte: 3, $lte: 5}})
db.users.find({$and: [{level: {$gte: 3}}, {level: {$lte: 5}}]})

--类似于Sql中的or
db.users.find({$or: [{level: {$gte: 3}}, {level: {$lte: 5}}]})

--查询level不为3的数据
db.users.find({level: {$not: {$eq: 3}}})

--用正则表达式查询,带'张'的数据,$options表述忽略大小写
db.users.find({name: {$regex: //}})
db.users.find({name: {$regex: //, $options: 'i'}})

--查询第一条数据
db.users.findOne({level: {$gte: 3}})

--查询排序,根据level字段排序,1是升序,-1是降序
db.users.find().sort({level: 1})
db.users.find().sort({level: 1,name: -1})

--skip用于跳过数据,跳过了第一条
db.users.find().sort({level: 1}).skip(1)

--更新level为1的第一条数据,如果money不存在,则直接插入
db.users.updateOne({level: 1},$set: {{money: 100}})

--更新多条数据
db.users.updateMany({level: 1},$set: {{money: 100}})

--删除level为1的第一条数据
db.users.deleteOne({level: 1})

--删除level为1的多条数据
db.users.deleteMany({level: 1})

聚合

--查询users表的数据行数,里面参数和find类似
db.users.countDocuments()
db.users.countDocuments({level: {$gte: 3}})

常见查询条件

中文 符号
小于 $lt
大于 $gt
小于或等于 $lte
大于或等于 $gte
不等于 $ne

MongoDB数据备份

MongoDB提供了4个命令行工具给我们进行数据库备份与恢复以及导入导出数据

备份与恢复的数据文件格式是bson格式,二进制文件,不需要进入mongo终端

导入与导出的数据文件格式是json格式,文本文档,不需要进入mongo终端

--备份数据库
mongodump -h dbhost -d dbname -o uri
mongodump -h 127.0.0.1:27017 -d user_table -o /home/desk

--恢复数据库 --drop表示删库再恢复 不保留已有数据
mongorestore -h dbhost -d dbname --dir uri --drop
mongorestore -h 127.0.0.1:27017 -d user_table --dir /home/desk --drop
--导出数据
mongoexport -h dbhost -d dbname -c collectionname -o file --type json/csv -f field
mongoexport -h 127.0.0.1:27017 -d test -c user -o /home/desk/user.json --type json

--导入数据
mongoimport -h dbhost -d dbname -c collectionname --file filename --type json/csv --headerline -f field
选项 作用 备注
-h 服务端地址 –host=address –port=port
-d 数据库名称 –db=数据库名称
-c 集合名称 –collection=集合名称
-o 保存文件名称
-f 导出字段列表
–type 导出文件格式 默认json,当为csv时,需要加上-f ‘字段1,字段2…’

MongoDB用户管理

db.createUser(user,pwd,writeConcem)

创建一个用户用db.createUser,如果用户存在会返回错误

db.createUser({
    user: '<用户名>',
    pwd: '<密码>',
    customData: {'xxx':'xxx','yyy‘:'yyy'},//任意用户描述
    roles: [
        {role: '<角色名>',db:'<数据库名>'},//    
    ]
})

PS:

MongoDB的用户是以数据库为单位分别划分管理的,每个数据库有自己的管理员

管理员只可以管理自己的数据库

内置角色:

数据库用户角色:read,readWrite

数库管理角色:dbAdmin,dbOwner,userAdmin

集群管理角色:clusterAdmin,clusterManager,clusterMonitor,hostManager

备份恢复角色:backup,restore

所有数据库角色:readAnyDatabase,readWriteAnyDatabase,userAdminAnyDatabase,dbAdminAnyDatabase

超级用户角色:root

Read:允许用户读取指定数据库
readWrite:允许用户读写指定数据库
dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile

userAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户

clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限

readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限

readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限

userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限

dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限

root:只在admin数据库中可用。超级账号,摊有超级权限

--查看当前数据库下用户
use dataName
show users

--查看所有用户
use admin
//db.auth('root','123456')//如果开启了访问控制,需要验证才能操作
db.system.users.find()//只能在admin数据库中使用
db.system.users.find().pretty()//5.0之前的版本用这个

--删除用户
db.system.users.remove({user:'test'})

--修改密码
db.changeUserPassword('账户名称','新密码')

MongoDB索引

MongoDB的索引是存储在运行内存(RAM)中的,必须确保索引大小不超过内存限制,如果超过内存限制,会自动删除部分索引,这涉及到MongoDB的驱逐机制

同样,MongoDB的索引在部分查询条件下不生效:

  • 正则表达式及非操作符,如$nin$not

  • 算数运算符,如$mod

  • $where自定义查询函数

  • ……

如果项目是写多读少,建议少量使用或者不使用缓存,因为索引会在写入时重排

使用索引注意事项:

  1. 一个集合中索引数量不能超过64个

  2. 索引名称不能超过126个字符

  3. 复合索引最多有31个字段

  4. 索引会在system.index集合中管理,这个集合只能通过createIndexdropIndexes操作

--获取当前集合中已创建的所有集合信息
db.集合.getIndex()

--获取当前集合中已创建集合的索引总大小(KB)
db.集合.totalIndexSize()

--获取当前数据库中所有的索引
db.system.indexes.find()

MongoDB在插入文档会默认生成_id字段索引

查询分析

与SQL类似,MongoDB提供了一个explan,供开发者进行分析

explain的使用有3个参数,分别为queryPlannerexecutionStatsallPlansExecution,默认是queryPlanner,开发中常用executionStats

db.orders.find({'title','愉快-6'}).explain('executionStats')

executionStats.stage扫描类型

类型名称 描述 期望
COLLSCAN 全表扫描 False
IXSCAN 索引扫描 True
FETCH 根据索引检索指定document True
IDHACK 针对_id进行查询 True
COUNTSCAN count不使用Index进行count时返回 False
COUNT_SCAN count使用了Index进行count时返回 True
SUBPLA 未使用到索引的$or查询时返回 False
TEST 使用全文索引进行查询时返回 -
SORT 使用sort排序但是无index时返回 False
SKIP 使用skip跳过但是无index时返回 False
PROJECTION 使用limit限定结果但是无index时返回 False

索引创建

MongoDB支持多种类型的索引,普通索引符合索引多列索引全文索引哈希索引地理位置索引等,支持不同场合,还有一种TTL索引,是在普通索引基础上加了过期时间,但字段类型必须为Date,到期删除数据,全文索引在MongoDB中很不好用,建议使用ES或者Sphinx

--创建索引
db.集合.createIndex({
    //单个字段,则为普通索引,sort表示排序,1升序2降序
    "字段名1":<sort|type>,//type为text则为全文索引,
    "字段名2":<sort|type>,//多个字段为复合索引
    "字段名3":[<1>,<2>],//多列索引
    ...
},{
    background: <Boolean>,//建立索引会阻塞数据库其他操作,可以指定是否异步,默认False
    unique:<Boolean>,//是否建立唯一索引,默认False
    name:<String>,//索引名称
    expireAfterSeconds:<integer>,//过期时间,TTL索引
    sparse:<Boolean>//对文档中不存在的字段数据是否启用索引,默认False
})

MongoDB进阶

MongoDB集群有三种模式,主从模式副本集模式sharding分片集模式

官网不推荐上生产环境上部署主从模式,主要是安全性太低。副本集和sharding分片集模式目前使用的最广的方案,通常这2种方案的选择通过数据量和并发数来权衡。

在GB级别的基本上副本集方案可满足,TB级别或以上采用sharding分片集模式,分片集模式可以解决单机容量和单机并发能力。这两种既有自己的优势也有自己的缺点,比如sharding分片集模式分片越多,性能自然下降越多

副本集

副本集也叫复制集,本质上来说就是一种具有监控能力的主从模式,类似redis中的哨兵主从模式。

一组MongoDB复制集,就是一组MongoDB进程,这些进程维护同一个数据集合。复制集提供了数据冗余和高等级的可靠性,这是生产部署的基础。说白了,就是高级主从,它可以保证数据在生产部署时的兄余和可靠性,通过在不同的机器上保存副本来保证数据的不会因为单点损坏而丢失。能够随时应对数据丢失、机器损坏带来的风险

副本集基本架构

  • 副本集:一组副本集就是一组MongDB实例组成的集群,由一个主(Primary)服务器和多个备份(Secondary)服务器构成

  • 主节点(Primary):主节点接受所有写入操作,对其数据集所做的所有更改记录到oplog日志

  • 副节点(Secondary):复制主节点的oplog日志并将其操作应用到其数据集,如果主节点不可用,一个合格的副节点将被选举为新的主节点

  • 仲裁节点(Arbiter):负责选举,当主节点不可用,它将从副节点选一个作为主节点

副本集的基本架构又3台服务器组成,有两种模式组成:

  1. 三成员的复制集,有3个主从节点(1主2从)

  2. 两成员的复制集,有2个主从节点,一个仲裁节点(1主1从1仲裁)

如果没有仲裁节点,从节点也会自行选举,仲裁节点只会进行投票选举,本身不会成为主库或从库

Primary选举

复制集通过replsetlnitiate命令(或mongo shellrs.initiate())进行初始化,初始化后各个成员间开始发送心跳消息,并发起Priamry选举操作,获得【【多数(总服务器-仲裁节点/2>50%)】成员投票支持的节点,会成为Primary主节点,其余节点成为Secondary

【大多数】的定义
假设复制集内投票成员数量为N,则大多数为 N/2+1,当复制集内存活成员数量不足大多数时,整个复制集将无法选举出复制集将无法提供写服务,外于只读状态。

通常建议将复制集的数量设置为奇数

Docker-Compose搭建MongoDB复制集

MongoDB使用keyFile认证,副本集中的每个MongoDB实例使用keyFile内容作为认证其他成员的共享密码。MongoDB实例只有拥有正确的keyFile才可以加入副本集。

keyFile的内容必须是6到1024个字符的长度,且副本集所有成员的keyFile内容必须相同。

## 生成KeyFile方式
## 400权限是要保证安全性,否则mongod启动会报错
openssl rand -base64 756 > mongodb.key
chmod 400 mongodb.key
version: "3"

services:
  #主节点
  mongodb1:
    image: mongo:6.0.6
    container_name: mongo1
    restart: always
    ports:
      - 27017:27017
    environment:
      - MONGO_INITDB_ROOT_USERNAME=root
      - MONGO_INITDB_ROOT_PASSWORD=mongodb@evescn
    command: mongod --replSet rs0 --keyFile /mongodb.key
    volumes:
      - /etc/localtime:/etc/localtime
      - /data/mongodb/mongo1/data:/data/db
      - /data/mongodb/mongo1/configdb:/data/configdb
      - /data/mongodb/mongo1/mongodb.key:/mongodb.key
    networks:
      - mongoNet
    entrypoint:
      - bash
      - -c
      - |
        chmod 400 /mongodb.key
        chown 999:999 /mongodb.key
        exec docker-entrypoint.sh       $$@
  # 副节点
  mongodb2:
    image: mongo:6.0.6
    container_name: mongo2
    restart: always
    ports:
      - 27018:27017
    environment:
      - MONGO_INITDB_ROOT_USERNAME=root
      - MONGO_INITDB_ROOT_PASSWORD=mongodb@evescn
    command: mongod --replSet rs0 --keyFile /mongodb.key
    volumes:
      - /etc/localtime:/etc/localtime
      - /data/mongodb/mongo2/data:/data/db
      - /data/mongodb/mongo2/configdb:/data/configdb
      - /data/mongodb/mongo2/mongodb.key:/mongodb.key
    networks:
      - mongoNet
    entrypoint:
      - bash
      - -c
      - |
        chmod 400 /mongodb.key
        chown 999:999 /mongodb.key
        exec docker-entrypoint.sh $$@
  # 副节点
  mongodb3:
    image: mongo:6.0.6
    container_name: mongo3
    restart: always
    ports:
      - 27019:27017
    environment:
      - MONGO_INITDB_ROOT_USERNAME=root
      - MONGO_INITDB_ROOT_PASSWORD=mongodb@evescn
    command: mongod --replSet rs0 --keyFile /mongodb.key
    volumes:
      - /etc/localtime:/etc/localtime
      - /data/mongodb/mongo3/data:/data/db
      - /data/mongodb/mongo3/configdb:/data/configdb
      - /data/mongodb/mongo3/mongodb.key:/mongodb.key
    networks:
      - mongoNet
    entrypoint:
      - bash
      - -c
      - |
        chmod 400 /mongodb.key
        chown 999:999 /mongodb.key
        exec docker-entrypoint.sh $$@
networks:
  mongoNet:
    driver: bridge

集群部署

mkdir /data/mongodb/mongo{1,2,3}/{data,configdb} -pv

## 提供redis.conf配置
cp mongodb.key /data/mongodb/mongo1/
cp mongodb.key /data/mongodb/mongo2/
cp mongodb.key /data/mongodb/mongo3/

docker-compose up -d

进入主节点初始化集群

use admin
db.auth('root', 'mongodb@evescn')
rs.initiate({_id:"rs0",members:[ 
{_id:0,host:"10.0.0.1:27017"}, 
{_id:1,host:"10.0.0.1:27018"}, 
{_id:2,host:"10.0.0.1:27019"}]   
})
## 查看副本集配置
rs.config();
## 查看副本集状态
rs.status();

分片集

当数据量比较大的时候(TB级别以上),我们需要把数据分片保存并运行在不同的服务器中,以降低CPU、内存和IO的压力,分片集(Sharding Set)就是MongoDB用于横向扩容的技术。

分片是数据跨多台机器存储,MongoDB使用分片来支持具有非常大的数据集和高吞吐量操作的部署。MongoDB分片技术类似MySQL的水平切分和垂直切分,Mongo数据库实现Shardin分片集主要有两种方式:垂直扩展/横向切分(也叫水平扩展)

  1. 垂直扩展的方式就是进行计算机资源扩容,添加更多的CPU,内存,磁盘空间等。

  2. 水平扩展则是通过数据分片的方式将数据集分布在多个服务器上,通过集群统一提供服务。

分片为应对高吞吐量与大数据量提供了方法。使用分片减少了每个分片需要处理的请求数,因此通过水平扩展,集群可以提高自己的存储容量和吞吐量。

举例来说,当插入一条数据时,项目应用只需要访问存储这条数据的分片.使用分片减少了每个分片存储的数据。

例如如果数据库1tb的数据集,并有4个分片,然后每个分片可能仅持有256 GB的数据。如果有40个分片,那么每个切分可能只有25GB的数据。

分片机制的优势

  1. 对集群进行抽象,让集群“不可见”

    MongoDB自带了一个叫做Mongos的专有路由进程。Mongos就是掌握统一路口的路由器,其会将客户端发来的请求准确无误的路由到集群中的一个或者一组服务器上,同时会把接收到的响应拼装起来发回到客户端

  2. 保证集群总是可读写

    MongoDB通过多种途径来确保集群的可用性和可靠性。将MongoDB的分片和复制功能结合使用,在确保数据分片到多台服务器的同时,也确保了每分数据都有相应的备份,这样就可以确保有服务器换掉时,其他的从库可以立即接替坏掉的部分继续工作

  3. 使集群易于扩展

    当系统需要更多的空间和资源的时候,MongoDB使我们可以按需方便的扩充系统容量

分片集群架构

组件 说明
ConfigServer 配置服务器,是一个 mongod 实例,用于存储整个分片集群的 元数据,包括所有分片节点的信息、数据分片的路由规则、分片配置等关键内容。默认需要部署 3 个 Config Server 节点,以确保高可用性。

Config Server 中保存的信息包括:
• 所有数据的存取方式
• 所有 shard 节点的信息
• 分片功能的配置细节
Mongos 路由服务,负责与客户端进行交互。所有对数据库的操作请求都必须通过 mongos 进行转发。

• 启动后会从 Config Server 加载最新的元数据
• 根据路由规则将请求发送到对应的 shard 节点
• 本身不存储数据

在实际部署中,通常会部署多个 mongos 实例,以提升访问性能与容错能力。
Mongod 数据分片中的核心组件,负责实际的数据存储。每个 shard 通常由一个或多个 mongod 实例组成,用于存储具体的业务数据。

• 数据以 chunk(数据块)为单位进行分片存储
• mongod 是唯一持久化数据的组件

多个 mongod 共同构成了一个完整的分片集群。
Diagram of a sample sharded cluster for production purposes.  Contains exactly 3 config servers, 2 or more ``mongos`` query routers, and at least 2 shards. The shards are replica sets.

Mongos的路由功能
当客户端连接mongoDB写入数据时,MongoDB Cluster根据分片键设计写入数据。
当外部语句发起数据查询时,MongoDB根据数据分布自动路由至指定节点返回数据。

分片集群中的数据分布

  1. 使用chunk(数据块)来把数据存储不同的分片(shard)

  2. 集群搭建完成之后,默认开启一个chunk,大小是64M,chunk的大小容量可以通过配置chunksize修改。

  3. 存储数据的容量只要超过64M,chunk会进行平均分裂,如果单位时间存储需求很大,设置更大的chunk

  4. chunk会被自动均衡迁移到各个Shard分片节点下面

Chunk(数据块)

在 MongoDB 中,chunk(数据块) 是用于分割和管理数据的基本单位,便于系统进行数据的分裂(切割)和迁移(平衡负载)。

在每个分片节点内部,MongoDB 会将存储的数据划分为多个 chunk,每个 chunk 代表该节点中一部分数据的范围。

chunk 的两个主要作用:
  1. 分裂(Splitting):
    当某个 chunk 的大小超过了配置项 chunkSize 所设定的上限(默认是 128MB),MongoDB 会自动将这个 chunk 拆分成两个或多个更小的 chunk,以防止单个 chunk 过大,影响系统性能和数据迁移效率。

  2. 迁移(Balancing):
    MongoDB 的后台进程 balancer 负责在各个分片之间迁移 chunk,从而实现数据的负载均衡。通过迁移 chunk,可以避免某些分片节点数据过多而其他节点数据过少的情况。

Chunksize的选择

在 MongoDB 中,chunkSize(分片数据块的大小)默认是 128MB(具体值可能随版本略有不同)。如果没有特殊需求,建议保持默认值,或者设置在 100MB ~ 200MB 之间。

设置合适的 chunkSize 对系统性能非常关键,因为它会直接影响 chunk 的分裂和迁移行为,而这些操作会消耗大量的 I/O 资源

chunk 什么时候会分裂?
  • 只有在 插入或更新数据 时才会触发分裂。

  • 读取数据 不会触发分裂。

  • chunk 只会分裂,不会合并

chunkSize 的优缺点:

优点:

  • chunk 更小,数据分布更均匀。

  • 迁移更快,灵活性更高。

缺点:

  • 分裂频率高,系统资源消耗大(尤其是路由节点的负担)。

  • 如果 chunkSize 过小,容易出现 jumbo chunk —— 即某个分片键的值非常集中,导致这部分数据无法再继续分裂,从而不能迁移。

chunkSize 的优缺点:

优点:

  • 分裂频率低,系统压力较小。

缺点:

  • 迁移速度慢,I/O 消耗集中。

  • chunk 内可能包含大量文档,迁移过程中占用资源较多,甚至可能因为太大而无法迁移。

分片键

分片键就是集合中的一个字段,MongoDB根据这个字段的值来决定数据该存储到哪个分片。分片键必须满足以下几个条件:

  • 必须是索引字段(系统会在调用 sh.shardCollection 时自动为分片键创建索引,前提是该索引不存在)。

  • 每个文档必须包含分片键,不能为 null 或缺失。

  • 分片键一旦设定就不能修改

  • 大小不能超过 512 bytes

分片方式

MongoDB 提供两种主要的分片策略:

  1. 基于范围的分片(Range Sharding)

    • 将数据按照分片键的值的范围进行划分。

    • 常用于有序数据,如时间戳、自增ID等。

    • 优点:范围查询效率高,适用于连续数据分析。

    • 缺点:若使用自增字段作为分片键,会导致数据集中写入某一分片,出现写入热点,负载不均。

  2. 基于哈希的分片(Hash Sharding)

    • MongoDB 对分片键进行哈希计算,然后按哈希值范围划分数据块。

    • 哈希后的值是伪随机的,相近分片键的文档通常不会落在同一数据块中。

    • 优点:数据分布更加均匀,有效防止热点分片,提升写入扩展性。

    • 缺点:不适合范围查询,因为哈希打乱了原始顺序,范围查询往往需要广播到所有分片后再汇总结果,效率较低。

范围分片适合有序范围查询但可能有热点问题,哈希分片适合写入密集型应用但牺牲了一部分查询效率,两者可根据业务场景灵活选择。

查询与性能
  • 查询时应尽量包含分片键,以便 mongos 能快速定位目标分片。

  • 如果查询条件不包含分片键,mongos 会将请求广播到所有分片,然后合并结果(归并排序),效率较低。

注意事项
  • 已分片的集合不能插入不包含分片键的文档。

  • 不允许插入分片键为 null 或缺失的文档。

  • 分片键在集群中起到了路由作用,是性能优化的关键。

Docker-Compose搭建MongoDB分片集

TODO

SpringBoot与MongoDB

  1. 引入Maven包

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
  2. 添加YML配置&映射实体类

    spring:
      data:
        mongodb:
          ## 最好采用这种写法 不然可能有些报错
          uri: mongodb://root:123456@192.168.0.107:27017/game?authSource=admin
    //指定实体类和数据库文档的映射关系 默认实体类名  数据库如果没有该文档,会自动创建
    @Document(value="users")
    public class GameUser {
        @Id
        private ObjectId id; //mongoDB推荐使用ID
        //指定属性名和数据库域的映射关系   默认属性名
        @Field("name")
        private String name;
        @Field("age")
        private Integer age;
        @Field("gold")
        private Integer gold;
    }
  3. 基于MongoTemplate增删改查操作

    基础的操作方法:

    mongoTemplate.findAll(User.class)

    mongoTemplate.findById(, User.class)

    mongoTemplate.find(query, User.class)

    mongoTemplate.upsert(query, update, User.class)

    mongoTemplate.remove(query, User.class)

    mongoTemplate.insert(User)

    mongoTemplate.save(User)

    import com.example.mongostudy.Entity.GameUser;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.domain.Sort;
    import org.springframework.data.mongodb.core.MongoTemplate;
    import org.springframework.data.mongodb.core.query.Criteria;
    import org.springframework.data.mongodb.core.query.Query;
    import org.springframework.data.mongodb.core.query.Update;
    
    import java.util.List;
    
    @SpringBootTest
    class MongoStudyApplicationTests {
    
        @Autowired
        private MongoTemplate mongoTemplate;
    
        @Test
        void find() {
            //设置查询条件 age小于20,且name="laoyang"
            Criteria criteria = Criteria.where("age").lt(20).and("name").is("laoyang");
    
            Query query = new Query(criteria);
            List<GameUser> userList = mongoTemplate.find(query, GameUser.class);
    
            userList.forEach(user -> System.out.println(user));
        }
    
        @Test
        void pageFind(){
            //设置查询条件 age小于20,且person_name="张三"
            Criteria criteria = Criteria.where("age").lt(20).and("name").is("laoyang");
    
            //根据条件 查询总数
            Query queryCount = new Query(criteria);
            long count = mongoTemplate.count(queryCount, GameUser.class);
    
            //查询当前页的数据列表, 查询第二页,每页查询2条
            Query queryLimit = new Query(criteria)
                    .with(Sort.by(Sort.Order.desc("age")))
                    .limit(2)//每页查询条数
                    .skip(2); //从第几页开始 (page-1)*size
    
            List<GameUser> userList = mongoTemplate.find(queryLimit, GameUser.class);
            userList.forEach(user -> System.out.println(user));
        }
    
        @Test
        void save() {
            GameUser add = new GameUser();
            add.setName("张三");
            add.setAge(18);
            mongoTemplate.save(add);
        }
    
        @Test
        void update() {
            Criteria criteria = Criteria.where("name").is("张三");
            //设置更新条件
            Query query = new Query(criteria);
            //设置更新数据
            Update update = new Update();
            update.set("age", 16);
            mongoTemplate.upsert(query, update, GameUser.class);
        }
    
        @Test
        void dlete() {
            mongoTemplate.remove(Query.query(Criteria.where("name").is("张三")), GameUser.class);
        }
    }
  4. 基于MongoRepository增删改查

    这里不展开细讲,需要自己去了解

参考

MongoDB副本集部署:https://www.cnblogs.com/evescn/p/16203350.html