この記事では、MongoDB を理解し、MongoDB の豊富なインデックス タイプを紹介します。皆様のお役に立てれば幸いです。
![MongoDB の豊富なインデックス タイプについて話しましょう](https://img.php.cn/upload/article/000/000/024/620db8b860683448.jpg)
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
});
}
ログイン後にコピー
単一キー インデックス
単一キー インデックスの意味最も基本的なインデックスであるインデックス付きフィールドが 1 つだけであることを示します。Method.
コレクション内の
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 であり、かなり低い基数です。この場合、インデックスの効率は高くなく、インデックスの失敗につながります。
# 実行プランをクエリするための
sex
フィールド インデックスを構築しましょう。クエリが関連インデックスなしでフル テーブル スキャンを実行したことがわかります。db.users.createIndex({sex:1})
'sex_1'
db.users.find({sex:1}).explain()
{
queryPlanner:
{
......
winningPlan:
{
stage: 'COLLSCAN',
filter: { sex: { '$eq': 1 } },
direction: 'forward'
},
rejectedPlans: []
},
......
ok: 1
}
ログイン後にコピー
ジョイント インデックス
結合インデックスは、インデックスに複数のフィールドが存在することを意味します。以下の
age## を使用します。# と
sex は、2 つのフィールド
db.users.createIndex({age:1,sex:1})
'age_1_sex_1'
ログイン後にコピー
## を持つインデックスを作成します# 次に、これら 2 つのフィールドを使用してクエリを実行し、実行計画を確認し、このインデックスを正常に実行します
db.users.find({age:23,sex:1}).explain()
{
queryPlanner:
{
......
winningPlan:
{
stage: 'FETCH',
inputStage:
{
stage: 'IXSCAN',
keyPattern: { age: 1, sex: 1 },
indexName: 'age_1_sex_1',
.......
indexBounds: { age: [ '[23, 23]' ], sex: [ '[1, 1]' ] }
}
},
rejectedPlans: [],
},
......
ok: 1
}
ログイン後にコピー
配列インデックス
配列インデックスは、配列フィールドのインデックス (複数値インデックスとも呼ばれます)。テストするために、users コレクション内のデータが以下のいくつかの配列フィールドに追加されます。
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
配列の平均
size
が 10 である場合、このコレクションの hobby
配列インデックスは次のようになります。
結合配列インデックス
結合配列インデックスとは、配列フィールドを含む結合インデックスです。は 1 つのインデックスをサポートしません。複数の配列フィールドが含まれます。つまり、インデックス内に存在できる配列フィールドは最大 1 つです。これは、インデックス エントリの爆発的な増加を避けるためです。インデックス内に 2 つの配列フィールドがあると仮定すると、その数はインデックス エントリの数は、通常のインデックスの n* になります。m 倍
地理空間インデックス
元の users コレクションに地理情報を追加します。
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 番目の次元空間インデックスの作成 db.users.createIndex({location:"2dsphere"})
'location_2dsphere'
//查询500米内的人
db.users.find({
location:{
$near:{
$geometry:{type:"Point",coordinates:[102,41.5]},
$maxDistance:500
}
}
})
ログイン後にコピー
地理空間インデックスの
type
には、
Ponit(point)
| を含むものが多数あります。 LineString(line)
| Polygon (Polygon)
etc
TTL インデックス
TTL の完全なスペルは、time to です。 live は、主に期限切れデータの自動削除に使用されます。この種のインデックスを使用するには、ドキュメント内で時間型フィールドを宣言する必要があります。また、このフィールドの TTL インデックスを作成するときに、次のことも行う必要があります。
expireAfterSecondsを設定します
作成完了後の有効期限の単位は秒ですMongoDB
コレクション内のデータは定期的にチェックされます。表示されるタイミング:##whenbefore 间−##TT #Lindexcite字セクション時間インター#>e##xpireAfte rSrcond sMongoDB
将会自动将这些文档删除,这种索引还有以下这些要求:
- TTL索引只能有一个字段,没有联合TTL索引
- TTL不能用于固定集合
- TTL索引是逐个遍历后,发现满足删除条件会使用
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 サイトの他の関連記事を参照してください。