Table of Contents
Preface
1. What exactly is an index?
(1) Multiple implementation methods and features of index
三、总结:
彩蛋
Home Database SQL Best Practices for MongoDB Indexes

Best Practices for MongoDB Indexes

Jun 24, 2019 pm 05:36 PM
mongodb index

Best Practices for MongoDB Indexes

Preface

Most developers know that indexing is faster. But in the actual process, we often encounter some questions & difficulties:

  • The fields we query will have various cases. Do all fields involved in the query need to be indexed?
  • How to choose between compound index and single field? Is it better to add both or a single field for each?
  • Are there any side effects of adding index?
  • The index has been added, but it’s still not fast enough? what to do?

This article attempts to explain the basic knowledge of indexing & answer the above questions.

1. What exactly is an index?

Most developers who come into contact with indexes probably know that indexes are similar to the catalog of books. You need to find the content you want, find the qualified keywords through the catalog, then find the pageno of the corresponding chapter, and then find the specific content. .
In the data structure, the simplest index implementation is similar to a hashmap, which maps to a specific location through the keyword key to find the specific content. But in addition to hashing, there are many ways to implement indexing.

(1) Multiple implementation methods and features of index

hash / b-tree / b -tree redis HSET / MongoDB&PostgreSQL / MySQL

hashmap

Best Practices for MongoDB Indexes

##See b-tree & b-tree difference in the picture:

Best Practices for MongoDB Indexes

    b -tree leaves store data, non-leaves store indexes, no data is stored, and there are links between leaves
  • b-tree non-leaves can store data
Algorithm search complexity:

    hash is close to O(1)
  • b-tree O(1)~ O(Log(n)) faster average search time , unstable query time
  • b tree O(Log(n)) continuous data, query stability
As for why the implementation of MongoDB chooses b-tree instead of b - tree?

There are many articles on the Internet that explain this, but it is not the focus of this article.

(2) Storage of data & index

Index should be stored in memory as much as possible, followed by data. Best Practices for MongoDB IndexesBe careful to keep only necessary indexes, and use memory as wisely as possible.
If the index memory is close to filling up the memory, it will be easy to read the disk and the speed will slow down.

(3) Thoughts after knowing the implementation & storage principle of index

insert/update/delete will trigger the rebalance tree, so if you add, delete or modify data, the index will trigger modifications, and performance will be lost. , the more indexes, the better. In this case, which fields should be selected as indexes? What should I do when the query uses these conditions?

Take the simplest hashmap as an example, why is the complexity not O(1), but so-called close to O(1). Because there are key conflicts/duplications, when the DB is looking for it, if there is a lot of data with key conflicts, it still has to take turns to continue looking. The same goes for b-tree looking at key selection.
So a mistake that most developers often make is to index keys that have no distinction. For example: many collections have only centralized categories of type/status documents with a count of hundreds of thousands or more. Usually this kind of index is not helpful.

2. Compound Index

(1) The more compound indexes, the better

If you don’t want to build more redundant indexes, the development colleagues will select compound & single fields. It's quite confusing sometimes. Let’s do a few experiments based on typical encounter scenarios:

A loans collection is created here. Simplified to only have 100 pieces of data. This is a loan table with _id, userId, status (loan status), amount (amount).

db.loans.count()100

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

db.loans.find({ "userId" "59e022d33f239800129c61c7""status" "repayed", }).explain()

{

"queryPlanner" : {

"plannerVersion" : 1,

"namespace" "cashLoan.loans",

"indexFilterSet" : false,

"parsedQuery" : {

 "$and" : [

   {

     "status" : {

       "$eq" "repayed"

     }

   },

   {

     "userId" : {

       "$eq" "59e022d33f239800129c61c7"

     }

   }

 ]

},

"queryHash" "15D5A9A1",

"planCacheKey" "15D5A9A1",

"winningPlan" : {

 "stage" "COLLSCAN",

 "filter" : {

   "$and" : [

     {

       "status" : {

         "$eq" "repayed"

       }

     },

     {

       "userId" : {

         "$eq" "59e022d33f239800129c61c7"

       }

     }

   ]

 },

 "direction" "forward"

},

"rejectedPlans" : [ ]

},

"serverInfo" : {

"host" "RMBAP",

"port" : 27017,

"version" "4.1.11",

"gitVersion" "1b8a9f5dc5c3314042b55e7415a2a25045b32a94"

},

"ok" : 1

}

Copy after login
Note The COLLSCAN above scans the entire table because there is no index. Next we create several indexes respectively.


step 1 First create {userId:1, status:1}

1

2

3

4

5

6

7

db.loans.createIndex({userId:1, status:1})

{

"createdCollectionAutomatically" : false,

"numIndexesBefore" : 1,

"numIndexesAfter" : 2,

"ok" : 1

}

Copy after login

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

db.loans.find({ "userId" "59e022d33f239800129c61c7""status" "repayed", }).explain()

{

"queryPlanner" : {

"plannerVersion" : 1,

"namespace" "cashLoan.loans",

"indexFilterSet" : false,

"parsedQuery" : {

 "$and" : [

   {

     "status" : {

       "$eq" "repayed"

     }

   },

   {

     "userId" : {

       "$eq" "59e022d33f239800129c61c7"

     }

   }

 ]

},

"queryHash" "15D5A9A1",

"planCacheKey" "BB87F2BA",

"winningPlan" : {

 "stage" "FETCH",

 "inputStage" : {

   "stage" "IXSCAN",

   "keyPattern" : {

     "userId" : 1,

     "status" : 1

   },

   "indexName" "userId_1_status_1",

   "isMultiKey" : false,

   "multiKeyPaths" : {

     "userId" : [ ],

     "status" : [ ]

   },

   "isUnique" : false,

   "isSparse" : false,

   "isPartial" : false,

   "indexVersion" : 2,

   "direction" "forward",

   "indexBounds" : {

     "userId" : [

       "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]"

     ],

     "status" : [

       "["repayed", "repayed"]"

     ]

   }

 }

},

"rejectedPlans" : [ ]

},

"serverInfo" : {

"host" "RMBAP",

"port" : 27017,

"version" "4.1.11",

"gitVersion" "1b8a9f5dc5c3314042b55e7415a2a25045b32a94"

},

"ok" : 1

}

Copy after login
Result: {userId:1, status:1} is hit as the winning plan.

step2: Create a typical index userId

1

2

3

4

5

6

7

db.loans.createIndex({userId:1})

{

"createdCollectionAutomatically" : false,

"numIndexesBefore" : 2,

"numIndexesAfter" : 3,

"ok" : 1

}

Copy after login

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

db.loans.find({ "userId" "59e022d33f239800129c61c7""status" "repayed", }).explain()

{

"queryPlanner" : {

"plannerVersion" : 1,

"namespace" "cashLoan.loans",

"indexFilterSet" : false,

"parsedQuery" : {

 "$and" : [

   {

     "status" : {

       "$eq" "repayed"

     }

   },

   {

     "userId" : {

       "$eq" "59e022d33f239800129c61c7"

     }

   }

 ]

},

"queryHash" "15D5A9A1",

"planCacheKey" "1B1A4861",

"winningPlan" : {

 "stage" "FETCH",

 "inputStage" : {

   "stage" "IXSCAN",

   "keyPattern" : {

     "userId" : 1,

     "status" : 1

   },

   "indexName" "userId_1_status_1",

   "isMultiKey" : false,

   "multiKeyPaths" : {

     "userId" : [ ],

     "status" : [ ]

   },

   "isUnique" : false,

   "isSparse" : false,

   "isPartial" : false,

   "indexVersion" : 2,

   "direction" "forward",

   "indexBounds" : {

     "userId" : [

       "[\"59e022d33f239800129c61c7\", \"59e022d33f239800129c61c7\"]"

     ],

     "status" : [

       "[\"repayed\", \"repayed\"]"

     ]

   }

 }

},

"rejectedPlans" : [

 {

   "stage" "FETCH",

   "filter" : {

     "status" : {

       "$eq" "repayed"

     }

   },

   "inputStage" : {

     "stage" "IXSCAN",

     "keyPattern" : {

       "userId" : 1

     },

     "indexName" "userId_1",

     "isMultiKey" : false,

     "multiKeyPaths" : {

       "userId" : [ ]

     },

     "isUnique" : false,

     "isSparse" : false,

     "isPartial" : false,

     "indexVersion" : 2,

     "direction" "forward",

     "indexBounds" : {

       "userId" : [

         "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]"

       ]

     }

   }

 }

]

},

"serverInfo" : {

"host" "RMBAP",

"port" : 27017,

"version" "4.1.11",

"gitVersion" "1b8a9f5dc5c3314042b55e7415a2a25045b32a94"

},

"ok" : 1

}

Copy after login
Note that DB detects {userId:1, status:1} as a better execution plan.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

db.loans.find({ "userId" "59e022d33f239800129c61c7" }).explain()

{

"queryPlanner" : {

"plannerVersion" : 1,

"namespace" "cashLoan.loans",

"indexFilterSet" : false,

"parsedQuery" : {

 "userId" : {

   "$eq" "59e022d33f239800129c61c7"

 }

},

"queryHash" "B1777DBA",

"planCacheKey" "1F09D68E",

"winningPlan" : {

 "stage" "FETCH",

 "inputStage" : {

   "stage" "IXSCAN",

   "keyPattern" : {

     "userId" : 1

   },

   "indexName" "userId_1",

   "isMultiKey" : false,

   "multiKeyPaths" : {

     "userId" : [ ]

   },

   "isUnique" : false,

   "isSparse" : false,

   "isPartial" : false,

   "indexVersion" : 2,

   "direction" "forward",

   "indexBounds" : {

     "userId" : [

       "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]"

     ]

   }

 }

},

"rejectedPlans" : [

 {

   "stage" "FETCH",

   "inputStage" : {

     "stage" "IXSCAN",

     "keyPattern" : {

       "userId" : 1,

       "status" : 1

     },

     "indexName" "userId_1_status_1",

     "isMultiKey" : false,

     "multiKeyPaths" : {

       "userId" : [ ],

       "status" : [ ]

     },

     "isUnique" : false,

     "isSparse" : false,

     "isPartial" : false,

     "indexVersion" : 2,

     "direction" "forward",

     "indexBounds" : {

       "userId" : [

         "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]"

       ],

       "status" : [

         "[MinKey, MaxKey]"

       ]

     }

   }

 }

]

},

"serverInfo" : {

"host" "RMBAP",

"port" : 27017,

"version" "4.1.11",

"gitVersion" "1b8a9f5dc5c3314042b55e7415a2a25045b32a94"

},

"ok" : 1

}

Copy after login
Notice that DB detects {userId:1} as a better execution plan, um~, as we expected.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

db.loans.find({ "status" "repayed" }).explain()

{

"queryPlanner" : {

"plannerVersion" : 1,

"namespace" "cashLoan.loans",

"indexFilterSet" : false,

"parsedQuery" : {

 "status" : {

   "$eq" "repayed"

 }

},

"queryHash" "E6304EB6",

"planCacheKey" "7A94191B",

"winningPlan" : {

 "stage" "COLLSCAN",

 "filter" : {

   "status" : {

     "$eq" "repayed"

   }

 },

 "direction" "forward"

},

"rejectedPlans" : [ ]

},

"serverInfo" : {

"host" "RMBAP",

"port" : 27017,

"version" "4.1.11",

"gitVersion" "1b8a9f5dc5c3314042b55e7415a2a25045b32a94"

},

"ok" : 1

}

Copy after login
Copy after login

Interesting part: status does not hit the index, Full table scanNext step, add a sort:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

db.loans.find({ "userId" "59e022d33f239800129c61c7" }).sort({status:1}).explain()

{

"queryPlanner" : {

"plannerVersion" : 1,

"namespace" "cashLoan.loans",

"indexFilterSet" : false,

"parsedQuery" : {

 "userId" : {

   "$eq" "59e022d33f239800129c61c7"

 }

},

"queryHash" "F5ABB1AA",

"planCacheKey" "764CBAA8",

"winningPlan" : {

 "stage" "FETCH",

 "inputStage" : {

   "stage" "IXSCAN",

   "keyPattern" : {

     "userId" : 1,

     "status" : 1

   },

   "indexName" "userId_1_status_1",

   "isMultiKey" : false,

   "multiKeyPaths" : {

     "userId" : [ ],

     "status" : [ ]

   },

   "isUnique" : false,

   "isSparse" : false,

   "isPartial" : false,

   "indexVersion" : 2,

   "direction" "forward",

   "indexBounds" : {

     "userId" : [

       "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]"

     ],

     "status" : [

       "[MinKey, MaxKey]"

     ]

   }

 }

},

"rejectedPlans" : [

 {

   "stage" "SORT",

   "sortPattern" : {

     "status" : 1

   },

   "inputStage" : {

     "stage" "SORT_KEY_GENERATOR",

     "inputStage" : {

       "stage" "FETCH",

       "inputStage" : {

         "stage" "IXSCAN",

         "keyPattern" : {

           "userId" : 1

         },

         "indexName" "userId_1",

         "isMultiKey" : false,

         "multiKeyPaths" : {

           "userId" : [ ]

         },

         "isUnique" : false,

         "isSparse" : false,

         "isPartial" : false,

         "indexVersion" : 2,

         "direction" "forward",

         "indexBounds" : {

           "userId" : [

             "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]"

           ]

         }

       }

     }

   }

 }

]

},

"serverInfo" : {

"host" "RMBAP",

"port" : 27017,

"version" "4.1.11",

"gitVersion" "1b8a9f5dc5c3314042b55e7415a2a25045b32a94"

},

"ok" : 1

}

Copy after login
(2) Other attempts

Interesting part: status does not hit the index

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

db.loans.find({ "status" "repayed","userId" "59e022d33f239800129c61c7", }).explain()

{

"queryPlanner" : {

"plannerVersion" : 1,

"namespace" "cashLoan.loans",

"indexFilterSet" : false,

"parsedQuery" : {

 "$and" : [

   {

     "status" : {

       "$eq" "repayed"

     }

   },

   {

     "userId" : {

       "$eq" "59e022d33f239800129c61c7"

     }

   }

 ]

},

"queryHash" "15D5A9A1",

"planCacheKey" "1B1A4861",

"winningPlan" : {

 "stage" "FETCH",

 "inputStage" : {

   "stage" "IXSCAN",

   "keyPattern" : {

     "userId" : 1,

     "status" : 1

   },

   "indexName" "userId_1_status_1",

   "isMultiKey" : false,

   "multiKeyPaths" : {

     "userId" : [ ],

     "status" : [ ]

   },

   "isUnique" : false,

   "isSparse" : false,

   "isPartial" : false,

   "indexVersion" : 2,

   "direction" "forward",

   "indexBounds" : {

     "userId" : [

       "[\"59e022d33f239800129c61c7\", \"59e022d33f239800129c61c7\"]"

     ],

     "status" : [

       "[\"repayed\", \"repayed\"]"

     ]

   }

 }

},

"rejectedPlans" : [

 {

   "stage" "FETCH",

   "filter" : {

     "status" : {

       "$eq" "repayed"

     }

   },

   "inputStage" : {

     "stage" "IXSCAN",

     "keyPattern" : {

       "userId" : 1

     },

     "indexName" "userId_1",

     "isMultiKey" : false,

     "multiKeyPaths" : {

       "userId" : [ ]

     },

     "isUnique" : false,

     "isSparse" : false,

     "isPartial" : false,

     "indexVersion" : 2,

     "direction" "forward",

     "indexBounds" : {

       "userId" : [

         "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]"

       ]

     }

   }

 }

]

},

"serverInfo" : {

"host" "RMBAP",

"port" : 27017,

"version" "4.1.11",

"gitVersion" "1b8a9f5dc5c3314042b55e7415a2a25045b32a94"

},

"ok" : 1

}

Copy after login
The hit index is not related to the order of each field of the query, as we guessed.


Come back to the interesting part, we delete the index {userId:1}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

db.loans.dropIndex({"userId":1})

"nIndexesWas" : 3, "ok" : 1 }

 

db.loans.find({"userId" "59e022d33f239800129c61c7", }).explain()

{

"queryPlanner" : {

"plannerVersion" : 1,

"namespace" "cashLoan.loans",

"indexFilterSet" : false,

"parsedQuery" : {

 "userId" : {

   "$eq" "59e022d33f239800129c61c7"

 }

},

"queryHash" "B1777DBA",

"planCacheKey" "5776AB9C",

"winningPlan" : {

 "stage" "FETCH",

 "inputStage" : {

   "stage" "IXSCAN",

   "keyPattern" : {

     "userId" : 1,

     "status" : 1

   },

   "indexName" "userId_1_status_1",

   "isMultiKey" : false,

   "multiKeyPaths" : {

     "userId" : [ ],

     "status" : [ ]

   },

   "isUnique" : false,

   "isSparse" : false,

   "isPartial" : false,

   "indexVersion" : 2,

   "direction" "forward",

   "indexBounds" : {

     "userId" : [

       "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]"

     ],

     "status" : [

       "[MinKey, MaxKey]"

     ]

   }

 }

},

"rejectedPlans" : [ ]

},

"serverInfo" : {

"host" "RMBAP",

"port" : 27017,

"version" "4.1.11",

"gitVersion" "1b8a9f5dc5c3314042b55e7415a2a25045b32a94"

},

"ok" : 1

}

Copy after login
DB execution analyzer thinks that the index {userId:1, status:1} can be better, but there is no hit Composite index, this is because status is not the leading field.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

db.loans.find({ "status" "repayed" }).explain()

{

"queryPlanner" : {

"plannerVersion" : 1,

"namespace" "cashLoan.loans",

"indexFilterSet" : false,

"parsedQuery" : {

 "status" : {

   "$eq" "repayed"

 }

},

"queryHash" "E6304EB6",

"planCacheKey" "7A94191B",

"winningPlan" : {

 "stage" "COLLSCAN",

 "filter" : {

   "status" : {

     "$eq" "repayed"

   }

 },

 "direction" "forward"

},

"rejectedPlans" : [ ]

},

"serverInfo" : {

"host" "RMBAP",

"port" : 27017,

"version" "4.1.11",

"gitVersion" "1b8a9f5dc5c3314042b55e7415a2a25045b32a94"

},

"ok" : 1

}

Copy after login
Copy after login
Change the sort angle again and interchange it with the previous query & sort. The previous one was:

1

db.loans.find({userId:1}).sort({ "status" "repayed" })

Copy after login
See what’s the difference?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

db.loans.find({ "status" "repayed" }).sort({userId:1}).explain()

{

"queryPlanner" : {

"plannerVersion" : 1,

"namespace" "cashLoan.loans",

"indexFilterSet" : false,

"parsedQuery" : {

 "status" : {

   "$eq" "repayed"

 }

},

"queryHash" "56EA6313",

"planCacheKey" "2CFCDA7F",

"winningPlan" : {

 "stage" "FETCH",

 "filter" : {

   "status" : {

     "$eq" "repayed"

   }

 },

 "inputStage" : {

   "stage" "IXSCAN",

   "keyPattern" : {

     "userId" : 1,

     "status" : 1

   },

   "indexName" "userId_1_status_1",

   "isMultiKey" : false,

   "multiKeyPaths" : {

     "userId" : [ ],

     "status" : [ ]

   },

   "isUnique" : false,

   "isSparse" : false,

   "isPartial" : false,

   "indexVersion" : 2,

   "direction" "forward",

   "indexBounds" : {

     "userId" : [

       "[MinKey, MaxKey]"

     ],

     "status" : [

       "[MinKey, MaxKey]"

     ]

   }

 }

},

"rejectedPlans" : [ ]

},

"serverInfo" : {

"host" "RMBAP",

"port" : 27017,

"version" "4.1.11",

"gitVersion" "1b8a9f5dc5c3314042b55e7415a2a25045b32a94"

},

"ok" : 1

}

Copy after login
As guessed, hit the index.

Let’s play again and confirm the leading filed test:

1

2

db.loans.dropIndex("userId_1_status_1")

"nIndexesWas" : 2, "ok" : 1 }

Copy after login

1

2

3

4

5

6

7

8

9

10

11

db.loans.getIndexes()

[

{

"v" : 2,

"key" : {

 "id" : 1

},

"name" "id_",

"ns" "cashLoan.loans"

}

]

Copy after login

1

2

3

4

5

6

7

db.loans.createIndex({status:1, userId:1})

{

"createdCollectionAutomatically" : false,

"numIndexesBefore" : 1,

"numIndexesAfter" : 2,

"ok" : 1

}

Copy after login

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

db.loans.getIndexes()

[

{

"v" : 2,

"key" : {

 "id" : 1

},

"name" "id_",

"ns" "cashLoan.loans"

},

{

"v" : 2,

"key" : {

 "status" : 1,

 "userId" : 1

},

"name" "status_1_userId_1",

"ns" "cashLoan.loans"

}

]

Copy after login
Copy after login

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

db.loans.find({ "status" "repayed" }).explain()

{

"queryPlanner" : {

"plannerVersion" : 1,

"namespace" "cashLoan.loans",

"indexFilterSet" : false,

"parsedQuery" : {

 "status" : {

   "$eq" "repayed"

 }

},

"queryHash" "E6304EB6",

"planCacheKey" "7A94191B",

"winningPlan" : {

 "stage" "FETCH",

 "inputStage" : {

   "stage" "IXSCAN",

   "keyPattern" : {

     "status" : 1,

     "userId" : 1

   },

   "indexName" "status_1_userId_1",

   "isMultiKey" : false,

   "multiKeyPaths" : {

     "status" : [ ],

     "userId" : [ ]

   },

   "isUnique" : false,

   "isSparse" : false,

   "isPartial" : false,

   "indexVersion" : 2,

   "direction" "forward",

   "indexBounds" : {

     "status" : [

       "["repayed", "repayed"]"

     ],

     "userId" : [

       "[MinKey, MaxKey]"

     ]

   }

 }

},

"rejectedPlans" : [ ]

},

"serverInfo" : {

"host" "RMBAP",

"port" : 27017,

"version" "4.1.11",

"gitVersion" "1b8a9f5dc5c3314042b55e7415a2a25045b32a94"

},

"ok" : 1

}

Copy after login

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

db.loans.getIndexes()

[

{

"v" : 2,

"key" : {

 "id" : 1

},

"name" "id_",

"ns" "cashLoan.loans"

},

{

"v" : 2,

"key" : {

 "status" : 1,

 "userId" : 1

},

"name" "status_1_userId_1",

"ns" "cashLoan.loans"

}

]

Copy after login
Copy after login

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

db.loans.find({"userId" "59e022d33f239800129c61c7", }).explain()

{

"queryPlanner" : {

"plannerVersion" : 1,

"namespace" "cashLoan.loans",

"indexFilterSet" : false,

"parsedQuery" : {

 "userId" : {

   "$eq" "59e022d33f239800129c61c7"

 }

},

"queryHash" "B1777DBA",

"planCacheKey" "5776AB9C",

"winningPlan" : {

 "stage" "COLLSCAN",

 "filter" : {

   "userId" : {

     "$eq" "59e022d33f239800129c61c7"

   }

 },

 "direction" "forward"

},

"rejectedPlans" : [ ]

},

"serverInfo" : {

"host" "RMBAP",

"port" : 27017,

"version" "4.1.11",

"gitVersion" "1b8a9f5dc5c3314042b55e7415a2a25045b32a94"

},

"ok" : 1

}

Copy after login

看完这个试验,明白了 {userId:1, status:1} vs {status:1,userId:1} 的差别了吗?

PS:这个case 里面其实status 区分度不高,这里只是作为实例展示。

三、总结:

  • 注意使用上、使用频率上、区分高的/常用的在前面;
  • 如果需要减少索引以节省memory/提高修改数据的性能的话,可以保留区分度高,常用的,去除区分度不高,不常用的索引。
  • 学会用explain()验证分析性能:

DB 一般都有执行器优化的分析,MySQL & MongoDB 都是 用explain 来做分析。
语法上MySQL :

explain your_sql

MongoDB:

yoursql.explain()

总结典型:理想的查询是结合explain 的指标,他们通常是多个的混合:

  • IXSCAN  : 索引命中;
  • Limit  : 带limit;
  • Projection :  相当于非 select * ;
  • Docs Size less is better  ;
  • Docs Examined less is better ;
  • nReturned=totalDocsExamined=totalKeysExamined ;
  • SORT in index :sort 也是命中索引,否则,需要拿到数据后,再执行一遍排序;
  • Limit Array elements : 限定数组返回的条数,数组也不应该太多数据,否则schema 设计不合理。

彩蛋

文末,还有最开头1个问题没回答:如果我的索引改加的都加了,还不够快,怎么办?
留个悬念,之后再写一篇。

更多PHP相关技术文章,请访问PHP教程栏目进行学习!

The above is the detailed content of Best Practices for MongoDB Indexes. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

What is the use of net4.0 What is the use of net4.0 May 10, 2024 am 01:09 AM

.NET 4.0 is used to create a variety of applications and it provides application developers with rich features including: object-oriented programming, flexibility, powerful architecture, cloud computing integration, performance optimization, extensive libraries, security, Scalability, data access, and mobile development support.

How to configure MongoDB automatic expansion on Debian How to configure MongoDB automatic expansion on Debian Apr 02, 2025 am 07:36 AM

This article introduces how to configure MongoDB on Debian system to achieve automatic expansion. The main steps include setting up the MongoDB replica set and disk space monitoring. 1. MongoDB installation First, make sure that MongoDB is installed on the Debian system. Install using the following command: sudoaptupdatesudoaptinstall-ymongodb-org 2. Configuring MongoDB replica set MongoDB replica set ensures high availability and data redundancy, which is the basis for achieving automatic capacity expansion. Start MongoDB service: sudosystemctlstartmongodsudosys

How to ensure high availability of MongoDB on Debian How to ensure high availability of MongoDB on Debian Apr 02, 2025 am 07:21 AM

This article describes how to build a highly available MongoDB database on a Debian system. We will explore multiple ways to ensure data security and services continue to operate. Key strategy: ReplicaSet: ReplicaSet: Use replicasets to achieve data redundancy and automatic failover. When a master node fails, the replica set will automatically elect a new master node to ensure the continuous availability of the service. Data backup and recovery: Regularly use the mongodump command to backup the database and formulate effective recovery strategies to deal with the risk of data loss. Monitoring and Alarms: Deploy monitoring tools (such as Prometheus, Grafana) to monitor the running status of MongoDB in real time, and

Navicat's method to view MongoDB database password Navicat's method to view MongoDB database password Apr 08, 2025 pm 09:39 PM

It is impossible to view MongoDB password directly through Navicat because it is stored as hash values. How to retrieve lost passwords: 1. Reset passwords; 2. Check configuration files (may contain hash values); 3. Check codes (may hardcode passwords).

What is the CentOS MongoDB backup strategy? What is the CentOS MongoDB backup strategy? Apr 14, 2025 pm 04:51 PM

Detailed explanation of MongoDB efficient backup strategy under CentOS system This article will introduce in detail the various strategies for implementing MongoDB backup on CentOS system to ensure data security and business continuity. We will cover manual backups, timed backups, automated script backups, and backup methods in Docker container environments, and provide best practices for backup file management. Manual backup: Use the mongodump command to perform manual full backup, for example: mongodump-hlocalhost:27017-u username-p password-d database name-o/backup directory This command will export the data and metadata of the specified database to the specified backup directory.

Major update of Pi Coin: Pi Bank is coming! Major update of Pi Coin: Pi Bank is coming! Mar 03, 2025 pm 06:18 PM

PiNetwork is about to launch PiBank, a revolutionary mobile banking platform! PiNetwork today released a major update on Elmahrosa (Face) PIMISRBank, referred to as PiBank, which perfectly integrates traditional banking services with PiNetwork cryptocurrency functions to realize the atomic exchange of fiat currencies and cryptocurrencies (supports the swap between fiat currencies such as the US dollar, euro, and Indonesian rupiah with cryptocurrencies such as PiCoin, USDT, and USDC). What is the charm of PiBank? Let's find out! PiBank's main functions: One-stop management of bank accounts and cryptocurrency assets. Support real-time transactions and adopt biospecies

How to encrypt data in Debian MongoDB How to encrypt data in Debian MongoDB Apr 12, 2025 pm 08:03 PM

Encrypting MongoDB database on a Debian system requires following the following steps: Step 1: Install MongoDB First, make sure your Debian system has MongoDB installed. If not, please refer to the official MongoDB document for installation: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-debian/Step 2: Generate the encryption key file Create a file containing the encryption key and set the correct permissions: ddif=/dev/urandomof=/etc/mongodb-keyfilebs=512

MongoDB and relational database: a comprehensive comparison MongoDB and relational database: a comprehensive comparison Apr 08, 2025 pm 06:30 PM

MongoDB and relational database: In-depth comparison This article will explore in-depth the differences between NoSQL database MongoDB and traditional relational databases (such as MySQL and SQLServer). Relational databases use table structures of rows and columns to organize data, while MongoDB uses flexible document-oriented models to better suit the needs of modern applications. Mainly differentiates data structures: Relational databases use predefined schema tables to store data, and relationships between tables are established through primary keys and foreign keys; MongoDB uses JSON-like BSON documents to store them in a collection, and each document structure can be independently changed to achieve pattern-free design. Architectural design: Relational databases need to pre-defined fixed schema; MongoDB supports

See all articles