本系列文章學(xué)習(xí)來源是技術(shù)胖的MogoDB系列
??索引的性能提高必須要有大量數(shù)據(jù)才能看出來,像我們之前操作數(shù)據(jù)庫,用10條數(shù)據(jù),是看不出來效果的豪嗽,接下來通過隨機數(shù)的方法,創(chuàng)造出一個百萬級數(shù)據(jù)的數(shù)據(jù)庫出來。
制作隨機數(shù):
??我們要想生成一個百萬級的數(shù)據(jù)集合龟梦,必須要有隨機數(shù)的參與隐锭,我們需要寫一個隨機數(shù)的方法。
//生成隨機數(shù)
function GetRandomNum(min,max){
let range = max-min; //得到隨機數(shù)區(qū)間
let rand = Math.random(); //得到隨機值
return (min + Math.round(rand *range)); //最小值+隨機數(shù)取整
}
console.log(GetRandomNum(10000,99999));
制作隨機用戶名:
??有了隨機數(shù)的方法计贰,我們就可以制作一個隨機生成的用戶名钦睡。目的是存在不同的用戶名,方便我們測試查詢速度躁倒。
//生成隨機數(shù)
function GetRandomNum(min,max){
let range = max-min; //得到隨機數(shù)區(qū)間
let rand = Math.random(); //得到隨機值
return (min + Math.round(rand *range)); //最小值+隨機數(shù)取整
}
// console.log(GetRandomNum(10000,99999));
//生成隨機用戶名
function GetRadomUserName(min,max){
let tempStringArray= "123456789qwertyuiopasdfghjklzxcvbnm".split("");//構(gòu)造生成時的字母庫數(shù)組
let outPuttext = ""; //最后輸出的變量
//進行循環(huán)荞怒,隨機生產(chǎn)用戶名的長度,這里需要生成隨機數(shù)方法的配合
for(let i=1 ;i<GetRandomNum(min,max);i++){
//隨機抽取字母秧秉,拼裝成需要的用戶名
outPuttext=outPuttext+tempStringArray[GetRandomNum(0,tempStringArray.length)]
}
return outPuttext;
}
// console.log(GetRadomUserName(7,16))
插入200萬數(shù)據(jù):
??有了生成隨機數(shù)和隨機用戶名的方法褐桌,就可以生產(chǎn)百萬級數(shù)據(jù)了。
var db = connect('user');
db.randomInfo.drop();
var tempInfo = [];
for (let i=0;i<2000000;i++){
tempInfo.push({
username:GetRadomUserName(7,16),
regeditTime:new Date(),
randNum0:GetRandomNum(100000,999999),
randNum1:GetRandomNum(100000,999999),
randNum2:GetRandomNum(100000,999999),
randNum3:GetRandomNum(100000,999999),
randNum4:GetRandomNum(100000,999999),
randNum5:GetRandomNum(100000,999999),
randNum6:GetRandomNum(100000,999999),
randNum7:GetRandomNum(100000,999999),
randNum8:GetRandomNum(100000,999999),
randNum8:GetRandomNum(100000,999999),
})
}
db.randomInfo.insert(tempInfo);
??這個過程會持續(xù)幾分鐘象迎,插入完成后撩嚼,我們可以使用 db.randomInfo.count()
這個命令查看數(shù)據(jù)中的數(shù)據(jù)條數(shù)。查詢后發(fā)現(xiàn)集合中已經(jīng)有了200萬條數(shù)據(jù)挖帘,可以進行索引的操作了,我們先來建立一個索引恋技,然后看看它的查詢性能到底提升了多少倍拇舀。
【普通查詢】
??我們先制作一個普通查詢,隨便查找一個用戶名蜻底,并計算出查詢和打印的時間骄崩,因為有200萬條數(shù)據(jù),所以性能不會很高薄辅。
var startTime = new Date().getTime() //得到程序運行的開始時間
var db = connect('user') //鏈接數(shù)據(jù)庫
var rs=db.randomInfo.find({username:"tfruhjy8k"}) //根據(jù)用戶名查找用戶
rs.forEach(rs=>{printjson(rs)}) //循環(huán)輸出
var runTime = new Date().getTime()-startTime; //得到程序運行時間
print('[SUCCESS]This run time is:'+runTime+'ms') //打印出運行時間
??[SUCCESS]This run time is:1153ms
【索引查詢】
??為用戶名(username)建立索引
db.randomInfo.ensureIndex({username:1})
??查看現(xiàn)有索引
db.randomInfo.getIndexes()
??建立一下索引大概會花費30s時間要拂,查看結(jié)果
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "user.randomInfo"
},
{
"v" : 2,
"key" : {
"username" : 1
},
"name" : "username_1",
"ns" : "user.randomInfo"
}
]
??再次執(zhí)行上面的查詢,[SUCCESS]This run time is:8ms站楚,這時候查詢的時間縮短到了8ms左右脱惰,查詢性能提升了大概150倍左右。
??無論是在關(guān)系型數(shù)據(jù)庫還是文檔數(shù)據(jù)庫窿春,建立索引都是非常重要的拉一。同時,建立索引是要消耗硬盤和內(nèi)存資源的旧乞,所以還是要根據(jù)程序需要進行建立了蔚润。MongoDB也給我們進行了限制,只允許我們建立64個索引值尺栖。
索引雖然很好用嫡纠,但并不是所有情況都適合用索引的。
- 數(shù)據(jù)不超萬條時,不需要使用索引除盏。性能的提升并不明顯叉橱,而大大增加了內(nèi)存和硬盤的消耗。
- 查詢數(shù)據(jù)超過表數(shù)據(jù)量30%時痴颊,不要使用索引字段查詢赏迟。實際證明會比不使用索引更慢,因為它大量檢索了索引表和我們原表蠢棱。
- 數(shù)字索引锌杀,要比字符串索引快的多,在百萬級甚至千萬級數(shù)據(jù)量面前泻仙,使用數(shù)字索引是個明智的選擇糕再。
- 把你經(jīng)常查詢的數(shù)據(jù)做成一個內(nèi)嵌數(shù)據(jù)(對象型的數(shù)據(jù)),然后集體進行索引玉转。
復(fù)合索引
??復(fù)合索引是兩條以上的索引突想,前面我們已經(jīng)把username字段建立了索引,現(xiàn)在把randNum0究抓,這個字段也設(shè)置成索引猾担。
db.randomInfo.ensureIndex({randNum0:1})
??建立好后,我們再用查詢索引狀態(tài)命令進行查詢刺下。
db.randomInfo.getIndexes()
??這時候已經(jīng)是兩個自建索引了绑嘹,一共有三個索引。
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "user.randomInfo"
},
{
"v" : 2,
"key" : {
"randNum0" : 1
},
"name" : "randNum0_1",
"ns" : "user.randomInfo"
},
{
"v" : 2,
"key" : {
"username" : 1
},
"name" : "username_1",
"ns" : "user.randomInfo"
}
]
【兩個索引同時查詢】
??同時查詢兩個索引的值橘茉,看看性能如何工腋。
var startTime=new Date().getTime();
var db = connect('user');
var rs= db.randomInfo.find({username:'7xwb8y3',randNum0:565509});
rs.forEach(rs=>{printjson(rs)});
var runTime = new Date().getTime()-startTime;
print('[Demo]this run time is '+runTime+'ms');
??[Demo]this run time is 8ms,從性能上看并沒有什么特殊的變化畅卓,查詢時間還是在8ms左右擅腰。這是因為MongoDB的復(fù)合查詢是按照索引順序進行查詢的,就是我們用db.randomInfo.getIndexes()
查詢出的數(shù)組翁潘。
【指定索引查詢(hint)】
??數(shù)字的索引要比字符串的索引快趁冈,這就需要一個方法來打破索引表的查詢順序,用我們自己指定的索引優(yōu)先查詢拜马,這個方法就是hint().
var rs= db.randomInfo.find({username:'7xwb8y3',randNum0:565509}).hint({randNum0:1});
??[Demo]this run time is 3ms箱歧,查詢速度的確加快了。
【刪除索引】
??當(dāng)索引性能不佳或起不到作用時一膨,我們需要刪除索引呀邢,刪除索引的命令是dropIndex()。
db.randomInfo.dropIndex('randNum0_1');//索引的唯一ID
??這里需要注意的是刪除時填寫的值豹绪,并不是我們的字段名稱(key)价淌,而是我們索引查詢表中的name值申眼,也支持刪除全部的索引。
db.randomInfo.dropIndexes()
全文索引
??有時候需要在大篇幅的文章中搜索關(guān)鍵詞蝉衣,MongoDB為我們提供了全文索引括尸。
??我們先建立一個集合(collections) info,然后插入一小段文章病毡,作用就是為建立全文索引提供數(shù)據(jù)财饥。
db.info.insert({contextInfo:"I am a programmer, I love life, love family. Every day after work, I write a diary."})
db.info.insert({contextInfo:"I am a programmer, I love PlayGame, love drink. Every day after work, I playGame and drink."})
建立全文索引
db.info.ensureIndex({contextInfo:'text'})
??需要注意的是這里使用text關(guān)鍵詞來代表全文索引恭垦,我們在這里就不建立數(shù)據(jù)模型了唤蔗。
查找時需要兩個關(guān)鍵修飾符:
$text:表示要在全文索引中查東西胃榕。
$search:后邊跟查找的內(nèi)容。
db.info.find({$text:{$search:"programmer"}})
查找多個詞
??全文索引是支持多個詞查找的僧家,比如我們希望查找數(shù)據(jù)中有programmer雀摘,family,diary八拱,drink的數(shù)據(jù)(這是或的關(guān)系)阵赠,所以兩條數(shù)據(jù)都會出現(xiàn)。
db.info.find({$text:{$search:"programmer family diary drink"}})
??如果我們這時候希望不查找出來有drink這個單詞的記錄肌稻,我們可以使用“-”減號來取消清蚀。
db .info.find({$text:{$search:"programmer family diary -drink"}})
轉(zhuǎn)義符
??全文搜索中是支持轉(zhuǎn)義符的,比如我們想搜索的是兩個詞(love PlayGame和drink)爹谭,這時候需要使用\斜杠來轉(zhuǎn)意枷邪。
db.info.find({$text:{$search:"\"love PlayGame\" drink"}})