月間10億PVから学んだMongoDBアンチパターン

5d247ff63b1861db5e6a56d4990e5a4f?s=47 yujiosaka
November 11, 2014

 月間10億PVから学んだMongoDBアンチパターン

ZenClerkが月間10億PVを支えるまでの
 過程で経験したアンチパターンを紹介

5d247ff63b1861db5e6a56d4990e5a4f?s=128

yujiosaka

November 11, 2014
Tweet

Transcript

  1. ݄ؒ10ԯPV͔ΒֶΜͩ
 MongoDBΞϯνύλʔϯ db tech showcase ౦ژ 2014 speaker: “Yuji Isobe”

  2. history: [
 “violinist”
 “engineer”
 “security specialist”
 “socket.io contributor”
 “startup member


    of ZenClerk ”
 ] name: “Yuji Isobe”
  3. questions: -> MongoDBΛ࢖༻ͨ͜͠ͱ͕͋Γ·͔͢ʁ -> ຊ൪؀ڥͰMongoDBΛ࢖༻͍ͯ͠·͔͢ʁ

  4. goal: -> ZenClerk͕݄ؒ10ԯPVΛࢧ͑Δ·Ͱͷ
 աఔͰܦݧͨ͠ΞϯνύλʔϯΛ঺հ -> ओʹύϑΥʔϚϯενϡʔχϯάͷ࿩ ->ʮ݄ؒ10ԯPVΛࢧ͑ΔMongoDBʯͷͦͷઌ IUUQXXXTMJEFTIBSFOFUZVKJPTBLBQWNPOHPEC

  5. None
  6. ϦΞϧλΠϜ

  7. ϦΞϧλΠϜ෼ੳ Ϗοάσʔλ

  8. db.ZenClerk .stats() -> ݄ؒ10ԯPV -> ඵؒ400PV -> ಉ࣌઀ଓ਺5ສ -> ݄ؒ10TBอଘ

  9. architecture: front-end: [ “AngularJS” “Ruby on Rails” “MySQL” “memcached”
 “AWS”

    ] back-end: [ “Node.js”
 “MongoDB” “socket.io” “Redis”
 “AWS” ]
  10. architecture: front-end: [ “AngularJS” “Ruby on Rails” “MySQL” “memcached”
 “AWS”

    ] back-end: [ “Node.js”
 “MongoDB” “socket.io” “Redis”
 “AWS” ]
  11. i2.2xlarge m1.large m3.large db.ZenClerk .aggregate() r3.large

  12. ʙ1000ສPV mogngod

  13. 1000ສʙ1ԯPV delayed replica replica set

  14. 1ԯPVʙ10ԯPV replica set shard mongos replica set shard replica set

    shard delayed replica delayed replica delayed replica mongoc
  15. ͜͜·ͰΞϓϦέʔγϣϯͷมߋ
 ΄ͱΜͲͳ͠ʢҰ෦ΫΤϦΛ࠷దԽʣ

  16. we.use(“MongoDB”).explain() -> εΩʔϚϨε -> τϥϯβΫγϣϯෆཁ
 ʢ࠷௿ݶͷΞτϛοΫૢ࡞ʣ -> ॊೈͳΫΤϦ -> ༰қͳεέʔϧΞ΢τ


    -> Node.jsͱͷ૬ੑͷྑ͞
  17. we.use(“MongoDB”).explain(true) -> ಋೖͷ༰қ͞ʢϗεςΟϯάαʔϏε༗ແʣ -> ֶशͷ༰қ͞ʢΤϯδχΞҎ֎ʹͱͬͯ΋ʣ

  18. ࠓ೔࿩͢ͷ͸ɾɾɾ ϕετϓϥΫςΟε Ξϯνύλʔϯ

  19. ͪ͜Βʂ ϕετϓϥΫςΟε Ξϯνύλʔϯ

  20. Ͱ΋ͦͷલʹɾɾɾ

  21. ϕετϓϥΫςΟε

  22. εΩʔϚσβΠϯ -> TwitterͷλΠϜϥΠϯΛอଘ͢Δ -> RDBMSͳΒਖ਼نԽ -> MongoDBͷϕετϓϥΫςΟε͸ʁ

  23. -> ಡΈࠐΈॏࢹ -> 1ΫΤϦ1ώοτ -> ίϝϯτ਺ʹԠͯ͡
 ॻ͖ࠐΈ͕ॏ͘ͳΔ υΩϡϝϯτΛຒΊࠐΉ session:
 _id:

    “session_id”
 device: “iPhone”
 page_views: [
 {url: “/items/1”}
 ] timeline:
 _id: “yujiosaka”
 tag: “#dbtechshowcase”
 tweets: [
 {text: “MongoDB Rocks!”}
 ]
  24. -> ॻ͖ࠐΈॏࢹ -> 2ΫΤϦN+1ώοτ -> ॻ͖ࠐΈ͸εέʔϧ͢Δ υΩϡϝϯτΛຒΊࠐ·ͳ͍ session:
 _id: “session_id”


    device: “iPhone”
 page_views: [
 {url: “/items/1”}
 ] timeline:
 _id: “yujiosaka”
 tag: “#dbtechshowcase” ! tweet: _id: 1234
 user_id: “yujiosaka”
 text: “MongoDB Rocks!”
  25. timeline:
 _id: “yujiosaka-20141111”
 tag: “#dbtechshowcase”
 tweets: [
 {text: “MongoDB Rocks!”}


    ] -> όϥϯεܕ -> 1ΫΤϦ೔਺෼ώοτ -> ॻ͖ࠐΈ͸1೔෼·Ͱ
 ͔͠ॏ͘ͳΒͳ͍ ϋΠϒϦου
  26. εΩʔϚϨε ≠ εΩʔϚఆ͕ٛෆཁ RDBMSΑΓ΋બ୒ࢶ͕ଟ͘೉͍͠ tips: IUUQXXXNPOHPECDPNQSFTFOUBUJPOTTDIFNBEFTJHOTDBMF

  27. Ξϯνύλʔϯ

  28. ΠϯσοΫεฤ ΫΤϦฤ Ξοϓσʔτฤ ΞʔΩςΫνϟฤ

  29. ᶃΠϯσοΫεͷషΓա͗

  30. MongoDB͸ϝϞϦͷ࢖͍ํ͕
 ͋·Γݡ͋͘Γ·ͤΜ

  31. before: user = new mongoose.Schema first: {type: String, required: true,

    index: true} last: {type: String, required: true, index: true} city: {type: String, required: true, index: true} ... created_at: {type: Date, default: Date.now, index: true} ! ॻ͖ࠐΈ͕٘ਜ਼ʹͳͬͯ΋ಡΈࠐΈ͕ૣ͚Ε͹͍͍͡ΌΜʂ
  32. after: user = new mongoose.Schema first: {type: String, required: true}

    last: {type: String, required: true} city: {type: String, required: true} ... created_at: {type: Date, default: Date.now} ! ॻ͖ࠐΈ͕٘ਜ਼ʹͳͬͯ΋ಡΈࠐΈ͕ૣ͚Ε͹͍͍͡ΌΜʂ
  33. ಡΈࠐΈ΋ɺΠϯσοΫε͕ ϝϞϦʹ৐Βͳ͚Ε͹஗͘ͳΔ

  34. tips: όʔδϣϯ2.8ʹPluggable Storage
 Engine͕༧ఆ͞Ε͍ͯΔͷͰɺ ݡ͍Τϯδϯ͕બ΂ΔΑ͏ʹͳΔ https://blog.compose.io/the-coming-of-the-mongodb-storage-engines/

  35. ᶄจࣈྻͷΠϯσοΫε

  36. ΠϯσοΫεͷߏ଄ -> RDBMSͱ΄ͱΜͲಉ͡ -> ΠϯσοΫεʹB-TreeΛ࠾༻ -> B-Tree͸ৗʹฏߧΛอ͍ͬͯΔ

  37. จࣈྻͷΠϯσοΫε -> ϗοτσʔλ͕෼ࢄ -> ϝϞϦޮ཰͕ѱ͍

  38. ࣌ؒɾObjectIdͷΠϯσοΫε -> ϗοτσʔλ͕ूத -> ϝϞϦޮ཰͕ྑ͍

  39. tips: Ͱ͖Δ͚ͩObjectIdΛ࢖͍·͠ΐ͏ ޙ͔Βͷஔ͖׵͑͸େมͰ͢

  40. ΠϯσοΫεฤ ΫΤϦฤ Ξοϓσʔτฤ ΞʔΩςΫνϟฤ

  41. ᶅϑΟʔϧυΛࢦఆ͠ͳ͍ΫΤϦ

  42. Ϣʔβʔ৘ใͷ͏ͪ ౎ࢢ৘ใ͚͕ͩཉ͍͠ user: {_id: “yujiosaka”, first: “Yuji”, last: “Isobe”, city:

    “Tokyo”} index: {_id:1, city: 1} // compound index
  43. ϑΟʔϧυࢦఆͳ͠ -> SQLͰͷʮselect *ʯ -> ෆཁͳωοτϫʔΫίετ -> ෆཁͳσΟεΫΞΫηε var user

    = db.users.findOne({_id: “yujiosaka”});
  44. ϑΟʔϧυࢦఆ͋Γ -> SQLͰͷʮselect cityʯ -> ωοτϫʔΫίετͷઅ໿ -> covered index ͕׆༻Ͱ͖Δ

    var user = db.users.findOne({_id: “yujiosaka”}, {city: 1});
  45. tips: covered indexΛ༗ޮ׆༻͢Ε͹ɺ
 σΟεΫ΁ͷΞΫηεҰ੾ͳ͠ʹ
 ϝϞϦ͚͔ͩΒ৘ใΛಡΈࠐΊΔ

  46. ᶆίʔϧυσʔλ΁ͷΫΤϦ

  47. MongoDBͷಛ௃ -> ϗοτσʔλ΁ͷΞΫηε͸ߴ଎ -> ίʔϧυσʔλ΁ͷΞΫηε͸஗͍ -> શσʔλ΁ͷΞΫηε͸ۤख

  48. ZenClerkʹ͓͚Δूܭ -> 5෼͝ͱʹσʔλΛཁ໿ -> 1࣌ؒ͝ͱʹ౷ܭσʔλΛूܭ -> 1೔͝ͱʹ౷ܭσʔλΛूܭ

  49. tips: ৗʹϗοτσʔλΛҙࣝͨ͠
 ࢖͍ํΛ͠·͠ΐ͏

  50. ᶇϓϥΠϚϦʹภͬͨΫΤϦ

  51. ϨϓϦΧηοτ -> ΫΤϦΛ෼ࢄͤ͞Δ͜ͱ͕Ͱ͖Δ -> ηΧϯμϦʹΫΤϦΛ౤͛Δ͔͸
 ϓϩάϥϚ͕ܾΊΒΕΔ -> σϑΥϧτͰ͸ϓϥΠϚϦʹ౤͛ΒΕΔ

  52. σϑΥϧτ -> ϓϥΠϚϦ͔Βಡ·ΕΔ -> ࠷৽ͷσʔλ͕อো͞ΕΔ var user = db.users.find({city: “Tokyo”});

  53. ηΧϯμϦΛࢦఆ -> ηΧϯμϦ͕͋Ε͹ηΧϯμϦ͔Βɺ
 ͳ͚Ε͹ϓϥΠϚϦ͔Βಡ·ΕΔ -> ࠷৽ͷσʔλ͸อো͞Εͳ͍ var user = db.users.find({city:

    “Tokyo”})
 .readPref("secondaryPreferred")
  54. tips: σʔλ͕࠷৽Ͱͳͯ͘΋Α͍৔߹͸
 ಡΈࠐΈΛηΧϯμϦʹ޲͚Δ͜ͱͰ
 ΫΤϦ͕෼ࢄ͢Δ IUUQTTQFBLFSEFDLDPNNPOHPECQSBDUJDBM TDBMJOHBOETIBSEJOHFMJPUIPSPXJU[HFO

  55. ΠϯσοΫεฤ ΫΤϦฤ Ξοϓσʔτฤ ΞʔΩςΫνϟฤ

  56. ᶈඇޮ཰ͳΞοϓσʔτ

  57. Α͘ݟ͔͚Δίʔυᶃ -> ඞͣΫΤϦ͕࣮ߦ͞ΕΔ -> ແବͳωοτϫʔΫίετ -> ඇΞτϛοΫͳॲཧ var user =

    db.users.findOne({_id: “yujiosaka”}); user.count += 1; user.save();
  58. Α͘ݟ͔͚Δίʔυᶃ -> updateͱ$incΛ࢖ͬͯ·ͱΊΒΕΔ -> 1ճͷΞοϓσʔτͰࡁΉ -> ΞτϛοΫͳॲཧ db.users.update({_id: “yujiosaka”}, {$inc:

    {count: 1}});
  59. Α͘ݟ͔͚Δίʔυᶄ -> σʔλ͕ݟ͔ͭͬͨΒߋ৽͠ɺݟ͔ͭΒͳ͔ͬͨΒ࡞੒͢Δ -> Ұݟෳࡶͳίʔυ var user = db.users.findOne({_id: “yujiosaka”});

    if (user) { user.count += 1;
 user.save(); } else { db.users.insert({_id: “yujiosaka”, count: 1}); }
  60. -> {upsert: true} Λ࢖͏ͱ·ͱΊΒΕΔ -> ΑΓγϯϓϧͳίʔυ Α͘ݟ͔͚Δίʔυᶄ db.users.update({_id: “yujiosaka”}, {$inc:

    {count: 1}}, {upsert: true});
  61. ᶉංେԽ͢ΔυΩϡϝϯτ

  62. Ϛ΢εͷ࠲ඪσʔλΛ
 อଘ͢Δ behavior: points: [{x: 151, y: 100} {x: 151,

    y: 179} {x: 151, y: 266} … {x: 151, y: 340}]
  63. MongoDBͷۤख෼໺ -> ϑΟʔϧυ͕ංେԽ͢ΔΞοϓσʔτ͕஗͍ -> σʔλ͕ҰఆͷαΠζʹऩ·Βͳ͘ͳΔͱ
 σΟεΫ಺ͰσʔλͷҠಈ͕ى͖Δ -> RedisͰMongoDBͷۤख෼໺Λิ͏

  64. Redis -> ΦϯϝϞϦܕͷKVS -> ಡΈࠐΈɾॻ͖ࠐΈ͕ૣ͍ -> جຊతʹσʔλͷӬଓԽ͸͠ͳ͍ -> σʔλܕ͕ඇৗʹ๛෋
 ʢϦετɾηοτɾϋογϡʣ

  65. RedisΛ࢖Θͳ͍৔߹ db.col.update({_id: 1234}, {$push: {points: {x: 151, y: 100}});
 db.col.update({_id:

    1234}, {$push: {points: {x: 151, y: 179}});
 db.col.update({_id: 1234}, {$push: {points: {x: 151, y: 266}});
 …
 db.col.update({_id: 1234}, {$push: {points: {x: 151, y: 340}}); -> ࠲ඪ͕Ҡಈ͢ΔͨͼʹMongoDBʹॻ͖ࠐ·ΕΔ -> αΠζ͕ऩ·Γ੾Βͳ͍ͱɺσʔλͷҠಈ͕ى͖Δ
  66. RedisΛ࢖ͬͨ৔߹ db.col.insert({points: [
 {x: 151, y: 100},
 {x: 151, y:

    179},
 {x: 151, y: 266},
 …
 {x: 151, y: 340}
 ]}); -> Ұ౓͔͠MongoDBʹॻ͖ࠐ·ͳ͍ -> υΩϡϝϯυͷαΠζ͸͜ΕҎ্େ͖͘ͳΒͳ͍
  67. tips: ͢΂ͯΛMongoDB͚ͩͰ΍Ζ͏ͱͤͣɺ
 ۤखͳ෦෼͸ଞͷٕज़Ͱิ͏͜ͱ͕େ੾

  68. ΠϯσοΫεฤ ΫΤϦฤ Ξοϓσʔτฤ ΞʔΩςΫνϟฤ

  69. ᶊ٧ΊࠐΈա͗DB

  70. σʔλΛҰͭͷDBʹ٧ΊࠐΜͰ
 ྑ͍͜ͱ͸ʢࠓͷͱ͜Ζʣ͋·Γͳ͍

  71. ͳͥDBΛ෼͚Δͷ͔ʁ -> Reader-WriterϩοΫ͕࢖ΘΕ͍ͯΔ -> 2.2.0Ҏ߱͸DB୯ҐͰϩοΫ͢Δ -> ϚγϯΛ෼͚Ε͹ϝϞϦ͕૿͑Δ

  72. ZenClerk ʹ͓͚ΔDBͷ༻్ -> ϝΠϯσʔλʢHotʣ -> ϝΠϯσʔλʢColdʣ -> ੜσʔλ -> ܭࢉ݁Ռσʔλ

    -> ౷ܭσʔλ -> αΠτଐੑσʔλ
  73. tips: όʔδϣϯ2.8ʹdocument-level locking 3.0Ҏ߱ʹpartitioned joins͕༧ఆ͞Ε͍ͯΔ
 ʢޙऀ͸͋·Γ͋ͯʹ͠ͳ͍ํ͕ແ೉ͦ͏ʣ http://www.slideshare.net/tetsutarowatanabe/mongodb-world-2014-37081258

  74. ᶃΠϯσοΫεͷషΓա͗ ᶄจࣈྻͷΠϯσοΫε ᶅϑΟʔϧυΛࢦఆ͠ͳ͍ΫΤϦ ᶆίʔϧυσʔλ΁ͷΫΤϦ ᶇϓϥΠϚϦʹภͬͨΫΤϦ ᶈඇޮ཰ͳΞοϓσʔτ ᶉංେԽ͢ΔυΩϡϝϯτ ᶊ٧ΊࠐΈա͗DB Ξϯνύλʔϯ͓͞Β͍

  75. Ξϯνύλʔϯʹ
 ؕΒͳ͍ͨΊʹ

  76. ̏ͭͷ؂ࢹ -> ϦΞϧλΠϜ؂ࢹ -> ݟฦͨ͢Ίͷ؂ࢹ -> ஗͍ΫΤϦͷ؂ࢹ

  77. ϦΞϧλΠϜ؂ࢹ

  78. -> ComposeʢMongoHQʣμογϡϘʔυ -> ݱࡏͷ݈߁νΣοΫ༻ -> ࣾ಺ʹઐ༻σΟεϓϨΠΛઃஔ -> ҟৗ͕͋Ε͹ʮ୭͔Mongoࡴͨ͠ʁʯ ϦΞϧλΠϜ؂ࢹ

  79. ݟฦͨ͢Ίͷ؂ࢹ

  80. -> MMSμογϡϘʔυ
 -> ఆظతͳ݈߁਍அ -> ো֐ൃੜ࣌ʹݟฦͯ͠
 ɹʮͳΜͰ͜ΜͳϩοΫߴ͘ͳͬͯΜͷʁʯ ݟฦͨ͢Ίͷ؂ࢹ

  81. ஗͍ΫΤϦͷ؂ࢹ

  82. -> explain(true) -> system.profile.find() -> Dex -> Compose (MongoHQ) Slow

    Queries ஗͍ΫΤϦͷ؂ࢹ
  83. tips: ObjectIdΛ࢖ͬͨΫΤϦʹ 100msҎ্͔͔ͬͨΒةݥ৴߸

  84. my.presentation.aggregate() -> MongoDB͸બ୒ࢶ͕ଟ͍ -> Ξϯνύλʔϯ͸͍͔ͭ͘ଘࡏ͢Δ -> ΞϯνύλʔϯʹؕΒͳ͍ͨΊʹ؂ࢹ͢Δ -> ࠷৽৘ใͷΩϟονΞοϓ΋ॏཁ

  85. ͍͞͝ʹ

  86. we.are “hiring! :)” https://www.zenclerk.com/recruit.html

  87. any.questions?