この記事では、MongoDB を理解し、MongoDB の豊富なインデックス タイプを紹介します。皆様のお役に立てれば幸いです。
MongoDB
のインデックスと MySql
のインデックスの機能は、機能と最適化原則の点で基本的に似ています。 MySql
インデックス タイプは基本的に次のように区別できます。
MongoDB には、これらの基本的な分類に加えて、配列インデックス | スパース インデックス | 地理空間インデックス | TTL インデックスなどの特殊なインデックス タイプもあります。 etc.
for(var i = 0;i < 100000;i++){ db.users.insertOne({ username: "user"+i, age: Math.random() * 100, sex: i % 2, phone: 18468150001+i }); }
username フィールドを使用して、単一のキー インデックスを作成します。
MongoDBこのインデックスには自動的に
username_1
db.users.createIndex({username:1}) 'username_1'
username フィールドを使用してクエリ プランを確認します。
stage は
IXSCAN (インデックス スキャンが使用されることを意味します)
db.users.find({username:"user40001"}).explain() { queryPlanner: { winningPlan: { ...... stage: 'FETCH', inputStage: { stage: 'IXSCAN', keyPattern: { username: 1 }, indexName: 'username_1', ...... } } rejectedPlans: [] , }, ...... ok: 1 }
users を作成するとき。収集中に表示される年齢の値が
0-99 の場合、
age フィールドには 100 個の一意の値が含まれます。つまり、
age フィールドのベースは 100 です。
sex フィールドには 2 つの値のみが含まれます
0 | 1、つまり、
sex フィールドの基数は 2 であり、かなり低い基数です。この場合、インデックスの効率は高くなく、インデックスの失敗につながります。
# 実行プランをクエリするための
フィールド インデックスを構築しましょう。クエリが関連インデックスなしでフル テーブル スキャンを実行したことがわかります。<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">db.users.createIndex({sex:1})
&#39;sex_1&#39;
db.users.find({sex:1}).explain()
{
queryPlanner:
{
......
winningPlan:
{
stage: &#39;COLLSCAN&#39;,
filter: { sex: { &#39;$eq&#39;: 1 } },
direction: &#39;forward&#39;
},
rejectedPlans: []
},
......
ok: 1
}</pre><div class="contentsignin">ログイン後にコピー</div></div>
sex は、2 つのフィールド
db.users.createIndex({age:1,sex:1}) 'age_1_sex_1'
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">db.users.find({age:23,sex:1}).explain()
{
queryPlanner:
{
......
winningPlan:
{
stage: &#39;FETCH&#39;,
inputStage:
{
stage: &#39;IXSCAN&#39;,
keyPattern: { age: 1, sex: 1 },
indexName: &#39;age_1_sex_1&#39;,
.......
indexBounds: { age: [ &#39;[23, 23]&#39; ], sex: [ &#39;[1, 1]&#39; ] }
}
},
rejectedPlans: [],
},
......
ok: 1
}</pre><div class="contentsignin">ログイン後にコピー</div></div>
配列インデックスdb.users.updateOne({username:"user1"},{$set:{hobby:["唱歌","篮球","rap"]}}) ......
配列を作成するインデックスを作成し、その実行プランを表示します。isMultiKey: true
は、使用されるインデックスが複数値のインデックスであることを意味します。
db.users.createIndex({hobby:1}) 'hobby_1' db.users.find({hobby:{$elemMatch:{$eq:"钓鱼"}}}).explain() { queryPlanner: { ...... winningPlan: { stage: 'FETCH', filter: { hobby: { '$elemMatch': { '$eq': '钓鱼' } } }, inputStage: { stage: 'IXSCAN', keyPattern: { hobby: 1 }, indexName: 'hobby_1', isMultiKey: true, multiKeyPaths: { hobby: [ 'hobby' ] }, ...... indexBounds: { hobby: [ '["钓鱼", "钓鱼"]' ] } } }, rejectedPlans: [] }, ...... ok: 1 }
配列インデックスは他のインデックスと比較されます。一般的に、インデックス エントリは、たとえば、各ドキュメントの hobby
配列の平均
が 10 である場合、このコレクションの hobby
配列インデックスは次のようになります。 結合配列インデックス
結合配列インデックスとは、配列フィールドを含む結合インデックスです。は 1 つのインデックスをサポートしません。複数の配列フィールドが含まれます。つまり、インデックス内に存在できる配列フィールドは最大 1 つです。これは、インデックス エントリの爆発的な増加を避けるためです。インデックス内に 2 つの配列フィールドがあると仮定すると、その数はインデックス エントリの数は、通常のインデックスの n* になります。m 倍
地理空間インデックスfor(var i = 0;i < 100000;i++){ db.users.updateOne( {username:"user"+i}, { $set:{ location:{ type: "Point", coordinates: [100+Math.random() * 4,40+Math.random() * 3] } } }); }
2 番目の次元空間インデックスの作成 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">db.users.createIndex({location:"2dsphere"})
&#39;location_2dsphere&#39;
//查询500米内的人
db.users.find({
location:{
$near:{
$geometry:{type:"Point",coordinates:[102,41.5]},
$maxDistance:500
}
}
})</pre><div class="contentsignin">ログイン後にコピー</div></div>
地理空間インデックスの
には、
Ponit(point) | を含むものが多数あります。 LineString(line)
| Polygon (Polygon)
etcTTL インデックス
作成完了後の有効期限の単位は秒ですMongoDB
コレクション内のデータは定期的にチェックされます。表示されるタイミング:<div class="math math-display"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics>##現在時刻<mrow><mtext>−</mtext><mo>T</mo><mi>T</mi> <mi>L</mi><mi>インデックス フィールド時間</mi><mtext>></mtext><mo>e</mo><mi>xx</mi><mi>p</mi><mi>i</mi> <mi>r</mi><mi>e</mi><mi>A</mi><mi>f</mi><mi>t</mi><mi>e</mi><mi>r</mi><mi>S</mi><mi>r</mi><mi>c</mi><mi>o</mi><mi>n</mi><mi>d</mi><mi>s</mi><mi></mi>現在時刻 - TTL インデックス フィールド時間>expireAfterSrconds</mrow><annotation encoding="application/x-tex"></annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base">##when<span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span>before<span class="mord cjk_fallback"></span> <span class="mord cjk_fallback"></span>间<span class="mord cjk_fallback"></span><span class="mord cjk_fallback"></span>−<span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin"></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span>##T<span class="base"><span class="strut" style="height:0.72243em;vertical-align:-0.0391em;">T</span> <span class="mord mathnormal" style="margin-right:0.13889em;">#L</span><span class="mord mathnormal" style="margin-right:0.13889em;">index</span><span class="mord mathnormal">cite</span><span class="mord cjk_fallback">字</span><span class="mord cjk_fallback">セクション</span><span class="mord cjk_fallback">時間</span><span class="mord cjk_fallback">インター</span><span class="mord cjk_fallback"></span>#><span class="mord cjk_fallback"></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel"></span><span class="mspace" style="margin-right:0.2777777777777778em;">e</span>##x</span><span class="base">p<span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span>i<span class="mord mathnormal"></span>r<span class="mord mathnormal"></span>e<span class="mord mathnormal"></span>A<span class="mord mathnormal"></span>f<span class="mord mathnormal" style="margin-right:0.02778em;"></span>t<span class="mord mathnormal"></span>e<span class="mord mathnormal"> </span>r<span class="mord mathnormal" style="margin-right:0.10764em;"></span>S<span class="mord mathnormal"></span>r<span class="mord mathnormal"></span>c<span class="mord mathnormal" style="margin-right:0.02778em;"></span>o<span class="mord mathnormal" style="margin-right:0.05764em;"></span>n<span class="mord mathnormal" style="margin-right:0.02778em;"></span>d<span class="mord mathnormal"></span> s<span class="mord mathnormal"></span><span class="mord mathnormal"></span><span class="mord mathnormal"></span><p><code>MongoDB
将会自动将这些文档删除,这种索引还有以下这些要求:
delete
函数删除,效率并不高首先在我们文档上增减一个时间字段
for(var i = 90000;i < 100000;i++){ db.users.updateOne( {username:"user"+i}, { $set:{ createdDate:new Date() } }); }
创建一个TTL索引并且设定过期时间为60s,待过60s后查询,会发现这些数据已经不存在
db.users.createIndex({createdDate:1},{expireAfterSeconds:60}) 'createdDate_1'
另外还可以用CollMod
命令更改TTL索引的过期时间
db.runCommand({ collMod:"users", index:{ keyPattern:{createdDate:1}, expireAfterSeconds:120 } }) { expireAfterSeconds_old: 60, expireAfterSeconds_new: 120, ok: 1 }
条件索引也叫部分索引(partial),只对满足条件的数据进行建立索引.
只对50岁以上的user
进行建立username_1
索引,查看执行计划会发现isPartial
这个字段会变成true
db.users.createIndex({username:1},{partialFilterExpression:{ age:{$gt:50} }}) 'username_1' db.users.find({$and:[{username:"user4"},{age:60}]}).explain() { queryPlanner: { ...... winningPlan: { stage: 'FETCH', filter: { age: { '$eq': 60 } }, inputStage: { stage: 'IXSCAN', keyPattern: { username: 1 }, indexName: 'username_1', ...... isPartial: true, ...... } }, rejectedPlans: [] }, ...... ok: 1 }
一般的索引会根据某个字段为整个集合创建一个索引,即使某个文档不存这个字段,那么这个索引会把这个文档的这个字段当作null
建立在索引当中.
稀疏索引不会对文档中不存在的字段建立索引,如果这个字段存在但是为null
时,则会创建索引.
下面给users
集合中的部分数据创建稀疏索引
for(var i = 5000;i < 10000;i++){ if(i < 9000){ db.users.updateOne( {username:"user"+i}, { $set:{email:(120000000+i)+"@qq.email"}} ) }else{ db.users.updateOne( {username:"user"+i}, { $set:{email:null}} ) } }
当不建立索引使用{email:null}
条件进行查询时,我们会发现查出来的文档包含没有email
字段的文档
db.users.find({email:null}) { _id: ObjectId("61bdc01ba59136670f6536fd"), username: 'user0', age: 64.41483801726282, sex: 0, phone: 18468150001, location: { type: 'Point', coordinates: [ 101.42490900320335, 42.2576650823515 ] } } ......
然后对email
这个字段创建一个稀疏索引使用{email:null}
条件进行查询,则发现查询来的文档全部是email
字段存在且为null
的文档.
db.users.createIndex({email:1},{sparse:true}); 'email_1' db.users.find({email:null}).hint({email:1}) { _id: ObjectId("61bdc12ca59136670f655a25"), username: 'user9000', age: 94.18397576757012, sex: 0, phone: 18468159001, hobby: [ '钓鱼', '乒乓球' ], location: { type: 'Point', coordinates: [ 101.25903151863596, 41.38450145025062 ] }, email: null } ......
文本索引将建立索引的文档字段先进行分词再进行检索,但是目前还不支持中文分词.
下面增加两个文本字段,创建一个联合文本索引
db.blog.insertMany([ {title:"hello world",content:"mongodb is the best database"}, {title:"index",content:"efficient data structure"} ]) //创建索引 db.blog.createIndex({title:"text",content:"text"}) 'title_text_content_text' //使用文本索引查询 db.blog.find({$text:{$search:"hello data"}}) { _id: ObjectId("61c092268c4037d17827d977"), title: 'index', content: 'efficient data structure' }, { _id: ObjectId("61c092268c4037d17827d976"), title: 'hello world', content: 'mongodb is the best database' }
唯一索引就是在建立索引地字段上不能出现重复元素,除了单字段唯一索引还有联合唯一索引以及数组唯一索引(即数组之间不能有元素交集 )
//对title字段创建唯一索引 db.blog.createIndex({title:1},{unique:true}) 'title_1' //插入一个已经存在的title值 db.blog.insertOne({title:"hello world",content:"mongodb is the best database"}) MongoServerError: E11000 duplicate key error collection: mock.blog index: title_1 dup key: { : "hello world" } //查看一下执行计划,isUnique为true db.blog.find({"title":"index"}).explain() { queryPlanner: { ...... winningPlan: { stage: 'FETCH', inputStage: { stage: 'IXSCAN', keyPattern: { title: 1 }, indexName: 'title_1', isMultiKey: false, multiKeyPaths: { title: [] }, isUnique: true, ...... } }, rejectedPlans: [] }, ....... ok: 1 }
相关视频教程推荐:《MongoDB教程》
以上がMongoDB の豊富なインデックス タイプについて話しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。