一丶 MongoDB簡介
對于社交類軟件的功能,我們需要對它的功能特點進行分析:
- 數據量會隨著用戶數增大而增大
- 讀多寫少
- 價值較低
- 非好友看不到其動態信息
- 地理位置的查詢
- …
針對以上特點進行分析各大存儲工具:
- mysql:關系型數據庫(效率低)
- reids: redis緩存(微博,效率高,數據格式不豐富)
- 對于大量數據而言,顯然不能使用關系型數據庫進行存儲,我們需要通過MongoDB進行存儲
- 對于讀多寫少的應用我們需要減少讀取成本,比如說:一條SQL語句,單表查詢一定比多表查詢更快
針對社交app,一般采用一下存儲策略:
- mongodb:存儲業務數據(圈子,推薦的數據,小視頻數據,點贊,評論等)
- redis:承擔的角色是緩存層(提升查詢效率)
- mysql:存儲和核心業務數據,賬戶
而對于mongoDB
- MongoDB是一個高效的非關系型數據庫(不支持表關系:只能操作單表)
- MongoDB是一個基于分布式文件存儲的數據庫,由C++語言編寫,旨在為WEB應用提供可擴展性高性能數據存儲解決方案
- MongoDB是一個介于關系型數據庫和非關系型數據庫之間的產品,是非關系數據庫當中功能最豐富,最像關系數據庫的,它支持的數據結構非常松散,是類似json的bson格式,因此可以存儲比較復雜的數據類型。
- MongoDB最大的特點是它支持的查詢語言非常強大,其語法有點類似于面向對象的查詢語言,幾乎可以實現類似關系數據庫單表查詢的絕大部分功能,而且還支持對數據建立索引。
- 官網: 添加鏈接描述
二丶MongoDB的特點
MongoDB 最大的特點是他支持的查詢語言非常強大,其語法有點類似于面向對象的查詢語言,幾乎可以實現類似關系數據庫單表查詢的絕大部分功能,而且還支持對數據建立索引。它是一個面向集合的,模式自由的文檔型數據庫。具體特點總結如下:
- 面向集合存儲,易于存儲對象型數據
- 模式自由
- 支持動態查詢
- 支持完全索引,包含內部對象
- 支持復制和故障恢復
- 使用高效的二進制數據存儲,包括大型對象(如視頻等)
- 自動處理碎片,以支持云計算層次的擴展性
- 支持 Python,PHP,Ruby,Java,C,C#,Javascript,Perl及C++語言的驅動程 序, 社區中也提供了對Erlang及.NET 等平臺的驅動程序
- 文件存儲格式為 BSON(一種 JSON 的擴展)
2.1通過docker 安裝MongoDB
拉取mongodb的鏡像,也可以通過docker-compose啟動,更方便:
docker-compose up -d
#查看容器
docker ps -a
2.2 MongoDB體系結構
MongoDB的羅杰結構是一種層次結構,主要由文檔(document),集合(collection),數據庫(databases)三部分組成,邏輯結構是,面向用戶的,用戶使用MongoDB開發應用程序就是使用這樣結構:
- MongoDB 的文檔(document),相當于關系數據庫中的一行記錄。
- 多個文檔組成一個集合(collection),相當于關系數據庫的表
- 多個集合(collection),邏輯上組織在一起,就是數據庫
- 應該MongoDB示例支持多個數據庫database.文檔document,集合collection
類似這樣:
為了更好的理解,可以與SQL中的概念進行對比
SQL術語/概念 | MongoDB術語/概念 | 解釋/說明 |
---|---|---|
database | database | 數據庫 |
table | collection | 數據庫表/集合 |
row | document | 表中的一條數據 |
column | field | 數據字段/域 |
index | index | 索引 |
table joins | 表連接,MongoDB不支持 | |
primary key | primary key | 主鍵,MongoDB自動將_id字段設置為主鍵 |
2.3 數據類型
- 數據格式:BSON{aa:bb}
- null:用于表示空值或者不存在的字段,{“x”:null}
- 布爾型:布爾類型有兩個值true和false,{“x”:true}
- 數值,shell默認使用64位浮點型數值。{“x”:3.14}或{“x”:3}。對于整型值,可以使用 NumberInt(4字節符號整數)或NumberLong(8字節符號整數), {“x”:NumberInt(“3”)}{“x”:NumberLong(“3”)}
- 字符串:UTF-8字符串都可以表示為字符串類型的數據{“x”:“你好”}
- 日期:日期被存儲為自新紀元依賴經過的毫秒數,不存儲時區,{“x”:new Date()}
- 正則表達式:查詢時,使用正則表達式作為限定條件,語法與JavaScript的正則表達式相同{“x”😕[abc]/}
- 數組:數據列表或數據集可以表示為數組,{“x”: [“a“,“b”,”c”]}
- 內嵌文檔:文檔可以嵌套其他文檔,被嵌套的文檔作為值來處理,{“x”:{“y”:3 }}
- 對象Id:對象id是一個12字節的字符串,是文檔的唯一標識,{“x”: objectId() }
- 二進制數據:二進制數據是一個任意字節的字符串。它不能直接在shell中使用。如果要 將非utf-字符保存到數據庫中,二進制數據是唯一的方式。
三丶MongoDB入門
3.1 數據庫以及表操作
#查看所有的數據庫
> show dbs#通過use關鍵字切換數據庫
> use admin#創建數據庫
#說明:在MongoDB中,數據庫是自動創建的,通過use切換到新數據庫中,進行插入數據即可自動創建數據庫
> use testdb> show dbs #并沒有創建數據庫> db.user.insert({id:1,name:'zhangsan'}) #插入數據> show dbs#查看表
> show tables> show collections#刪除集合(表)
> db.user.drop()
true #如果成功刪除選定集合,則 drop() 方法返回 true,否則返回 false。#刪除數據庫
> use testdb #先切換到要刪除的數據中> db.dropDatabase() #刪除數據庫
3.2 新增數據
在MongoDB中,存儲的文檔結構是一種類似于json的結構,稱之為bson(全稱為:Binary JSON)。
#插入數據
#語法:db.表名.insert(json字符串)> db.user.insert({id:1,username:'zhangsan',age:20})> db.user.find() #查詢數據
3.3 更新數據
update() 方法用于更新已存在的文檔。語法格式如下:
db.collection.update(<query>,<update>,[upsert: <boolean>,multi: <boolean>,writeConcern: <document>]
)
參數說明:
- query : update的查詢條件,類似sql update查詢內where后面的。
- update : update的對象和一些更新的操作符(如 , , ,inc.$set)等,也可以理解為sql update查詢內set后面的
- upsert : 可選,這個參數的意思是,如果不存在update的記錄,是否插入objNew,true為插入,默認是false,不插入。
- multi : 可選,mongodb 默認是false,只更新找到的第一條記錄,如果這個參數為true,就把按條件查出來多條記錄全部更新。
- writeConcern :可選,拋出異常的級別。
#查詢全部
> db.user.find()#更新數據
> db.user.update({id:1},{$set:{age:22}}) #注意:如果這樣寫,會刪除掉其他的字段
> db.user.update({id:1},{age:25})#更新不存在的字段,會新增字段
> db.user.update({id:2},{$set:{sex:1}}) #更新數據#更新不存在的數據,默認不會新增數據
> db.user.update({id:3},{$set:{sex:1}})#如果設置第一個參數為true,就是新增數據
> db.user.update({id:3},{$set:{sex:1}},true)
3.4 刪除數據
通過remove()方法進行刪除數據,語法如下:
db.collection.remove(<query>,{justOne: <boolean>,writeConcern: <document>}
)
參數說明:
- query :(可選)刪除的文檔的條件。
- justOne : (可選)如果設為 true 或 1,則只刪除一個文檔,如果不設置該參數,或使用默認值 false,則刪除所有匹配條件的文檔。
- writeConcern :(可選)拋出異常的級別。
實例:
#刪除數據
> db.user.remove({})#插入4條測試數據
db.user.insert({id:1,username:'zhangsan',age:20})
db.user.insert({id:2,username:'lisi',age:21})
db.user.insert({id:3,username:'wangwu',age:22})
db.user.insert({id:4,username:'zhaoliu',age:22})> db.user.remove({age:22},true)#刪除所有數據
> db.user.remove({})
3.5 查詢數據
MongoDB 查詢數據的語法格式如下:
db.user.find([query],[fields])
- query :可選,使用查詢操作符指定查詢條件
- fields :可選,使用投影操作符指定返回的鍵。查詢時返回文檔中所有鍵值, 只需省略該參數即可(默認省略)。
條件查詢:
操作 | 格式 | 范例 | RDBMS中的類似語句 |
---|---|---|---|
等于 | {<key>:<value> } | db.col.find({"by":"黑馬程序員"}).pretty() | where by = '黑馬程序員' |
小于 | {<key>:{$lt:<value>}} | db.col.find({"likes":{$lt:50}}).pretty() | where likes < 50 |
小于或等于 | {<key>:{$lte:<value>}} | db.col.find({"likes":{$lte:50}}).pretty() | where likes <= 50 |
大于 | {<key>:{$gt:<value>}} | db.col.find({"likes":{$gt:50}}).pretty() | where likes > 50 |
大于或等于 | {<key>:{$gte:<value>}} | db.col.find({"likes":{$gte:50}}).pretty() | where likes >= 50 |
不等于 | {<key>:{$ne:<value>}} | db.col.find({"likes":{$ne:50}}).pretty() | where likes != 50 |
實例:
#插入測試數據
db.user.insert({id:1,username:'zhangsan',age:20})
db.user.insert({id:2,username:'lisi',age:21})
db.user.insert({id:3,username:'wangwu',age:22})
db.user.insert({id:4,username:'zhaoliu',age:22})db.user.find() #查詢全部數據
db.user.find({},{id:1,username:1}) #只查詢id與username字段
db.user.find().count() #查詢數據條數
db.user.find({id:1}) #查詢id為1的數據
db.user.find({age:{$lte:21}}) #查詢小于等于21的數據
db.user.find({$or:[{id:1},{id:2}]}) #查詢id=1 or id=2#分頁查詢:Skip()跳過幾條,limit()查詢條數
db.user.find().limit(2).skip(1) #跳過1條數據,查詢2條數據
db.user.find().sort({id:-1}) #按照id倒序排序,-1為倒序,1為正序
3.6 索引
索引通常能夠極大的提高查詢的效率,如果沒有索引MongoDB在讀取數據時必須草棉集合中的每個文件選取那些符合查詢條件的記錄
這種掃描全集合的查詢效率是非常低的,特別在處理大量的數據時,查詢可以要花費幾十秒甚至幾分鐘,這對網站的性能是非常致命的。
索引是特殊的數據結構,索引存儲在一個易于遍歷讀取的數據集合中,索引是對數據庫表中一列或多列的值進行排序的一種結構
#創建索引
> db.user.createIndex({'age':1})#查看索引
> db.user.getIndexes()
[{"v" : 2,"key" : {"_id" : 1},"name" : "_id_","ns" : "testdb.user"}
]
#說明:1表示升序創建索引,-1表示降序創建索引。
3.7 執行計劃
MongoDB查詢分析可以確保我們建議的索引有效,是查詢語句性能分析的重要工具
#插入1000條數據
for(var i=1;i<1000;i++)db.user.insert({id:100+i,username:'name_'+i,age:10+i})#查看執行計劃
> db.user.find({age:{$gt:100},id:{$lt:200}}).explain()#測試沒有使用索引
> db.user.find({username:'zhangsan'}).explain()#winningPlan:最佳執行計劃
#"stage" : "FETCH", #查詢方式,常見的有COLLSCAN/全表掃描、IXSCAN/索引掃描、FETCH/根據索引去檢索文檔、SHARD_MERGE/合并分片結果、IDHACK/針對_id進行查詢
四丶SpringData-Mongo
Spring-data對MongoDB做了支持,使用spring-data-mongodb可以簡化MongoDB的操作,封裝了底層的mongodb-driver。
地址:https://spring.io/projects/spring-data-mongodb
使用Spring-Data-MongoDB很簡單,只需要如下幾步即可:
- 導入起步依賴
- 編寫配置信息
- 編寫實體類(配置注解 @Document,@Id)
- 操作mongodb
- 注入MongoTemplate對象,完成CRUD操作
- 編寫Repository接口,注入接口完成基本Crud操作
4.1、環境搭建
第一步,導入依賴:
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.9.RELEASE</version>
</parent>
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>
第二步,編寫application.yml配置文件
spring:data:mongodb:uri: mongodb://192.168.136.160:27017/test
第三步,編寫啟動類
package com.tanhua.mongo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class MongoApplication {public static void main(String[] args) {SpringApplication.run(MongoApplication.class, args);}
}
4.2、完成基本操作
第一步,編寫實體類
package com.tanhua.mongo.domain;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.mapping.Document;@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(value="person")
public class Person {private ObjectId id;private String name;private int age;private String address;}
第二步,通過MongoTemplate完成CRUD操作
package cn.itcast.mongo.test;import cn.itcast.mongo.MongoApplication;
import cn.itcast.mongo.domain.Person;
import org.bson.types.ObjectId;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
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 org.springframework.test.context.junit4.SpringRunner;import java.util.List;@RunWith(SpringRunner.class)
@SpringBootTest(classes = MongoApplication.class)
public class MongoTest {/*** SpringData-mongodb操作* 1、配置實體類* 2、實體類上配置注解(配置集合和對象間的映射關系)* 3、注入MongoTemplate對象* 4、調用對象方法,完成數據庫操作*/@Autowiredprivate MongoTemplate mongoTemplate;//保存@Testpublic void testSave() {for (int i = 0; i < 10; i++) {Person person = new Person();person.setId(ObjectId.get()); //ObjectId.get():獲取一個唯一主鍵字符串person.setName("張三"+i);person.setAddress("北京順義"+i);person.setAge(18+i);mongoTemplate.save(person);}}//查詢-查詢所有@Testpublic void testFindAll() {List<Person> list = mongoTemplate.findAll(Person.class);for (Person person : list) {System.out.println(person);}}@Testpublic void testFind() {//查詢年齡小于20的所有人Query query = new Query(Criteria.where("age").lt(20)); //查詢條件對象//查詢List<Person> list = mongoTemplate.find(query, Person.class);for (Person person : list) {System.out.println(person);}}/*** 分頁查詢*/@Testpublic void testPage() {Criteria criteria = Criteria.where("age").lt(30);//1、查詢總數Query queryCount = new Query(criteria);long count = mongoTemplate.count(queryCount, Person.class);System.out.println(count);//2、查詢當前頁的數據列表, 查詢第二頁,每頁查詢2條Query queryLimit = new Query(criteria).limit(2)//設置每頁查詢條數.skip(2) ; //開啟查詢的條數 (page-1)*sizeList<Person> list = mongoTemplate.find(queryLimit, Person.class);for (Person person : list) {System.out.println(person);}}/*** 更新:* 根據id,更新年齡*/@Testpublic void testUpdate() {//1、條件Query query = Query.query(Criteria.where("id").is("5fe404c26a787e3b50d8d5ad"));//2、更新的數據Update update = new Update();update.set("age", 20);mongoTemplate.updateFirst(query, update, Person.class);}@Testpublic void testRemove() {Query query = Query.query(Criteria.where("id").is("5fe404c26a787e3b50d8d5ad"));mongoTemplate.remove(query, Person.class);}
}