SimpleResource

2,515 views
2,476 views

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,515
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
5
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

SimpleResource

  1. 1. WebアプリのDBスキーマレス化がRubyにぴったりな件<br />RubyKansai #37<br />松本一輝<br />Lang-8,Inc.<br />
  2. 2. 自己紹介<br /><ul><li>Lang-8
  3. 3. Ruby歴</li></li></ul><li>本日の話題<br /><ul><li>Webアプリ開発時の問題点
  4. 4. SimpleResource</li></li></ul><li>※独断と偏見による<br /><ul><li>DBスキーマ拡張の手間
  5. 5. キャッシュ機構の複雑さ</li></ul>Webアプリ開発時の2つの問題点<br />
  6. 6. DBスキーマ拡張の手間 (一例:blogアプリ)<br />Entryモデル<br />Commentモデル<br />Commentモデル<br />
  7. 7. Commentに写真が張れる<br />Commentモデル<br />Commentモデル<br />CommentPic<br />モデルを導入<br />
  8. 8. has_many<br />has_many<br />
  9. 9. Entry一覧ページに写真枚数カウンタを追加するには?<br />Entryモデル<br />5枚<br />Entryモデル<br />2枚<br />Entryモデル<br />3枚<br />
  10. 10. ダメな例その1 : ナイーブ<br />Comment.find_all_by_entry_id.map{|c|<br />CommentPic.find_all_by_comment_id(c.id).size<br />}.sum<br />-> SQLクエリが、Entry1つにつきコメントの数だけ<br />生じる。非効率。<br />
  11. 11. (個人的には)ダメな例その2 : SQLJOIN<br />SELECT COUNT(*) FROM comment_pics<br />LEFT JOIN comments<br />ON comment_pics.comment_id = comments.id<br />LEFT JOIN entries<br />ON comment.entry_id = entries.id<br />WHERE entries.id = entry_id<br />-> クエリが遅い。<br />entries、comments、comment_pics各テーブルが<br />すべて同一DB上にあることが前提。<br />スケールがめんどくさそう。<br />
  12. 12. 一般的手法1:DBスキーマの非正規化<br />entriesテーブルに、写真カウンタフィールドを追加。<br />has_many<br />has_many<br />
  13. 13. 一般的手法2:KVSによる<br />オブジェクトキャッシュ<br />App Server<br />Read<br />Write<br />Write<br />DB Server<br />Memcached Server<br />
  14. 14. 問題点<br /><ul><li>DBスキーマの変更は高コスト
  15. 15. 複雑なキャッシュ機構
  16. 16. 効率の悪いキャッシュ生成
  17. 17. R/W比が低いアプリケーションについては効果薄(例:SNS)</li></li></ul><li>もっとシンプルに<br />
  18. 18. SimpleResource<br />
  19. 19. SimpleResource?<br /><ul><li>スキーマレス
  20. 20. JSONシリアライズ保存
  21. 21. ActiveRecord風インターフェース
  22. 22. バックエンドは任意のKVSDB (現在MySQLのみ対応)
  23. 23. 透過的にMemcachedによりキャッシュ</li></ul>なDBインターフェースライブラリ<br />
  24. 24. 使用例 –blogアプリ<br />has_many<br />has_many<br />{<br />“id” =&gt; 12763,<br />“body” =&gt; “Hello, world!”,<br />“comments” =&gt; [ {“id”=&gt;735634, “body” =&gt; “comment1”, “pics” =&gt; [“pic1.jpg”]},<br /> {“id” =&gt; 735635, “body” =&gt; “comment2”} ]<br />}<br />スキーマレス化<br />
  25. 25. class Entry &lt; SimpleResource::Base<br /> include SimpleResource::MysqlEntityBackend<br />end<br /> entry = Entry.create(&quot;subject&quot; =&gt; &quot;test&quot;,<br /> &quot;body&quot; =&gt; &quot;hello world!&quot;,<br /> &quot;author&quot; =&gt; &quot;kazuki&quot;)<br />entry.id #=&gt; 54<br /> (create時にidを指定しなかった場合は、自動採番される)<br />
  26. 26. Entryへのコメントの追加<br /> comment = {&quot;body&quot; =&gt; &quot;nice1!&quot;,<br /> &quot;from&quot; =&gt; &quot;kazuko&quot;,<br /> &quot;pics&quot; =&gt; [&quot;pic1.jpg&quot;, &quot;pic2.jpg&quot;]}<br /> entry = Entry.find(12763)<br />entry.comments ||= []<br />entry.comments &lt;&lt; comment<br />entry.save<br />ダメな例<br />
  27. 27. プロセス1<br />プロセス2<br /> entry = Entry.find(12763)<br />entry.comments ||= []<br />entry.comments &lt;&lt; “foo”<br />entry.save<br />①<br />②<br />entry = Entry.find(12763)<br />entry.comments ||= []<br />entry.comments &lt;&lt; “bar”<br />entry.save<br />③<br />④<br />プロセス1の書き込みが<br />破壊される<br />
  28. 28. Entryへのコメントの追加<br /> comment = {&quot;body&quot; =&gt; &quot;nice1!&quot;,<br /> &quot;from&quot; =&gt; &quot;kazuko&quot;,<br /> &quot;pics&quot; =&gt; [&quot;pic1.jpg&quot;, &quot;pic2.jpg&quot;]}<br />Entry.find_with_lock(12763) do |entry|<br />entry.comments ||= []<br />entry.comments &lt;&lt; comment<br />entry.save<br />end<br />
  29. 29. プロセス1<br />プロセス2<br />Entry.find_with_lock(12763) do |entry|<br />entry.comments ||= []<br />entry.comments &lt;&lt; comment<br />entry.save<br />end<br />Entry.find_with_lock(12763) do |entry|<br />entry.comments ||= []<br />entry.comments &lt;&lt; comment<br />entry.save<br />end<br />
  30. 30. SimpleResourceの主なメソッド<br /><ul><li>find
  31. 31. Create
  32. 32. destroy
  33. 33. find_or_create
  34. 34. find_with_lock
  35. 35. find_or_create_with_lock</li></ul> ※Lockを取得しなくてもReadできる。Lockの利用は紳士協定。<br /> ※30秒で強制アンロック<br />
  36. 36. インデックス機能<br /><ul><li> 全Entry( 100万件~ )を時系列で並べたページを作りたい。</li></li></ul><li> class RubyKansaiEntry &lt; SimpleResource::Base<br /> include SimpleResource::MysqlEntityBackend<br /> include SimpleResource::MysqlIndexBackend<br /> end<br /> entry = Entry.create(&quot;subject&quot; =&gt; &quot;test&quot;, <br />&quot;body&quot; =&gt; &quot;hello world!&quot;)<br />entry.add_to_index(&quot;latest_entries&quot;, Time.now.to_i)<br /> @comments, @pager = Entry.paginate(&quot;latest_entries“,<br /> :page =&gt; 1,<br /> :page_size =&gt; 10)<br />
  37. 37. まとめ<br /><ul><li>スキーマレスはありかも
  38. 38. ロックを意識する必要がでてきた
  39. 39. まるでDOMツリー操作
  40. 40. Rubyが得意とする領域</li></ul> (強力な基本クラス、高い可読性)<br />
  41. 41. ご清聴ありがとうございました!<br />GitHub:<br />http://github.com/kazuki-m/SimpleResource/tree/master<br />

×