MongoDB入门实践

MongoDB 入门实践#

简单介绍 MongoDB,包括 MongoDB 的使用场景、和 MySQL 的对比、安装部署、Java 客户端访问及总结

MongoDB?#

我们遵循需求驱动技术的原则,通过一个场景来引入 MongoDB,在现行业务需求下考虑下面三个问题:

  • 为什么要使用 MongoDB?
  • MongoDB 是一个什么样的产品?
  • 它能做什么?

Situation(场景)##

以公司站点报告业务场景 (当前其实已换成 hdfs):

业务流程 ###

  • 从 Server 获取站点日志;
  • 日志分析后入 MySQL 库 (8~9 个字段,日期和多个维度字段和多个指标字段),每天数据量大约 50w;
  • 依照维度字段、指标字段、日期 (一般是每天) 聚合查询,查询到的结果集 (前 1000 条) 存入 Redis 供前端展现;
  • 目前每天展示的报告实际是当天凌晨通过离线计算 (相对时间较长) 得到的结果。

运行状态 ###

  • 数月时间运行后,MySQL 库当中的数据量倍增,整个数据库的查询效率大大下降;
  • Redis 的内存占用逐渐上升,并大量占用虚拟内存,导致服务器崩溃。
  • 并且由于 Redis 本身并不支持类似关系数据库的分页动作,只能存储少量数据作为前端展现,而不能一页一页展示整个结果集查询的结果。

Problem(问题)##

原有的解决方案(MySQL+Redis)

  • MySQL 存储的数据量太大导致查询效率太低;
  • 把 Redis 当作存储使用,导致对虚拟内存占用太高;
  • Redis 本身不支持关系型数据库的分页,实际展现数据并不完整。

MongoDB 特性

  • 大数据量存储,单实例 TB 级别
  • 高性能持久化数据:支持内存数据模型 (减少 IO),支持索引;
  • 高可用性 (副本集):自动故障切换 (automatic failover),数据备份 (data redundancy);
  • 自动扩容:自动分片,副本集保证最终一致性、低延迟高流量。

如此看来,MongoDB 太强大了,似乎一种招招要了 MySQL 命的感觉,不过千万别这么想,毕竟术业有专攻!
那现在就让我们来看看 MongoDB 的庐山真面目。

先睹为快#

插播一段广告,这里引用官方网站上 MongoDB 的查询示例比较下 MySQL 和 MongoDB。

MongoDB & MySQL 查询比较

  • MySQL 查询 (SELECT _id,name,address FROM users WHERE age>18 LIMIT 5)
    sql-select
  • MongoDB 查询 (db.users.find( { age: { $gt: 18} }, {name: 1, address: 1} ).limit(5))
    mongodb-find

有没有一种他们俩长的真像的感觉,没错你答对了,MongoDB 被称作是最像关系型数据库的 NOSQL 数据库。

How To Use#

温馨提示

  • MongoDB 存在企业版和社区版,企业版是收费版本 (咱是穷人,自然不用收费版了)
  • 推荐安装 64 位
  • 推荐 Linux 下安装
  • Windows 版本可用于调试

官方网站 ##

官网
官网 ORG

下载##

  • 选择 Current,Previous,Development
  • 选择 Windows,Linux,Max OS X,Solaris
  • 选择下载格式

文档##

  • 选择下载在线
  • 选择文档版本 (2.6)
  • 选择文档格式

安装 & 启动 & 关闭 ##

Linux

以 Linux ReadHat 为例

安装

  • 创建文件 mongdb.repo, 路径:/etc/yum.repos.d/mongodb.repo
  • 配置 mongdb.repo:
    64 位:
[mongodb]
name=MongoDB Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64/
gpgcheck=0
enabled=1

32 位:

name=MongoDB Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/i686/
gpgcheck=0
enabled=1
  • yum 安装完整版: sudo yum install -y mongodb-org

启动

  • selinux 开启 MongoDB tcp 端口: semanage port -a -t mongod_port_t -p tcp 27017
  • 关闭 SELINUX: SELINUX=disabled
  • 启动:sudo service mongod start
  • client: 执行 mongo 命令即可

关闭

  • sudo service mongod stop
  • 进入 mongo 的窗口:use admin; db.shutdownServer()

Windows 64 位

安装

  • 查看 windows 版本:wmic os get osarchitecture**
  • windows msi 或 zip(解压即可, 比如目录:D:\Program Files\mongodb)
  • 将 bin 目录加入到 classpath

启动

  • 解压目录下创建两级目录:data\db,data\log
  • 启动 server(mongod,mongos( 分片集群客户端))
    mongod.exe --dbpath "D:\Program Files\mongodb\data"(如果路径中存在空格,请使用 "" 引用路径)
  • client(* driver,mongo)
    mongo

关闭

  • Ctrl+C
  • 进入 mongo 的窗口:use admin; db.shutdownServer()

一般情况下,对于开发人员,我们更加关心 MongoDB 的 Driver, 和 Mysql 一样,它自然是多语言版的,作为 Java 开发人员,我们介绍 MongoDB Java Driver。

MongoDB Java Driver#

获取 Driver(pom)##

        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongo-java-driver</artifactId>
            <version>2.13.0</version>
        </dependency>

CRUD##

我们以 java 代码方式操作 MongoDB, 然后用图形化的工具查看结果.

insert###

  • 插入文档记录:{"name" : "someone" , "age" : 18 , "gender" : 1 , "address" : { "country" : "china" , "city" : "shanghai"}}
    @Test
    public void insert() throws Exception {
        // 新建客户端.
        MongoClient client = new MongoClient("127.0.0.1", 27017);
        // 获取 DB.
        DB db = client.getDB("testDB");
        // 获取文档集.
        DBCollection collection = db.getCollection("demo");
        // 一行记录.
        // json: {"name" : "someone" , "age" : 18 , "gender" : 1 , "address" : { "country" : "china" , "city" : "shanghai"}}
        BasicDBObject user = new BasicDBObject("name", "someone")
                .append("age", 18)
                .append("gender", 1)
                .append("address", new BasicDBObject("country", "china").append("city", "shanghai"));
    System.out.println(collection.insert(user));
    <span class="hljs-comment">// 关闭客户端</span>
    client.close();
}

insert-demo

query###

  • 查询 条件:{"name" : "someone"} 的结果集
    @Test
    public void query() throws Exception {
        // 新建客户端.
        MongoClient client = new MongoClient("127.0.0.1", 27017);
        // 获取 DB.
        DB db = client.getDB("testDB");
        // 获取文档集.
        DBCollection collection = db.getCollection("demo");
        // 条件: {"name" : "someone"}
        BasicDBObject match = new BasicDBObject("name", "someone");
        // 执行查询并打印结果集列表.
        System.out.println(collection.find(match).toArray());
        // 关闭客户端
        client.close();
    }

insert-demo

update###

  • 更新 条件: {"name" : "someone"} set:
    @Test
    public void update() throws Exception {
        // 新建客户端.
        MongoClient client = new MongoClient("127.0.0.1", 27017);
        // 获取 DB.
        DB db = client.getDB("testDB");
        // 获取文档集.
        DBCollection collection = db.getCollection("demo");
        // 条件.
        // json: {"name" : "someone"}
        BasicDBObject match = new BasicDBObject("name", "someone");
    <span class="hljs-comment">// set:{"age": 28}</span>
    <span class="hljs-type">BasicDBObject</span> <span class="hljs-variable">updatedValue</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BasicDBObject</span>();
    updatedValue.put(<span class="hljs-string">"age"</span>, <span class="hljs-number">28</span>);
    <span class="hljs-type">DBObject</span> <span class="hljs-variable">updateSetValue</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BasicDBObject</span>(<span class="hljs-string">"$set"</span>, updatedValue);

    <span class="hljs-comment">// 执行更新并打印.</span>
    <span class="hljs-comment">//update api: DBObject q, DBObject o, boolean upsert, boolean multi.</span>
    <span class="hljs-comment">//upsert:若为true,没有找到匹配的记录时会执行新增一条记录,false则不做任何处理.</span>
    <span class="hljs-comment">//multi:若为false,表示只更新查询到的一条记录,true则表示更新所有匹配记录.</span>
    System.out.println(collection.update(match, updateSetValue, <span class="hljs-literal">false</span>, <span class="hljs-literal">true</span>));
    <span class="hljs-comment">// 关闭客户端</span>
    client.close();
}

update-demo

delete###

  • 删除 条件:
    @Test
    public void delete() throws Exception {
        // 新建客户端.
        MongoClient client = new MongoClient("127.0.0.1", 27017);
        // 获取 DB.
        DB db = client.getDB("testDB");
        // 获取文档集.
        DBCollection collection = db.getCollection("demo");
        // 条件.
        // json: {"name" : "someone"}
        BasicDBObject match = new BasicDBObject("name", "someone");
        // 删除匹配记录.
        System.out.println(collection.remove(match));
        // 关闭客户端
        client.close();
    }

delete-demo

常用 Api##

常用类 ###

  • MongoClient 客户端连接抽象
  • DB 文档库
  • DBCollection 文档集
  • DBObject 文档对象基类
  • BasicDBObject 基础文档对象
  • BasicDBList 基础文档列表对象

常用方法 ###

  • DBCollection: find(),update(),insert(),remove(), MongoDB 大部分操作数据有关的 Api 都是基于此类.
  • MongoClient: getDB(), close()
  • DB: getCollection()
  • DBObject: put()
  • BasicDBObject: append()
  • BasicDBList: add()

第三方封装 (Third Party Frameworks and Libraries)##

基于 MongoDB 的 driver,有很多第三方封装的包,比如:

  • Spring MongoDB
    spring-mongodb
  • Jongo
    JONGO

大多数框架都支持以 POJO 的方式开发基于 MongoDB 的应用!

MongoDB & MySQL#

Indicator MongoDB MySQL
大数据支持 较强 一般
ACID 部分支持 全部
SQL 特性 非结构化,支持部分 SQL 语言特性 完全支持 SQL 语言规范,标准化,交互性强
扩展 自动扩展 有一定的硬件成本,需要第三方程序
应用灵活性 一般

MongoDB 与 MySQL 是互补的关系
MongoDB 没有全面的 ACID 支持,因此不能将有事务场景的业务底层存储从 MySQL 改为 NOSQL

MongoDB 最佳实践 (不全)#

  • 版本更新
  • 采用 Replicaton Sets(副本集) 部署
  • 采用 64 位系统部署
  • 默认开启 journaling 日志
  • 谨慎分片 sharding
  • 不要当做 Sql 数据库使用
  • 明确数据需要的一致性和可靠性
  • 按照真实业务场景全面测试
  • 采用固定的 Schema 设计 (某个 DBCollection 下的文档结构一致)
  • 采用索引 (和其它关系型数据使用索引的意义是一致的)

QA#