ActiveRecordデータ処理アンチパターン / active-record-anti-patterns

ActiveRecordデータ処理アンチパターン / active-record-anti-patterns

Rails Developers Meetup 2018: Day 2( https://techplay.jp/event/655769 )で行った発表の資料です。

ActiveRecordはWebエンジニア達が嫌う(?)SQLを書かずとも、Rubyオブジェクトで気軽にデータベースへアクセスできる魔法のようなツールです。しかし便利な反面、何も考えずにゴリゴリActiveRecordを使ってDBアクセスしていると、劇的に重たいクエリが発行されたり非効率的なクエリが量産されたりします。

本発表ではそれらActiveRecordで陥りがちな罠をパターン化し、ActiveRecordデータ処理アンチパターンとして発表します。

※発表では実際のサンプルコードとともにパフォーマンスの計測結果も紹介します。

---

Blog記事: http://blog.toshimaru.net/rdm2018-active-record-anti-patterns/

5919537a0ecfa5d4dea704cf878ae90e?s=128

toshimaru

March 25, 2018
Tweet

Transcript

  1. "DUJWF3FDPSEσʔλॲཧ Ξϯνύλʔϯ  3BJMT%FWFMPQFST.FFUVQ

  2. ࣗݾ঺հ • ӿຊ හؙʢtoshimaruʣ • Twitter: @toshimaru_e/Github: toshimaru • Rails

    Engineer ˏגࣜձࣾGunosy • ࠷ۙͷਪ͠gem: • toshimaru/rubocop-rails 2
  3. ࠓ೔࿩͢͜ͱ ߦͰ ̏ͭͷࣄྫΛ΋ͱʹ̒ͭͷ"DUJWF3FDPSEσʔλ ॲཧʹ͓͚ΔΞϯνύλʔϯΛίʔυͱͦͷϕ ϯνϚʔΫ݁Ռͱ΋ʹ঺հ͠·͢ɻ 3

  4. ຊൃදͷΰʔϧ "DUJWF3FDPSEΛ࢖ͬͨඇޮ཰ͳσʔλॲཧʹΑͬͯύϑΥʔ Ϛϯε͕ܶతʹམͪΔ͜ͱ͕͋Δ͜ͱΛཧղ͢Δ ຊൃදͰ঺հ͢ΔΞϯνύλʔϯΛਖ਼͘͠ཧղ͠Ξϯνύ λʔϯΛ౿ΜͩίʔυΛमਖ਼͢Δ͜ͱ͕Ͱ͖Δɻ΋͘͠͸ ΞϯνύλʔϯΛճආͨ͠ίʔυΛॻ͘͜ͱ͕Ͱ͖Δ ίʔυϨϏϡʔʹ͓͍ͯΞϯνύλʔϯΛ౿ΜͩίʔυΛ ൃݟɾࢦఠ͢Δ͜ͱ͕Ͱ͖Δ 4

  5. 8IZOPU42- w ʮͦΕ42-ͰͰ͖ΔΑʯ w ύϑΥʔϚϯεϑΝʔετͰٕज़બ୒͢ΔͷͰ͋Ε͹ۃ࿦/P 03. /P3VCZ͕ϕετ w ͳͥ"DUJWF3FDPSEʹͩ͜ΘΔ͔ʁ w

    %#ͱͷΠϯλϥΫγϣϯΛ"DUJWF3FDPSEϞσϧʹू໿͠ڽू ౓ͷߴ͍ϞσϧΛߏங͢Δ w 'MFYJCJMJUZ ॊೈੑ .FJOUFOBCJMJUZ ϝϯςφϯεੑ  3FBEBCJMJUZ Մಡੑ 5
  6. ϕϯνϚʔΫ w ϕϯνϚʔΫ͸Լه63-ͷίʔυΛࢀߟʹॲཧͷ࣮ߦ࣌ؒͱϝϞϦ࢖༻ྔΛ ܭଌ w IUUQTEBMJCPSOBTFWJDDPNQPTUTQSPDFTTJOHMBSHFDTWpMFTXJUISVCZ w ϕϯνϚʔΫ؀ڥ w $JSDMF$*

    $16.# ্Ͱܭଌ w 3VCZ 3BJMT .Z42- w ιʔείʔυɾϕϯνϚʔΫ݁Ռ͸ެ։͞Ε·͢ w IUUQTHJUIVCDPNUPTIJNBSVSENSBJMT 6
  7. લఏࣄ߲ w ࠓճίʔυ಺Ͱొ৔͢ΔϞσϧ͸6TFSIBTNBOZ1PTUTͳؔ ܎ͷ6TFSϞσϧ1PTUϞσϧ w ࣄલʹ6TFSສ݅ɺ1PTU໿ສ݅Λσʔλͱͯ͠ొ࿥ w ϢʔβʔͷλΠϜκʔϯ͸શͯ65$ͱͯ͠ѻ͏ w ࠓճߦ͏࠷దԽͷର৅͸ΞϓϦέʔγϣϯίʔυͰ͋Γɺ

    σʔλϕʔεͷ࠷దԽ͸ߟ͑ͳ͍ w σʔλϕʔεͷτϥϯβΫγϣϯॲཧ͸ߟྀ͠ͳ͍ 7
  8. εΩʔϚఆٛ TDIFNBSC 8

  9. ࣄྫ̍ શϢʔβʔͷத͔Β೥Ҏ߱ͷొ࿥Ϣʔβʔ΁ϙΠ ϯτΛ෇༩͢Δɻ 9 ˞ొ࿥೔6TFSDSFBUFE@BUͱͯ͠ѻ͍·͢

  10. ΦϦδφϧιʔείʔυ 10

  11. ΦϦδφϧίʔυ ϕϯνϚʔΫ 11 5JNF .FNPSZ ΦϦδφϧ TFDT .#

  12. -PPLT(PPE5P:PV 12

  13. All Each Pattern 13 Anti-Pattern

  14. "MM&BDI1BUUFSO w .PEFMBMMͰ͋ΔςʔϒϧͷશϨίʔυΛऔಘͨ͠ޙʹɺ ͦͷ݁ՌΛFBDIͰϧʔϓͤ͞Δ͜ͱ 14

  15. "MM&BDI1BUUFSO໰୊఺ w શ݅औಘͰϝϞϦ͕ඡഭ w ͦ΋ͦ΋શ݅औಘͰ͋Δඞཁ͕͋Δͷ͔ʁ w ୯७ʹϧʔϓճ਺͕૿͑ΔͷͰ$16Ϧιʔε΋ফඅ 15

  16. "MM&BDI1BUUFSOղܾࡦ  6TFSऔಘ݅਺ΛϑΟϧλʔ w BMM➜XIFSF  গͣͭ͠6TFSΛऔಘͯ͠ϝϞϦϑϨϯυϦʔͳॲཧʹ w FBDI➜pOE@FBDI 16

  17. վળޙιʔείʔυ 17

  18. վળޙιʔείʔυ ϕϯνϚʔΫ 18 5JNF .FNPSZ ΦϦδφϧ TFDT .# վળ TFDT

    .#
  19. 19 -PPLT(PPE5P:PV

  20. N+1 Update Queries Pattern 20 Anti-Pattern

  21. / 6QEBUF2VFSJFT1BUUFSO 21 1 Select N Update

  22. / 6QEBUF2VFSJFT1BUUFSO ໰୊఺ w ճ4FMFDU /ճ6QEBUFͷΫΤϦ͕૸Δ w /ͷ਺͕ଟ͘ͳΕ͹ͳΔ΄ͲύϑΥʔϚϯε͕ѱԽ w ؔ࿈Ξϯνύλʔϯ

    w / %FMFUF1BUUFSO 22
  23. / 6QEBUF2VFSJFT1BUUFSO ղܾࡦ  ෳ਺ϨίʔυΛҰׅͰߋ৽͢Δ w VQEBUF➜VQEBUF@BMM 23

  24. վળޙιʔείʔυ 24

  25. վળޙιʔείʔυ ϕϯνϚʔΫ 25 5JNF .FNPSZ ΦϦδφϧ TFDT .# վળ TFDT

    .# վળ̎ TFDT .#
  26. վળޙιʔείʔυ ϕϯνϚʔΫ 26 5JNF .FNPSZ ΦϦδφϧ TFDT .# վળ TFDT

    .# վળ̎ TFDT .# ࣄྫ̍݁Ռ ࣮ߦ࣌ؒ͸ഒҎ্ߴ଎ԽɺϝϞϦফඅ΋෼ͷఔ౓· Ͱ཈͑Δ͜ͱ͕Ͱ͖·ͨ͠ɻ
  27. ஫ҙ఺ w VQEBUFͱVQEBUF@BMMͷॲཧ͸౳ՁͰ͸ͳ͍ w JUEPFTOPUUSJHHFS"DUJWF3FDPSEDBMMCBDLTPSWBMJEBUJPOT w ݱ৔ʹ͓͍ͯ͸ద੾ͳॲཧΛՃ্͑ͨͰVQEBUF@BMMΛ࢖͏͜ ͱ w ςʔϒϧϩοΫʹ஫ҙʂ

    w ద੾ͳτϥϯβΫγϣϯઃఆɾϩοΫͷઃఆΛʂ w গͣͭ͠VQEBUF@BMMJO@CBUDIFT VQEBUF@BMM 27
  28. ࣄྫ̎ Ϣʔβʔຖͷهࣄͷ͍͍Ͷ਺ʢMJLF@DPVOUʣ߹ܭ͕ଟ͍ॱͰ 501ϢʔβʔͷϢʔβʔ໊Λ߹ܭ͍͍Ͷ਺ͱͱ΋ʹग़ྗ ͢Δɻ 28 ˞࣮ࡍ͸ԿΒ͔ͷσʔλετΞʹอଘ͢Δॲཧ͕૝ఆ͞ΕΔ͕ࠓճ͸γϯϓϧʹඪ ४ग़ྗͱ͢Δ

  29. 29 ΦϦδφϧιʔείʔυ

  30. ΦϦδφϧίʔυ ϕϯνϚʔΫ 30 5JNF .FNPSZ ΦϦδφϧ TFDT .#

  31. 31 -PPLT(PPE5P:PV

  32. Ruby Aggregation Pattern 32 Anti-Pattern

  33. 3VCZ"HHSFHBUJPO1BUUFSO w Ϩίʔυશऔಘɺ3VCZͷੈքͰूܭɾฒͼସ͑ 33

  34. 3VCZ"HHSFHBUJPO1BUUFSO ໰୊఺ w طʹ঺հͨ͠ԼهͷΞϯνύλʔϯΛ౿ΜͰ͍Δ w "MM&BDI1BUUFSO w ϝϞϦϦιʔεʹՃ͑ɺ$16Ϧιʔε΋ແବʹফඅ 34

  35. 3VCZ"HHSFHBUJPO1BUUFSO ղܾࡦ w ૉ௚ʹ3%#ͷू໿ɾूܭؔ਺Λ࢖͍·͠ΐ͏ w <3VCZ>TPSU@CZSFWFSTF➜<42->PSEFS w <3VCZ>UBLF➜<42->MJNJU w <3VCZ>QPTUTTVN

    MJLF@DPVOU ➜<42-> HSPVQ VTFS@JE TFMFDU l46. MJLF@DPVOU z 35
  36. վળޙιʔείʔυ 36

  37. վળޙιʔείʔυ ϕϯνϚʔΫ 37 5JNF .FNPSZ ΦϦδφϧ TFDT .# վળ TFDT

    .#
  38. 38 -PPLT(PPE5P:PV

  39. N+1 Queries Pattern 39 Anti-Pattern

  40. 40 N+1 / 2VFSJFT1BUUFSO w ৄ͍͠આ໌͸লུ w 3BJMTॳ৺ऀ͕࠷΋౿Έ΍͍͢ύλʔϯ

  41. / 2VFSJFT1BUUFSOղܾࡦ w ϞσϧʹJODMVEFTΛ෇༩ w ʲ5JQTʳKPJOT JODMVEFT QSFMPBE FBHFS@MPBEͷҧ͍Λҙࣝ ͯ͠࢖͍෼͚ΒΕΔͱϕετ

    w SFG"DUJWF3FDPSEͷKPJOTͱQSFMPBEͱJODMVEFTͱ FBHFS@MPBEͷҧ͍ 41
  42. վળޙιʔείʔυ 42

  43. վળޙιʔείʔυ ϕϯνϚʔΫ 43 5JNF .FNPSZ ΦϦδφϧ TFDT .# վળ̍ TFDT

    .# վળ̎ TFDT .#
  44. վળޙιʔείʔυ ϕϯνϚʔΫ 44 5JNF .FNPSZ ΦϦδφϧ TFDT .# վળ̍ TFDT

    .# վળ̎ TFDT .# ࣄྫ̎݁Ռ ࣮ߦ࣌ؒ͸໿ഒߴ଎ԽɺϝϞϦফඅ΋෼ͷҎԼ·Ͱ ཈͑Δ͜ͱ͕Ͱ͖·ͨ͠ɻ
  45. ࣄྫ̏ Ϣʔβʔຖͷهࣄͷ͍͍Ͷ਺ʢMJLF@DPVOUʣ߹ܭ͕ଟ͍ॱͰ 501ϢʔβʔͷϢʔβʔ*%ҰཡΛग़ྗ͢Δɻ 45 ˞Ϣʔβʔ*%Ұཡ͸"SSBZͷܗࣜͰඪ४ग़ྗͰ͖ͯΕ͹Α͠ͱ͢Δ

  46. 46 ΦϦδφϧιʔείʔυ

  47. ΦϦδφϧίʔυ ϕϯνϚʔΫ 47 5JNF .FNPSZ ΦϦδφϧ TFDT .#

  48. 48 -PPLT(PPE5P:PV

  49. Unnecessary Query Pattern 49 Anti-Pattern

  50. 6OOFDFTTBSZ2VFSZ1BUUFSO ໰୊఺ w ຊདྷඞཁͰ͸ͳ͍ϦιʔεΛऔಘ͍ͯ͠Δ͜ͱ w ແବͳ42-ൃߦɾϝϞϦফඅ͕ൃੜ 50

  51. 6OOFDFTTBSZ2VFSZ1BUUFSO ղܾࡦ w ෆඞཁͳࢀরΛ࡟আ w QPTUVTFSJE➜QPTUVTFS@JE 51

  52. վળޙιʔείʔυ 52

  53. վળޙιʔείʔυ ϕϯνϚʔΫ 53 5JNF .FNPSZ ΦϦδφϧ TFDT .# վળ TFDT

    .#
  54. 54 -PPLT(PPE5P:PV

  55. Unnecessary Model Initialization Pattern 55 Anti-Pattern

  56. w ෆඞཁͳ"DUJWF3FDPSEϞσϧͷੜ੒͕ൃੜ w "DUJWF3FDPSEͷϞσϧੜ੒ίετ͸ۃΊͯߴ͍ w "3ϞσϧΛੜ੒͠ͳͯ͘ࡁΉͷͰ͋Ε͹ɺͦΕʹӽ͠ ͨ͜ͱ͸ͳ͍ 6OOFDFTTBSZ.PEFM*OJU1BUUFSO ໰୊఺ 56

  57. 6OOFDFTTBSZ.PEFM*OJU1BUUFSO ղܾࡦ w "DUJWF3FDPSEϞσϧΛੜ੒ΛӌճͰ͖ΔϝιουΛ࢖༻ w TFMFDU➜QMVDL 57

  58. վળޙιʔείʔυ 58

  59. վળޙιʔείʔυ ϕϯνϚʔΫ 59 5JNF .FNPSZ ΦϦδφϧ TFDT .# վળ̍ TFDT

    .# վળ̎ TFDT .#
  60. 5JNF .FNPSZ ΦϦδφϧ TFDT .# վળ̍ TFDT .# վળ̎ TFDT

    .# վળޙιʔείʔυ ϕϯνϚʔΫ 60 ࣮ߦ࣌ؒɺϝϞϦফඅͱ΋ʹఔ౓·Ͱվળ͢Δ͜ͱ͕Ͱ ͖·ͨ͠ɻ ࣄྫ̏݁Ռ 60
  61. "OUJ1BUUFSOT  "MM&BDI1BUUFSO  / 6QEBUF2VFSJFT1BUUFSO  3VCZ"HHSFHBUJPO1BUUFSO  /

    2VFSJFT1BUUFSO  6OOFDFTTBSZ2VFSZ1BUUFSO  6OOFDFTTBSZ.PEF*OJUJBMJ[BUJPO1BUUFSO 61
  62. ·ͱΊ w "DUJWF3FDPSE͸ॾਕͷ݋ w ਖ਼͘͠࢖͑͹ڧྗͳπʔϧͰ͋ΔҰํɺޡͬͨ࢖͍ํΛ ͢ΔͱϘτϧωοΫΛ࡞Γग़͠΍͍͢ w ීஈ͔ΒΫΤϦϩάΛோΊͯσʔλϕʔεͷύϑΥʔϚϯ εΛҙࣝͯ͠ίʔυΛॻ͘श׳Λ͚ͭΔ w

    ίʔυϨϏϡʔ΋ಉ༷ 62
  63. ͱ͸͍͑ w Ұൠతʹσʔλϕʔεͷ࠷దԽͷํ͕γεςϜશମʹޮ͘ ൚༻తͳղܾࡦͱͳΓ΍͍͢ w σʔλϕʔεͷ࠷దԽΛ·ͣ͸ݕ౼ͦ͠ΕΛߦ্ͬͨͰΞ ϓϦέʔγϣϯίʔυͷ࠷దԽΛߦ͏͜ͱ w SFGʰ42-Ξϯνύλʔϯʱ 63

  64. "DUJWF3FDPSEΛਖ਼͘͠࢖͍͜ͳͯ͠ ΑΓྑ͍"DUJWF3FDPSEϥΠϑΛʂ 64

  65. Thank You 65