2. アンケート
突然ですが質問です
Q1. O(N)という表記の意味を知っていますか?
1. 余裕で知ってる!
2. 見たことある。
3. 何それ?
Q2. log Nってわかりますか?(底は何でもいい)
1. どれくらいの値かイメージできる。
2. logは知っているけどイメージはできない。
3. 昔やったけど忘れた/logって丸太ですか?
2 0 1 0
J u n e 0 4
2
appengine ja night #8 koher
3. koherって誰?
普段は京都GTUG主催のappengine ja night in kansaiに参加
• koher(id:koherent、@koher)
• 読み方は「コヒー」。koherentを省略。
• 普段は京都GTUG(※)で@bufferingsさんが中心となって開催さ
れているappengine ja night in kansaiに参加している。
• App Engineは昨冬(2009年12月)から触り始める。
• Qonceptという会社でAR(拡張現実)関連の仕事に従事。
• 現在発売中の「GQ JAPAN 7月号」に収録されているバーチャル試着
のARプログラムを開発したり。よかったら買ってね!
WEBでバーチャルの時計を表示
※GTUG
Google Technology User Groupの略で、世
界中に支部を持つGoogleの技術のユーザ団体。
京都GTUGのサイトは http://bit.ly/d1nMLN
2 0 1 0
J u n e 0 4
(引用: http://bit.ly/arupc6 のPDFより) 3
appengine ja night #8 koher
4. 今回発表することになった経緯
ajn7→Skip Listでランキングを実装→声をかけていただく
• ajn7でランキング問題が取り上げられた。
• 「#appengine でランキングを実装するには?androidアプリのゲーム
で得点を元にユーザが何位だったのかを算出したい。昨日これを聞かれ
て、あーなかなか思ったよりも難しいなーと考え中。みんなならどうす
る?(ユーザ数は仮に10万だとして考える)」by @bluerabbit777jpさん
(引用: App Engineでランキングを実装する問題 http://bit.ly/dmgEiI より)
• Indexable Skip Listを使えば解決できそうだと考え実装。
• 「Google App Engineでランキングやページングを実現する 」
http://bit.ly/auyI3S
• PIAX開発者の@kibayosさんの発言から、App Engineで集約関数
が実現できるのではないかという話に。
• @kazunori_279さんに発表の機会をいただく。
2 0 1 0
J u n e 0 4
4
appengine ja night #8 koher
5. 発表のテーマ
Skip Listによる高速な順位・平均等の計算方法
• Indexable Skip Listというデータ構造を使えば、順位計算や指定範
囲に対する集約関数の実行が高速に実現できる。
• RANK、COUNT、SUM、AVG、MIN、MAX、MEDIANを実現!
• ただし、万能ではない…。
• App Engine上にどうやって実装するか。問題点は何か。
2 0 1 0
J u n e 0 4
5
appengine ja night #8 koher
6. O(log N)とは
1000個→10回、100万個→20回、10憶個→30回の処理
• アルゴリズムの速さは時間計算量のオーダーで表されることが多い。
• O(N)のように表され、「Nのオーダー」と読む。
• 厳密にはO、Ω、Θなどの表記が区別される。
• 時間計算量がO(N)とは、N回の定数倍の処理でアルゴリズムが実行でき
ることを表す(Nはデータの個数など)。
• O(log N)のときは、N=1000→10回、100万→20回、10憶→30
回程度で処理が終わると覚えておくとイメージしやすい。
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
2 0 1 0
J u n e 0 4
6
appengine ja night #8 koher
14. O(log N)とは
1000個→10回、100万個→20回、10憶個→30回の処理
• アルゴリズムの速さは時間計算量のオーダーで表されることが多い。
• O(N)のように表され、「Nのオーダー」と読む。
• 厳密にはO、Ω、Θなどの表記が区別される。
• 時間計算量がO(N)とは、N回の定数倍の処理でアルゴリズムが実行でき
ることを表す(Nはデータの個数など)。
• O(log N)のときは、N=1000→10回、100万→20回、10憶→30
回程度で処理が終わると覚えておくとイメージしやすい。
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
11を発見!
11を検索
2 0 1 0
J u n e 0 4
14
appengine ja night #8 koher
15. Linked List
Skip Listのデータ構造のベースはLinked List
• Skip Listのデータ構造はLinked Listをベースにしている。
• Linked Listは配列のように複数の値を格納するためのデータ構造。
• 各ノードは値と次ノードへのポインタを持つ(下図)。
• 先頭ノードから順次アクセスするため参照は遅い→O(N)
• 先頭への要素の挿入・削除は速い→O(1)
• 要素がソート済であっても値の検索は遅い→O(N)
H 3 5 7 11 13 17 19 T
2 0 1 0
J u n e 0 4
15
appengine ja night #8 koher
16. Linked List
Skip Listのデータ構造のベースはLinked List
• Skip Listのデータ構造はLinked Listをベースにしている。
• Linked Listは配列のように複数の値を格納するためのデータ構造。
• 各ノードは値と次ノードへのポインタを持つ(下図)。
• 先頭ノードから順次アクセスするため参照は遅い→O(N)
• 先頭への要素の挿入・削除は速い→O(1)
• 要素がソート済であっても値の検索は遅い→O(N)
5
H 17 13 11
3 19 7
データはどのような位
置に格納されていても、
2 0 1 0
T ポインタでつながって
J u n e 0 4 いればよい
16
appengine ja night #8 koher
17. Linked List
Skip Listのデータ構造のベースはLinked List
• Skip Listのデータ構造はLinked Listをベースにしている。
• Linked Listは配列のように複数の値を格納するためのデータ構造。
• 各ノードは値と次ノードへのポインタを持つ(下図)。
• 先頭ノードから順次アクセスするため参照は遅い→O(N)
• 先頭への要素の挿入・削除は速い→O(1)
• 要素がソート済であっても値の検索は遅い→O(N)
H 3 5 7 11 13 17 19 T
Linked Listは前から順
4番目を に要素にアクセスしな
参照 ければならない
2 0 1 0
J u n e 0 4
17
appengine ja night #8 koher
18. Linked List
Skip Listのデータ構造のベースはLinked List
• Skip Listのデータ構造はLinked Listをベースにしている。
• Linked Listは配列のように複数の値を格納するためのデータ構造。
• 各ノードは値と次ノードへのポインタを持つ(下図)。
• 先頭ノードから順次アクセスするため参照は遅い→O(N)
• 先頭への要素の挿入・削除は速い→O(1)
• 要素がソート済であっても値の検索は遅い→O(N)
H 3 5 7 11 13 17 19 T
先頭ノードから順々に
4番目を たどっていく
参照 →O(N)と遅い
2 0 1 0
J u n e 0 4
18
appengine ja night #8 koher
19. Linked List
Skip Listのデータ構造のベースはLinked List
• Skip Listのデータ構造はLinked Listをベースにしている。
• Linked Listは配列のように複数の値を格納するためのデータ構造。
• 各ノードは値と次ノードへのポインタを持つ(下図)。
• 先頭ノードから順次アクセスするため参照は遅い→O(N)
• 先頭への要素の挿入・削除は速い→O(1)
• 要素がソート済であっても値の検索は遅い→O(N)
H 3 5 7 11 13 17 19 T
0番目
4番目を
参照
2 0 1 0
J u n e 0 4
19
appengine ja night #8 koher
20. Linked List
Skip Listのデータ構造のベースはLinked List
• Skip Listのデータ構造はLinked Listをベースにしている。
• Linked Listは配列のように複数の値を格納するためのデータ構造。
• 各ノードは値と次ノードへのポインタを持つ(下図)。
• 先頭ノードから順次アクセスするため参照は遅い→O(N)
• 先頭への要素の挿入・削除は速い→O(1)
• 要素がソート済であっても値の検索は遅い→O(N)
H 3 5 7 11 13 17 19 T
1番目
4番目を
参照
2 0 1 0
J u n e 0 4
20
appengine ja night #8 koher
21. Linked List
Skip Listのデータ構造のベースはLinked List
• Skip Listのデータ構造はLinked Listをベースにしている。
• Linked Listは配列のように複数の値を格納するためのデータ構造。
• 各ノードは値と次ノードへのポインタを持つ(下図)。
• 先頭ノードから順次アクセスするため参照は遅い→O(N)
• 先頭への要素の挿入・削除は速い→O(1)
• 要素がソート済であっても値の検索は遅い→O(N)
H 3 5 7 11 13 17 19 T
2番目
4番目を
参照
2 0 1 0
J u n e 0 4
21
appengine ja night #8 koher
22. Linked List
Skip Listのデータ構造のベースはLinked List
• Skip Listのデータ構造はLinked Listをベースにしている。
• Linked Listは配列のように複数の値を格納するためのデータ構造。
• 各ノードは値と次ノードへのポインタを持つ(下図)。
• 先頭ノードから順次アクセスするため参照は遅い→O(N)
• 先頭への要素の挿入・削除は速い→O(1)
• 要素がソート済であっても値の検索は遅い→O(N)
H 3 5 7 11 13 17 19 T
3番目
4番目を
参照
2 0 1 0
J u n e 0 4
22
appengine ja night #8 koher
23. Linked List
Skip Listのデータ構造のベースはLinked List
• Skip Listのデータ構造はLinked Listをベースにしている。
• Linked Listは配列のように複数の値を格納するためのデータ構造。
• 各ノードは値と次ノードへのポインタを持つ(下図)。
• 先頭ノードから順次アクセスするため参照は遅い→O(N)
• 先頭への要素の挿入・削除は速い→O(1)
• 要素がソート済であっても値の検索は遅い→O(N)
H 3 5 7 11 13 17 19 T
4番目
4番目を →目標の値に到着
参照
2 0 1 0
J u n e 0 4
23
appengine ja night #8 koher
24. Linked List
Skip Listのデータ構造のベースはLinked List
• Skip Listのデータ構造はLinked Listをベースにしている。
• Linked Listは配列のように複数の値を格納するためのデータ構造。
• 各ノードは値と次ノードへのポインタを持つ(下図)。
• 先頭ノードから順次アクセスするため参照は遅い→O(N)
• 先頭への要素の挿入・削除は速い→O(1)
• 要素がソート済であっても値の検索は遅い→O(N)
H 3 5 7 11 13 17 19 T
挿入・削除時に配列の
先頭に ように後ろの要素をず
2を挿入 らす必要がない
2 0 1 0
J u n e 0 4
24
appengine ja night #8 koher
25. Linked List
Skip Listのデータ構造のベースはLinked List
• Skip Listのデータ構造はLinked Listをベースにしている。
• Linked Listは配列のように複数の値を格納するためのデータ構造。
• 各ノードは値と次ノードへのポインタを持つ(下図)。
• 先頭ノードから順次アクセスするため参照は遅い→O(N)
• 先頭への要素の挿入・削除は速い→O(1)
• 要素がソート済であっても値の検索は遅い→O(N)
H 3 5 7 11 13 17 19 T
2
挿入するノードを作成
先頭に
2を挿入
2 0 1 0
J u n e 0 4
25
appengine ja night #8 koher
26. Linked List
Skip Listのデータ構造のベースはLinked List
• Skip Listのデータ構造はLinked Listをベースにしている。
• Linked Listは配列のように複数の値を格納するためのデータ構造。
• 各ノードは値と次ノードへのポインタを持つ(下図)。
• 先頭ノードから順次アクセスするため参照は遅い→O(N)
• 先頭への要素の挿入・削除は速い→O(1)
• 要素がソート済であっても値の検索は遅い→O(N)
H 3 5 7 11 13 17 19 T
2
ポインタをつなぎ替え
先頭に →後ろの要素をずらす
2を挿入 必要はない
2 0 1 0
J u n e 0 4
26
appengine ja night #8 koher
27. Skip List ‒ 基本データ構造と検索
Skip Listを用いれば高速な(O(log N)の)検索が可能
• Skip ListはLinked Listを複数階層積み上げたようなデータ構造。
• 最下層だけみるとただのLinked List。
• 上位階層になるほど要素が間引かれる。
• 特急→急行→各停と電車を乗り継ぐように上位階層からノードをた
どることで高速な(O(log N)の)検索を実現。
H 5 T
H 3 5 11 T
H 2 3 5 7 11 13 17 T
William Pugh, Skip Lists: A Probabilistic
2 0 1 0 Alternative to Balanced Trees, (1990)
J u n e 0 4 http://bit.ly/dxfh4D
27
appengine ja night #8 koher
28. Skip List ‒ 基本データ構造と検索
Skip Listを用いれば高速な(O(log N)の)検索が可能
• Skip ListはLinked Listを複数階層積み上げたようなデータ構造。
• 最下層だけみるとただのLinked List。
• 上位階層になるほど要素が間引かれる。
• 特急→急行→各停と電車を乗り継ぐように上位階層からノードをた
どることで高速な(O(log N)の)検索を実現。
H 5 T
H 3 5 11 T
H 2 3 5 7 11 13 17 T
最上位階層の先頭ノー
2 0 1 0 13を検索 ドから検索開始
J u n e 0 4
28
appengine ja night #8 koher
29. Skip List ‒ 基本データ構造と検索
Skip Listを用いれば高速な(O(log N)の)検索が可能
• Skip ListはLinked Listを複数階層積み上げたようなデータ構造。
• 最下層だけみるとただのLinked List。
• 上位階層になるほど要素が間引かれる。
• 特急→急行→各停と電車を乗り継ぐように上位階層からノードをた
どることで高速な(O(log N)の)検索を実現。
H 5 T
H 3 5 11 T
H 2 3 5 7 11 13 17 T
次ノードがないので下
2 0 1 0 13を検索 の階層に下りる
J u n e 0 4 (次ノードが13より大
きい場合も同様) 29
appengine ja night #8 koher
30. Skip List ‒ 基本データ構造と検索
Skip Listを用いれば高速な(O(log N)の)検索が可能
• Skip ListはLinked Listを複数階層積み上げたようなデータ構造。
• 最下層だけみるとただのLinked List。
• 上位階層になるほど要素が間引かれる。
• 特急→急行→各停と電車を乗り継ぐように上位階層からノードをた
どることで高速な(O(log N)の)検索を実現。
H 5 T
H 3 5 11 T
H 2 3 5 7 11 13 17 T
次ノードが13より小さ
2 0 1 0 13を検索 いので次ノードへ進む
J u n e 0 4
30
appengine ja night #8 koher
31. Skip List ‒ 基本データ構造と検索
Skip Listを用いれば高速な(O(log N)の)検索が可能
• Skip ListはLinked Listを複数階層積み上げたようなデータ構造。
• 最下層だけみるとただのLinked List。
• 上位階層になるほど要素が間引かれる。
• 特急→急行→各停と電車を乗り継ぐように上位階層からノードをた
どることで高速な(O(log N)の)検索を実現。
H 5 T
H 3 5 11 T
H 2 3 5 7 11 13 17 T
次ノードがないので下
2 0 1 0 13を検索 の階層に下りる
J u n e 0 4
31
appengine ja night #8 koher
32. Skip List ‒ 基本データ構造と検索
Skip Listを用いれば高速な(O(log N)の)検索が可能
• Skip ListはLinked Listを複数階層積み上げたようなデータ構造。
• 最下層だけみるとただのLinked List。
• 上位階層になるほど要素が間引かれる。
• 特急→急行→各停と電車を乗り継ぐように上位階層からノードをた
どることで高速な(O(log N)の)検索を実現。
H 5 T
H 3 5 11 T
H 2 3 5 7 11 13 17 T
次ノードが13より小さ
2 0 1 0 13を検索 いので次ノードへ進む
J u n e 0 4
32
appengine ja night #8 koher
33. Skip List ‒ 基本データ構造と検索
Skip Listを用いれば高速な(O(log N)の)検索が可能
• Skip ListはLinked Listを複数階層積み上げたようなデータ構造。
• 最下層だけみるとただのLinked List。
• 上位階層になるほど要素が間引かれる。
• 特急→急行→各停と電車を乗り継ぐように上位階層からノードをた
どることで高速な(O(log N)の)検索を実現。
H 5 T
H 3 5 11 T
H 2 3 5 7 11 13 17 T
13を発見!
2 0 1 0 13を検索
J u n e 0 4
33
appengine ja night #8 koher
34. Skip List ‒ 挿入と削除 (1)
挿入・削除の際に要素の分布を維持することは困難
• どのように上位階層の要素を間引くべきか。
• 上の階層に上るごとに、要素を半分ずつ間引いていけば効率的(下図)。
• 下から2階層目では偶数番目、3階層目では4の倍数番目、4階層目
では8の倍数番目以外の要素は間引かれる。
• この場合検索は効率的だが、要素の挿入・削除時にこのような要素
の分布を維持することは困難。
H 7 T
H 3 7 13 T
H 2 3 5 7 11 13 17 T
2 0 1 0
J u n e 0 4
34
appengine ja night #8 koher
35. Skip List ‒ 挿入と削除 (2)
確率的に要素の分布を維持することで高速な挿入・削除を実現
• 挿入時に適切な確率に基づいてノードのレベル(階層数)を決定す
ることで、理想的な分布と同程度の検索効率を実現できる。
• 任意のp>1について、レベルiのノードを(1/p)^iの確率で生成。
• 例えば、p=1/2のとき、50%の確率でレベル0、25%の確率でレベ
ル1、12.5%の確率でレベル2、…のノードが生成される。
• ノードのレベルはノード作成時に決定し、その後は変化しない。
H 5 T
H 3 5 11 T
H 2 3 5 7 11 13 17 T
2 0 1 0
J u n e 0 4
35
appengine ja night #8 koher
36. Indexable Skip List ‒ 要素番号の計算
要素番号の計算のため次ノードへのポインタに距離を付加
• 次ノードへのポインタに距離情報を付加したIndexable Skip Listを
用いれば、O(log N)で要素番号や順位を計算できる(下図)。
• 検索時には通ったルートの距離の和によって要素番号を計算。
• 挿入・削除の際にはポインタの距離も更新する。
• 最上位層の距離をすべて足せばリストの全要素数も計算できる。
3 5
H 5 T
2 1 2 3
H 3 5 11 T
1 1 1 1 1 1 1 1
H 2 3 5 7 11 13 17 T
2 0 1 0
J u n e 0 4
36
appengine ja night #8 koher
37. Indexable Skip List ‒ 要素番号による検索
要素番号によって値を検索することもできる
• ノードを検索するときに、キー値での検索同様に要素番号によって
O(log N)で検索することもできる。
• キー値との比較を行うでのはなく、指定された要素番号と現在のノー
ドの要素番号を比較しながら検索を行う。
3 5
H 5 T
2 1 2 3
H 3 5 11 T
1 1 1 1 1 1 1 1
H 2 3 5 7 11 13 17 T
2 0 1 0
J u n e 0 4
37
appengine ja night #8 koher
38. Indexable Skip List ‒ 挿入・削除
挿入・削除時は全階層分の距離の更新が必要となる
• Indexable Skip Listにノードを挿入・削除する場合、対象ノードの
レベルに関わらず全階層分のデータを更新する必要がある。
• 最上位ノードまで距離情報を更新しなければならないため。
• それでも挿入・削除の計算量はO(log N)。
• 処理が途中で打ち切られた場合、データに不整合が生じる。
3 5
H 5 T
2 1 2 3
H 3 5 11 T
1 1 1 1 1 1 1 1
H 2 3 5 7 11 13 17 T
2 0 1 0
J u n e 0 4
38
appengine ja night #8 koher
39. Indexable Skip List ‒ 総和の計算
ポインタに値の区間の総和を付加すれば、総和を計算できる
• ポインタにその区間のキー値の総和を持たせれば、総和を計算する
こともできる。
• 総和を要素数(ノードの順位)で割れば平均も計算できる。
• 要素数は順位計算の原理で計算。
• この場合、ポインタに和と距離の両方の情報を持たせる。
10 48
H 5 T
5 5 18 30
H 3 5 11 T
2 3 5 7 11 13 17 0
H 2 3 5 7 11 13 17 T
2 0 1 0
J u n e 0 4
39
appengine ja night #8 koher
40. Indexable Skip List ‒ 範囲に対する集約
要素番号や総和の差をとることで、範囲内の集約ができる
• キー値や要素番号による任意の範囲を指定して、その範囲内の要素
数や総和・平均を計算することもできる。
• 例えば3<KEY<=13の範囲の要素数を求めるには、13のノードの要素
番号から3のノードの要素番号を引く。
• 指定した範囲に対して、SQLのCOUNT、SUM、AVGのような計算が
できるようになる。
3 5
H 5 T
2 1 2 3
H 3 5 11 T
1 1 1 1 1 1 1 1
H 2 3 5 7 11 13 17 T
2 0 1 0
J u n e 0 4
40
appengine ja night #8 koher
41. Indexable Skip List ‒ 最大・最小・中央値
最初のノードが最小、最後が最大、真ん中が中央値
• 任意の範囲に対して(もちろんリスト全体に対しても)、キー値の
最大・最小・中央値を簡単に計算できる。
• 最初のノードの値が最小値(昇順の場合)→O(1)
• 最後のノードの値が最大値(降順の場合)→O(1)
• 真ん中(要素数/2番目)のノードの値が中央値→O(log N)
3 5
H 5 T
2 1 2 3
H 3 5 11 T
1 1 1 1 1 1 1 1
H 2 3 5 7 11 13 17 T
2 0 1 0
J u n e 0 4
41
appengine ja night #8 koher
42. Indexable Skip List ‒ キー値以外の集約 (1)
キーに紐付けられた値の総和を計算することもできる
• ポインタにキー値そのものではなくキーに紐付けられた値の和を持
たせることで、その値の総和をO(log N)で計算することができる。
123 116
H 5 T
100 23 50 66
H 3 5 11 T
43 57 23 19 31 37 29 0
H 2 3 5 7 11 13 17 T
43 57 23 19 31 37 29
2 0 1 0
J u n e 0 4
42
appengine ja night #8 koher
43. Indexable Skip List ‒ キー値以外の集約 (2)
キー値以外の最大・最小も求められるが少々困難
• ポインタに、区間内の最大・最小の情報を付加すれば、キーに紐付
けられた値の最大・最小をO(log N)で求めることもできる。
• 範囲検索が難しい。
• 最下層から上位階層に登って再び最下層に下りる、山型のサーチパ
スならO(log N)で計算可?他にもっと良い方法があるかも。
• 削除時に、削除されるノードを除いた最大・最小の再計算が必要。
57 37
H 5 T
57 23 31 37
H 3 5 11 T
43 57 23 19 31 37 29 N/A
H 2 3 5 7 11 13 17 T
43 57 23 19 31 37 29
2 0 1 0
J u n e 0 4
43
appengine ja night #8 koher
44. App Engine上での実装 ‒ Entityとノード (1)
方法1: 1ノード、1エンティティで表す
• 1個のノードを1個のエンティティで表し、実装する。
• 次ノードへのポインタはリストプロパティに格納する。
• 4月25日のブログエントリー http://bit.ly/auyI3S の実装はこの方法。
• ノードを1個ずつたどることになるので、getが遅いDatastoreではパ
フォーマンスが悪い。
• N=10000で要素番号の計算に平均2.8秒。
H 5 T
H 3 5 11 T
H 2 3 5 7 11 13 17 T
Entity
2 0 1 0
J u n e 0 4
44
appengine ja night #8 koher
45. App Engine上での実装 ‒ Entityとノード (2)
方法2: 複数のノードを1個のエンティティで表す
• ノードを階層ごとに分解し、同階層で複数のノードをまとめて1個
のエンティティに格納する。
• 例えばp=1000に設定すると、3階層で10憶ノード程度を格納できる。
• 参照系の処理を数百ミリ秒まで縮められるか?
• エンティティの内部は二分探索で検索可。
H 5 T
H 5 17 T
H 2 3 5 7 11 13 17 T
Entity
2 0 1 0
J u n e 0 4
45
appengine ja night #8 koher
46. App Engine上での実装 ‒ 考慮すべき点
不整合を防ぐためにGlobal Transactionがほしい
• Indexableだと処理が途中で打ち切られた場合に不整合が発生する。
→ 定期的に不整合を検出して修正する。
→ Global Transactionを用いる。
• 同時実行制御の必要あり。
• 開始ノードにアクセスが集中するとスケールしないかも?
• 「私の理解だと、最近できたConsistencyモードを弱く設定しないと常に
マスタノードを読みにいく感じですね。」
by @ashigeruさん http://bit.ly/aBdxUn
• Log Counterのような方法も使えるかも?
「LogCounterはどうでしょう?・実装編 」 @kazunori_279さん
http://bit.ly/cxlNUv
• Skip Listの発展形であるSkip GraphならスケールするけどIndexableにで
きる?App Engine上で集約Skip Graphは可能?
• Indexableだと上位階層のエンティティの更新が競合しやすい。
2 0 1 0
J u n e 0 4
46
appengine ja night #8 koher
47. MVCC ‒ Global Transactionの実現
MVCCでGlobal Transactionを実現できそう
• MVCCはMulti Version Concurrency Control(多版型同時実行
制御)の略で、同時実行制御の方式。
• OracleやPostgreSQLはMVCCでトランザクションを実現している。
• 更新時にデータを上書きするのではなく、複数のバージョンのデータを
保持することで対応する。
• 読み込みと書き込みが互いにロックしないことが特徴。
• Slim3のGlobal Transactionを用いることもできるが、ライブラリ
に依存関係が生まれてしまう。
• MVCCレイヤーだけ切り出して、DatastoreServiceインタフェー
スを実装しようかと考え中。
• きちんと検証できてないので、本当に実装できるかビミョー…。
2 0 1 0
J u n e 0 4
47
appengine ja night #8 koher
48. MVCC ‒ 追記型のMVCC
追記型のMVCCではレコードを更新するのではなく追記する
• 追記型のMVCCはデータ更新時にレコードを追記する。
• トランザクションIDの有効区間(下表のStart、End)を持ち、Start
(含む)からEnd(含まない)の区間にトランザクションIDが含まれて
いる場合だけそのレコードを有効とする。
• 追記型のMVCCはPostgreSQLで採用されている。
Key Start End Data
A001 100 105 A
A001 105 999 B
トランザクションIDが
2 0 1 0 100∼104は1行目、
J u n e 0 4 105∼は2行目が有効
48
appengine ja night #8 koher
49. MVCC ‒ 追記型のMVCC
追記型のMVCCではレコードを更新するのではなく追記する
• 追記型のMVCCはデータ更新時にレコードを追記する。
• トランザクションIDの有効区間(下表のStart、End)を持ち、Start
(含む)からEnd(含まない)の区間にトランザクションIDが含まれて
いる場合だけそのレコードを有効とする。
• 追記型のMVCCはPostgreSQLで採用されている。
Key Start End Data
A001 100 105 A
A001 105 108 B
A001 108 999 C
トランザクションID
2 0 1 0
Update 108でDataをCに更新
J u n e 0 4
49
appengine ja night #8 koher
50. MVCC ‒ 追記型のMVCC
追記型のMVCCではレコードを更新するのではなく追記する
• 追記型のMVCCはデータ更新時にレコードを追記する。
• トランザクションIDの有効区間(下表のStart、End)を持ち、Start
(含む)からEnd(含まない)の区間にトランザクションIDが含まれて
いる場合だけそのレコードを有効とする。
• 追記型のMVCCはPostgreSQLで採用されている。
Key Start End Data
A001 100 105 A
A001 105 108 B
A001 108 110 C
トランザクションID
2 0 1 0
Delete 110で削除
J u n e 0 4 →110以降では有効レ
50
appengine ja night #8 koher コードなし
51. MVCC ‒ App Engine上での実装の工夫
Queryを使うと遅いので、Keyで検索できるようにする
• 有効なエンティティをQueryで検索すると遅い。
• Queryではなく、Keyでエンティティを検索できるようにする。
• Keyで最新のEntityを検索し、Transaction IDが当てはまらない場合だ
け、Linked Listの要領で過去のエンティティをたどるようにする。
• 更新の際には、現在のエンティティを複製し、元のエンティティを上書
きする際に複製されたエンティティへのポインタを持たせる。
name = name =
_A0001 100_105_1_A0001
start = 100 start = 100
end = 105 end = 105
value = B value = A
Entity
2 0 1 0
J u n e 0 4
51
appengine ja night #8 koher
52. MVCC ‒ App Engine上での実装の工夫
Queryを使うと遅いので、Keyで検索できるようにする
• 有効なエンティティをQueryで検索すると遅い。
• Queryではなく、Keyでエンティティを検索できるようにする。
• Keyで最新のEntityを検索し、Transaction IDが当てはまらない場合だ
け、Linked Listの要領で過去のエンティティをたどるようにする。
• 更新の際には、現在のエンティティを複製し、元のエンティティを上書
きする際に複製されたエンティティへのポインタを持たせる。
name = name = name =
_A0001 105_108_1_A0001 100_105_1_A0001
start = 108 start = 105 start = 100
end = 999 end = 108 end = 105
value = C value = B value = A
Entity
2 0 1 0
J u n e 0 4
Update
52
appengine ja night #8 koher
53. ライブラリの仕様
指定範囲に対して集約関数を実行可能なJavaのライブラリ
• 指定範囲に対してCOUNT、MIN、MAX、SUM、AVG、MEDIAN
相当の機能をJavaで実装。もちろんイテレーションも可能。
• Comparatorでキーの大小関係を自由に指定可能に。
• Datastoreで実現できない複雑なルールに基づく並び順も実現可。
• LongやDouble以外の値に対しても加算の演算ルールを実装すれば、
任意の型に対して総和や平均を計算可能に。
• (例)方向を単位ベクトルに変換して方向の平均を計算する。
• 双方向にポインタを持たせて逆順のリストを取得可能に。
• 部分リストを取得可能に(指定範囲の集約は部分リストを利用)。
• (例)部分リストから要素を取り出せばページングを実現できる。
• キー値だけでなく、キーに紐づいた値の集約も可能に。
2 0 1 0 気長にお待ち
J u n e 0 4
下さい…
53
appengine ja night #8 koher
54. まとめ
Indexable Skip Listによって集約関数を実現する方法を紹介
• Indexable Skip Listを使えば、App Engine上で集約関数のような
計算が可能になる。
• ただし、予め決定したソート順のある範囲に対してのみ実効できる。
• ソート順はComparatorで自由に指定できる。
• 要素番号や順位、COUNTがO(log N)で計算でき、ページングにも利用
できる。
• キーに対してはSUM、AVG、MIN、MAX、MEDIANが計算できる。
• 紐づいた値にもSUM、AVG、MIN、MAXが計算できる。
• ライブラリ化するんで気長にお待ち下さいm(_ _)m
2 0 1 0
J u n e 0 4
54
appengine ja night #8 koher