ツイートID生成とツイッターリアルタイム検索システムの話

Post on 14-Dec-2014

14.873 views 0 download

description

 

Transcript of ツイートID生成とツイッターリアルタイム検索システムの話

ツイートID生成とツイッターリアルタイム検索

システムの話

PFIセミナー

Eiichiro Iwata

2012年 12月20日

自己紹介

l 岩田 英一郎 (eiichiroi)l 元さいたまな人

l 経歴l 2009年6月~ アルバイトl 2010年3月  埼玉大学 大学院理工学研究科 修了l 2010年8月~ PFI入社

l 所属l 製品開発部l Sedueプロジェクト

l 仕事l Sedue(検索エンジン)の開発

l コア~運用ツールを幅広くl 研究開発成果の取り込み

2

本日の内容

l ツイートID生成システムSnowflakeの解説l ツイートIDの構造と生成方法

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

3

突然ですが

4

ツイッターIDの生成方法を

知っていますか5

これ

6

本題

7

ツイートID生成システムSnowflakeとは

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

8() httpsgithubcomtwittersnowflake

Snowflakeが開発されるまで

l ツイートの流速増加とツイッターのシステム移行l 5億ツイート日(約6000ツイート秒) (1)

l 2012年10月時点l MySQLからCassandraやGizzard(Sharded MySQL)への移行

l CassandraがID生成をデフォルトで提供していないl ステータスIDの変遷 (2 3 4)

l 2006年5月~ 符号付き32bitl 2009年6月~ 符号無し32bitl 2009年9月~ 64bitl 2010年11月~ 64bit(現状のsnowflake)

l 要求l スケールする(分散できる)

9

(1) Report Twitter hits half a billion tweets a day(2) Twitpocalypse - TwitterメッセージIDの64ビット科- いよいよ明日に実施(3) Status IDs are changing on 21st September(4) Announcing Snowflake

生成するIDの構造

l 64bitを3つのブロックに分割l 時刻(41bit69年分)

l (おそらく)snowflakeの運用開始時刻からの経過時間(ミリ秒)l 2010年11月4日(epoch 1288834974657)が基点

l マシンID(10bit1024台分)l データセンターID(上位5bit)ワーカーID(下位5bit)l 起動時にZookeeperか設定ファイルから取得

l 連番(12bit4096個)l 同時刻同マシンでのID重複回避用ワーカー別

l 参考 バルス砲 25088 ツイート秒10

時刻 マシンID 連番

41bit 10bit 12bit

ツイートIDのデコード(デモ)

l ツイートID = 279622981959970816l 時刻 = 1355502288700 (2012-12-15 012448 +0900)l マシンID = 39

l データセンタID = 1l ワーカーID = 7

l 連番 = 011

生成するIDの特徴

l 64bit整数l ユニークl 時間とともにIDの値が増加する

l ステータスIDでざっくり時刻順にソートできる(k-sorted)l 目標精度は1秒l 1秒以内に投稿されたツイート間では順序を保証しない

l 実際の時刻順と逆になることもある

12

生成するIDの特徴 - k-sorted

l 系列α = (a1 an) が k-sorted であるとはl 全ての 1 ≦ i j ≦ n に対して i lt j-k ならば ai ≦ aj

l 概要l 0-sortedは普通のソートと等価l 距離 k 以内の要素間での順序は不問

l 例 (2 1 3)は1-sorted

l k-sortedの性質 (1 2)

l 系列αとkが与えられたときにk-soterdかどうかはO(n)で判定可能l 系列αが与えられたときに最も小さいkの値をO(n)で計算可能l 2つのk-sorted系列が与えられたときにそれらをマージした1つの

k-sorted系列をO(n)で計算可能l 系列αをk-sortedを満たすようにするにはO(n log k)で計算可能

13

a1 ai an ai-k+1

k

(1) Roughly Sorting A Generalization of Sorting(2) Roughly Sorting Sequential and Parallel Approach

生成するIDの特徴 - k-sortedのkの値

l 複数のマシンで完全に同じ時刻を参照できたと仮定するとl 222 -sorted 22bit = 10bit(マシンID)+12bit(連番)

l 精度 1ミリ秒l 232-sorted 32bit = 約10bit(時刻)+10bit(マシンID)+12bit(連番)

l 精度 1秒l 実際にはマシンID連番が疎なので k はそこまで大きくないはず

l 3000 ツイート秒なら連番は3くらいl NTPの精度はミリ秒単位 ()

l 現実的にはこちらの方が精度のネックになりそうl kの値自体にあまり意味はないはず

14

時刻 マシンID 連番

41bit 10bit 12bit

() httpwww2nictgojpaeriststspPubNtpqahtmlq2-2

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

自己紹介

l 岩田 英一郎 (eiichiroi)l 元さいたまな人

l 経歴l 2009年6月~ アルバイトl 2010年3月  埼玉大学 大学院理工学研究科 修了l 2010年8月~ PFI入社

l 所属l 製品開発部l Sedueプロジェクト

l 仕事l Sedue(検索エンジン)の開発

l コア~運用ツールを幅広くl 研究開発成果の取り込み

2

本日の内容

l ツイートID生成システムSnowflakeの解説l ツイートIDの構造と生成方法

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

3

突然ですが

4

ツイッターIDの生成方法を

知っていますか5

これ

6

本題

7

ツイートID生成システムSnowflakeとは

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

8() httpsgithubcomtwittersnowflake

Snowflakeが開発されるまで

l ツイートの流速増加とツイッターのシステム移行l 5億ツイート日(約6000ツイート秒) (1)

l 2012年10月時点l MySQLからCassandraやGizzard(Sharded MySQL)への移行

l CassandraがID生成をデフォルトで提供していないl ステータスIDの変遷 (2 3 4)

l 2006年5月~ 符号付き32bitl 2009年6月~ 符号無し32bitl 2009年9月~ 64bitl 2010年11月~ 64bit(現状のsnowflake)

l 要求l スケールする(分散できる)

9

(1) Report Twitter hits half a billion tweets a day(2) Twitpocalypse - TwitterメッセージIDの64ビット科- いよいよ明日に実施(3) Status IDs are changing on 21st September(4) Announcing Snowflake

生成するIDの構造

l 64bitを3つのブロックに分割l 時刻(41bit69年分)

l (おそらく)snowflakeの運用開始時刻からの経過時間(ミリ秒)l 2010年11月4日(epoch 1288834974657)が基点

l マシンID(10bit1024台分)l データセンターID(上位5bit)ワーカーID(下位5bit)l 起動時にZookeeperか設定ファイルから取得

l 連番(12bit4096個)l 同時刻同マシンでのID重複回避用ワーカー別

l 参考 バルス砲 25088 ツイート秒10

時刻 マシンID 連番

41bit 10bit 12bit

ツイートIDのデコード(デモ)

l ツイートID = 279622981959970816l 時刻 = 1355502288700 (2012-12-15 012448 +0900)l マシンID = 39

l データセンタID = 1l ワーカーID = 7

l 連番 = 011

生成するIDの特徴

l 64bit整数l ユニークl 時間とともにIDの値が増加する

l ステータスIDでざっくり時刻順にソートできる(k-sorted)l 目標精度は1秒l 1秒以内に投稿されたツイート間では順序を保証しない

l 実際の時刻順と逆になることもある

12

生成するIDの特徴 - k-sorted

l 系列α = (a1 an) が k-sorted であるとはl 全ての 1 ≦ i j ≦ n に対して i lt j-k ならば ai ≦ aj

l 概要l 0-sortedは普通のソートと等価l 距離 k 以内の要素間での順序は不問

l 例 (2 1 3)は1-sorted

l k-sortedの性質 (1 2)

l 系列αとkが与えられたときにk-soterdかどうかはO(n)で判定可能l 系列αが与えられたときに最も小さいkの値をO(n)で計算可能l 2つのk-sorted系列が与えられたときにそれらをマージした1つの

k-sorted系列をO(n)で計算可能l 系列αをk-sortedを満たすようにするにはO(n log k)で計算可能

13

a1 ai an ai-k+1

k

(1) Roughly Sorting A Generalization of Sorting(2) Roughly Sorting Sequential and Parallel Approach

生成するIDの特徴 - k-sortedのkの値

l 複数のマシンで完全に同じ時刻を参照できたと仮定するとl 222 -sorted 22bit = 10bit(マシンID)+12bit(連番)

l 精度 1ミリ秒l 232-sorted 32bit = 約10bit(時刻)+10bit(マシンID)+12bit(連番)

l 精度 1秒l 実際にはマシンID連番が疎なので k はそこまで大きくないはず

l 3000 ツイート秒なら連番は3くらいl NTPの精度はミリ秒単位 ()

l 現実的にはこちらの方が精度のネックになりそうl kの値自体にあまり意味はないはず

14

時刻 マシンID 連番

41bit 10bit 12bit

() httpwww2nictgojpaeriststspPubNtpqahtmlq2-2

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

本日の内容

l ツイートID生成システムSnowflakeの解説l ツイートIDの構造と生成方法

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

3

突然ですが

4

ツイッターIDの生成方法を

知っていますか5

これ

6

本題

7

ツイートID生成システムSnowflakeとは

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

8() httpsgithubcomtwittersnowflake

Snowflakeが開発されるまで

l ツイートの流速増加とツイッターのシステム移行l 5億ツイート日(約6000ツイート秒) (1)

l 2012年10月時点l MySQLからCassandraやGizzard(Sharded MySQL)への移行

l CassandraがID生成をデフォルトで提供していないl ステータスIDの変遷 (2 3 4)

l 2006年5月~ 符号付き32bitl 2009年6月~ 符号無し32bitl 2009年9月~ 64bitl 2010年11月~ 64bit(現状のsnowflake)

l 要求l スケールする(分散できる)

9

(1) Report Twitter hits half a billion tweets a day(2) Twitpocalypse - TwitterメッセージIDの64ビット科- いよいよ明日に実施(3) Status IDs are changing on 21st September(4) Announcing Snowflake

生成するIDの構造

l 64bitを3つのブロックに分割l 時刻(41bit69年分)

l (おそらく)snowflakeの運用開始時刻からの経過時間(ミリ秒)l 2010年11月4日(epoch 1288834974657)が基点

l マシンID(10bit1024台分)l データセンターID(上位5bit)ワーカーID(下位5bit)l 起動時にZookeeperか設定ファイルから取得

l 連番(12bit4096個)l 同時刻同マシンでのID重複回避用ワーカー別

l 参考 バルス砲 25088 ツイート秒10

時刻 マシンID 連番

41bit 10bit 12bit

ツイートIDのデコード(デモ)

l ツイートID = 279622981959970816l 時刻 = 1355502288700 (2012-12-15 012448 +0900)l マシンID = 39

l データセンタID = 1l ワーカーID = 7

l 連番 = 011

生成するIDの特徴

l 64bit整数l ユニークl 時間とともにIDの値が増加する

l ステータスIDでざっくり時刻順にソートできる(k-sorted)l 目標精度は1秒l 1秒以内に投稿されたツイート間では順序を保証しない

l 実際の時刻順と逆になることもある

12

生成するIDの特徴 - k-sorted

l 系列α = (a1 an) が k-sorted であるとはl 全ての 1 ≦ i j ≦ n に対して i lt j-k ならば ai ≦ aj

l 概要l 0-sortedは普通のソートと等価l 距離 k 以内の要素間での順序は不問

l 例 (2 1 3)は1-sorted

l k-sortedの性質 (1 2)

l 系列αとkが与えられたときにk-soterdかどうかはO(n)で判定可能l 系列αが与えられたときに最も小さいkの値をO(n)で計算可能l 2つのk-sorted系列が与えられたときにそれらをマージした1つの

k-sorted系列をO(n)で計算可能l 系列αをk-sortedを満たすようにするにはO(n log k)で計算可能

13

a1 ai an ai-k+1

k

(1) Roughly Sorting A Generalization of Sorting(2) Roughly Sorting Sequential and Parallel Approach

生成するIDの特徴 - k-sortedのkの値

l 複数のマシンで完全に同じ時刻を参照できたと仮定するとl 222 -sorted 22bit = 10bit(マシンID)+12bit(連番)

l 精度 1ミリ秒l 232-sorted 32bit = 約10bit(時刻)+10bit(マシンID)+12bit(連番)

l 精度 1秒l 実際にはマシンID連番が疎なので k はそこまで大きくないはず

l 3000 ツイート秒なら連番は3くらいl NTPの精度はミリ秒単位 ()

l 現実的にはこちらの方が精度のネックになりそうl kの値自体にあまり意味はないはず

14

時刻 マシンID 連番

41bit 10bit 12bit

() httpwww2nictgojpaeriststspPubNtpqahtmlq2-2

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

突然ですが

4

ツイッターIDの生成方法を

知っていますか5

これ

6

本題

7

ツイートID生成システムSnowflakeとは

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

8() httpsgithubcomtwittersnowflake

Snowflakeが開発されるまで

l ツイートの流速増加とツイッターのシステム移行l 5億ツイート日(約6000ツイート秒) (1)

l 2012年10月時点l MySQLからCassandraやGizzard(Sharded MySQL)への移行

l CassandraがID生成をデフォルトで提供していないl ステータスIDの変遷 (2 3 4)

l 2006年5月~ 符号付き32bitl 2009年6月~ 符号無し32bitl 2009年9月~ 64bitl 2010年11月~ 64bit(現状のsnowflake)

l 要求l スケールする(分散できる)

9

(1) Report Twitter hits half a billion tweets a day(2) Twitpocalypse - TwitterメッセージIDの64ビット科- いよいよ明日に実施(3) Status IDs are changing on 21st September(4) Announcing Snowflake

生成するIDの構造

l 64bitを3つのブロックに分割l 時刻(41bit69年分)

l (おそらく)snowflakeの運用開始時刻からの経過時間(ミリ秒)l 2010年11月4日(epoch 1288834974657)が基点

l マシンID(10bit1024台分)l データセンターID(上位5bit)ワーカーID(下位5bit)l 起動時にZookeeperか設定ファイルから取得

l 連番(12bit4096個)l 同時刻同マシンでのID重複回避用ワーカー別

l 参考 バルス砲 25088 ツイート秒10

時刻 マシンID 連番

41bit 10bit 12bit

ツイートIDのデコード(デモ)

l ツイートID = 279622981959970816l 時刻 = 1355502288700 (2012-12-15 012448 +0900)l マシンID = 39

l データセンタID = 1l ワーカーID = 7

l 連番 = 011

生成するIDの特徴

l 64bit整数l ユニークl 時間とともにIDの値が増加する

l ステータスIDでざっくり時刻順にソートできる(k-sorted)l 目標精度は1秒l 1秒以内に投稿されたツイート間では順序を保証しない

l 実際の時刻順と逆になることもある

12

生成するIDの特徴 - k-sorted

l 系列α = (a1 an) が k-sorted であるとはl 全ての 1 ≦ i j ≦ n に対して i lt j-k ならば ai ≦ aj

l 概要l 0-sortedは普通のソートと等価l 距離 k 以内の要素間での順序は不問

l 例 (2 1 3)は1-sorted

l k-sortedの性質 (1 2)

l 系列αとkが与えられたときにk-soterdかどうかはO(n)で判定可能l 系列αが与えられたときに最も小さいkの値をO(n)で計算可能l 2つのk-sorted系列が与えられたときにそれらをマージした1つの

k-sorted系列をO(n)で計算可能l 系列αをk-sortedを満たすようにするにはO(n log k)で計算可能

13

a1 ai an ai-k+1

k

(1) Roughly Sorting A Generalization of Sorting(2) Roughly Sorting Sequential and Parallel Approach

生成するIDの特徴 - k-sortedのkの値

l 複数のマシンで完全に同じ時刻を参照できたと仮定するとl 222 -sorted 22bit = 10bit(マシンID)+12bit(連番)

l 精度 1ミリ秒l 232-sorted 32bit = 約10bit(時刻)+10bit(マシンID)+12bit(連番)

l 精度 1秒l 実際にはマシンID連番が疎なので k はそこまで大きくないはず

l 3000 ツイート秒なら連番は3くらいl NTPの精度はミリ秒単位 ()

l 現実的にはこちらの方が精度のネックになりそうl kの値自体にあまり意味はないはず

14

時刻 マシンID 連番

41bit 10bit 12bit

() httpwww2nictgojpaeriststspPubNtpqahtmlq2-2

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

ツイッターIDの生成方法を

知っていますか5

これ

6

本題

7

ツイートID生成システムSnowflakeとは

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

8() httpsgithubcomtwittersnowflake

Snowflakeが開発されるまで

l ツイートの流速増加とツイッターのシステム移行l 5億ツイート日(約6000ツイート秒) (1)

l 2012年10月時点l MySQLからCassandraやGizzard(Sharded MySQL)への移行

l CassandraがID生成をデフォルトで提供していないl ステータスIDの変遷 (2 3 4)

l 2006年5月~ 符号付き32bitl 2009年6月~ 符号無し32bitl 2009年9月~ 64bitl 2010年11月~ 64bit(現状のsnowflake)

l 要求l スケールする(分散できる)

9

(1) Report Twitter hits half a billion tweets a day(2) Twitpocalypse - TwitterメッセージIDの64ビット科- いよいよ明日に実施(3) Status IDs are changing on 21st September(4) Announcing Snowflake

生成するIDの構造

l 64bitを3つのブロックに分割l 時刻(41bit69年分)

l (おそらく)snowflakeの運用開始時刻からの経過時間(ミリ秒)l 2010年11月4日(epoch 1288834974657)が基点

l マシンID(10bit1024台分)l データセンターID(上位5bit)ワーカーID(下位5bit)l 起動時にZookeeperか設定ファイルから取得

l 連番(12bit4096個)l 同時刻同マシンでのID重複回避用ワーカー別

l 参考 バルス砲 25088 ツイート秒10

時刻 マシンID 連番

41bit 10bit 12bit

ツイートIDのデコード(デモ)

l ツイートID = 279622981959970816l 時刻 = 1355502288700 (2012-12-15 012448 +0900)l マシンID = 39

l データセンタID = 1l ワーカーID = 7

l 連番 = 011

生成するIDの特徴

l 64bit整数l ユニークl 時間とともにIDの値が増加する

l ステータスIDでざっくり時刻順にソートできる(k-sorted)l 目標精度は1秒l 1秒以内に投稿されたツイート間では順序を保証しない

l 実際の時刻順と逆になることもある

12

生成するIDの特徴 - k-sorted

l 系列α = (a1 an) が k-sorted であるとはl 全ての 1 ≦ i j ≦ n に対して i lt j-k ならば ai ≦ aj

l 概要l 0-sortedは普通のソートと等価l 距離 k 以内の要素間での順序は不問

l 例 (2 1 3)は1-sorted

l k-sortedの性質 (1 2)

l 系列αとkが与えられたときにk-soterdかどうかはO(n)で判定可能l 系列αが与えられたときに最も小さいkの値をO(n)で計算可能l 2つのk-sorted系列が与えられたときにそれらをマージした1つの

k-sorted系列をO(n)で計算可能l 系列αをk-sortedを満たすようにするにはO(n log k)で計算可能

13

a1 ai an ai-k+1

k

(1) Roughly Sorting A Generalization of Sorting(2) Roughly Sorting Sequential and Parallel Approach

生成するIDの特徴 - k-sortedのkの値

l 複数のマシンで完全に同じ時刻を参照できたと仮定するとl 222 -sorted 22bit = 10bit(マシンID)+12bit(連番)

l 精度 1ミリ秒l 232-sorted 32bit = 約10bit(時刻)+10bit(マシンID)+12bit(連番)

l 精度 1秒l 実際にはマシンID連番が疎なので k はそこまで大きくないはず

l 3000 ツイート秒なら連番は3くらいl NTPの精度はミリ秒単位 ()

l 現実的にはこちらの方が精度のネックになりそうl kの値自体にあまり意味はないはず

14

時刻 マシンID 連番

41bit 10bit 12bit

() httpwww2nictgojpaeriststspPubNtpqahtmlq2-2

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

これ

6

本題

7

ツイートID生成システムSnowflakeとは

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

8() httpsgithubcomtwittersnowflake

Snowflakeが開発されるまで

l ツイートの流速増加とツイッターのシステム移行l 5億ツイート日(約6000ツイート秒) (1)

l 2012年10月時点l MySQLからCassandraやGizzard(Sharded MySQL)への移行

l CassandraがID生成をデフォルトで提供していないl ステータスIDの変遷 (2 3 4)

l 2006年5月~ 符号付き32bitl 2009年6月~ 符号無し32bitl 2009年9月~ 64bitl 2010年11月~ 64bit(現状のsnowflake)

l 要求l スケールする(分散できる)

9

(1) Report Twitter hits half a billion tweets a day(2) Twitpocalypse - TwitterメッセージIDの64ビット科- いよいよ明日に実施(3) Status IDs are changing on 21st September(4) Announcing Snowflake

生成するIDの構造

l 64bitを3つのブロックに分割l 時刻(41bit69年分)

l (おそらく)snowflakeの運用開始時刻からの経過時間(ミリ秒)l 2010年11月4日(epoch 1288834974657)が基点

l マシンID(10bit1024台分)l データセンターID(上位5bit)ワーカーID(下位5bit)l 起動時にZookeeperか設定ファイルから取得

l 連番(12bit4096個)l 同時刻同マシンでのID重複回避用ワーカー別

l 参考 バルス砲 25088 ツイート秒10

時刻 マシンID 連番

41bit 10bit 12bit

ツイートIDのデコード(デモ)

l ツイートID = 279622981959970816l 時刻 = 1355502288700 (2012-12-15 012448 +0900)l マシンID = 39

l データセンタID = 1l ワーカーID = 7

l 連番 = 011

生成するIDの特徴

l 64bit整数l ユニークl 時間とともにIDの値が増加する

l ステータスIDでざっくり時刻順にソートできる(k-sorted)l 目標精度は1秒l 1秒以内に投稿されたツイート間では順序を保証しない

l 実際の時刻順と逆になることもある

12

生成するIDの特徴 - k-sorted

l 系列α = (a1 an) が k-sorted であるとはl 全ての 1 ≦ i j ≦ n に対して i lt j-k ならば ai ≦ aj

l 概要l 0-sortedは普通のソートと等価l 距離 k 以内の要素間での順序は不問

l 例 (2 1 3)は1-sorted

l k-sortedの性質 (1 2)

l 系列αとkが与えられたときにk-soterdかどうかはO(n)で判定可能l 系列αが与えられたときに最も小さいkの値をO(n)で計算可能l 2つのk-sorted系列が与えられたときにそれらをマージした1つの

k-sorted系列をO(n)で計算可能l 系列αをk-sortedを満たすようにするにはO(n log k)で計算可能

13

a1 ai an ai-k+1

k

(1) Roughly Sorting A Generalization of Sorting(2) Roughly Sorting Sequential and Parallel Approach

生成するIDの特徴 - k-sortedのkの値

l 複数のマシンで完全に同じ時刻を参照できたと仮定するとl 222 -sorted 22bit = 10bit(マシンID)+12bit(連番)

l 精度 1ミリ秒l 232-sorted 32bit = 約10bit(時刻)+10bit(マシンID)+12bit(連番)

l 精度 1秒l 実際にはマシンID連番が疎なので k はそこまで大きくないはず

l 3000 ツイート秒なら連番は3くらいl NTPの精度はミリ秒単位 ()

l 現実的にはこちらの方が精度のネックになりそうl kの値自体にあまり意味はないはず

14

時刻 マシンID 連番

41bit 10bit 12bit

() httpwww2nictgojpaeriststspPubNtpqahtmlq2-2

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

本題

7

ツイートID生成システムSnowflakeとは

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

8() httpsgithubcomtwittersnowflake

Snowflakeが開発されるまで

l ツイートの流速増加とツイッターのシステム移行l 5億ツイート日(約6000ツイート秒) (1)

l 2012年10月時点l MySQLからCassandraやGizzard(Sharded MySQL)への移行

l CassandraがID生成をデフォルトで提供していないl ステータスIDの変遷 (2 3 4)

l 2006年5月~ 符号付き32bitl 2009年6月~ 符号無し32bitl 2009年9月~ 64bitl 2010年11月~ 64bit(現状のsnowflake)

l 要求l スケールする(分散できる)

9

(1) Report Twitter hits half a billion tweets a day(2) Twitpocalypse - TwitterメッセージIDの64ビット科- いよいよ明日に実施(3) Status IDs are changing on 21st September(4) Announcing Snowflake

生成するIDの構造

l 64bitを3つのブロックに分割l 時刻(41bit69年分)

l (おそらく)snowflakeの運用開始時刻からの経過時間(ミリ秒)l 2010年11月4日(epoch 1288834974657)が基点

l マシンID(10bit1024台分)l データセンターID(上位5bit)ワーカーID(下位5bit)l 起動時にZookeeperか設定ファイルから取得

l 連番(12bit4096個)l 同時刻同マシンでのID重複回避用ワーカー別

l 参考 バルス砲 25088 ツイート秒10

時刻 マシンID 連番

41bit 10bit 12bit

ツイートIDのデコード(デモ)

l ツイートID = 279622981959970816l 時刻 = 1355502288700 (2012-12-15 012448 +0900)l マシンID = 39

l データセンタID = 1l ワーカーID = 7

l 連番 = 011

生成するIDの特徴

l 64bit整数l ユニークl 時間とともにIDの値が増加する

l ステータスIDでざっくり時刻順にソートできる(k-sorted)l 目標精度は1秒l 1秒以内に投稿されたツイート間では順序を保証しない

l 実際の時刻順と逆になることもある

12

生成するIDの特徴 - k-sorted

l 系列α = (a1 an) が k-sorted であるとはl 全ての 1 ≦ i j ≦ n に対して i lt j-k ならば ai ≦ aj

l 概要l 0-sortedは普通のソートと等価l 距離 k 以内の要素間での順序は不問

l 例 (2 1 3)は1-sorted

l k-sortedの性質 (1 2)

l 系列αとkが与えられたときにk-soterdかどうかはO(n)で判定可能l 系列αが与えられたときに最も小さいkの値をO(n)で計算可能l 2つのk-sorted系列が与えられたときにそれらをマージした1つの

k-sorted系列をO(n)で計算可能l 系列αをk-sortedを満たすようにするにはO(n log k)で計算可能

13

a1 ai an ai-k+1

k

(1) Roughly Sorting A Generalization of Sorting(2) Roughly Sorting Sequential and Parallel Approach

生成するIDの特徴 - k-sortedのkの値

l 複数のマシンで完全に同じ時刻を参照できたと仮定するとl 222 -sorted 22bit = 10bit(マシンID)+12bit(連番)

l 精度 1ミリ秒l 232-sorted 32bit = 約10bit(時刻)+10bit(マシンID)+12bit(連番)

l 精度 1秒l 実際にはマシンID連番が疎なので k はそこまで大きくないはず

l 3000 ツイート秒なら連番は3くらいl NTPの精度はミリ秒単位 ()

l 現実的にはこちらの方が精度のネックになりそうl kの値自体にあまり意味はないはず

14

時刻 マシンID 連番

41bit 10bit 12bit

() httpwww2nictgojpaeriststspPubNtpqahtmlq2-2

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

ツイートID生成システムSnowflakeとは

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

8() httpsgithubcomtwittersnowflake

Snowflakeが開発されるまで

l ツイートの流速増加とツイッターのシステム移行l 5億ツイート日(約6000ツイート秒) (1)

l 2012年10月時点l MySQLからCassandraやGizzard(Sharded MySQL)への移行

l CassandraがID生成をデフォルトで提供していないl ステータスIDの変遷 (2 3 4)

l 2006年5月~ 符号付き32bitl 2009年6月~ 符号無し32bitl 2009年9月~ 64bitl 2010年11月~ 64bit(現状のsnowflake)

l 要求l スケールする(分散できる)

9

(1) Report Twitter hits half a billion tweets a day(2) Twitpocalypse - TwitterメッセージIDの64ビット科- いよいよ明日に実施(3) Status IDs are changing on 21st September(4) Announcing Snowflake

生成するIDの構造

l 64bitを3つのブロックに分割l 時刻(41bit69年分)

l (おそらく)snowflakeの運用開始時刻からの経過時間(ミリ秒)l 2010年11月4日(epoch 1288834974657)が基点

l マシンID(10bit1024台分)l データセンターID(上位5bit)ワーカーID(下位5bit)l 起動時にZookeeperか設定ファイルから取得

l 連番(12bit4096個)l 同時刻同マシンでのID重複回避用ワーカー別

l 参考 バルス砲 25088 ツイート秒10

時刻 マシンID 連番

41bit 10bit 12bit

ツイートIDのデコード(デモ)

l ツイートID = 279622981959970816l 時刻 = 1355502288700 (2012-12-15 012448 +0900)l マシンID = 39

l データセンタID = 1l ワーカーID = 7

l 連番 = 011

生成するIDの特徴

l 64bit整数l ユニークl 時間とともにIDの値が増加する

l ステータスIDでざっくり時刻順にソートできる(k-sorted)l 目標精度は1秒l 1秒以内に投稿されたツイート間では順序を保証しない

l 実際の時刻順と逆になることもある

12

生成するIDの特徴 - k-sorted

l 系列α = (a1 an) が k-sorted であるとはl 全ての 1 ≦ i j ≦ n に対して i lt j-k ならば ai ≦ aj

l 概要l 0-sortedは普通のソートと等価l 距離 k 以内の要素間での順序は不問

l 例 (2 1 3)は1-sorted

l k-sortedの性質 (1 2)

l 系列αとkが与えられたときにk-soterdかどうかはO(n)で判定可能l 系列αが与えられたときに最も小さいkの値をO(n)で計算可能l 2つのk-sorted系列が与えられたときにそれらをマージした1つの

k-sorted系列をO(n)で計算可能l 系列αをk-sortedを満たすようにするにはO(n log k)で計算可能

13

a1 ai an ai-k+1

k

(1) Roughly Sorting A Generalization of Sorting(2) Roughly Sorting Sequential and Parallel Approach

生成するIDの特徴 - k-sortedのkの値

l 複数のマシンで完全に同じ時刻を参照できたと仮定するとl 222 -sorted 22bit = 10bit(マシンID)+12bit(連番)

l 精度 1ミリ秒l 232-sorted 32bit = 約10bit(時刻)+10bit(マシンID)+12bit(連番)

l 精度 1秒l 実際にはマシンID連番が疎なので k はそこまで大きくないはず

l 3000 ツイート秒なら連番は3くらいl NTPの精度はミリ秒単位 ()

l 現実的にはこちらの方が精度のネックになりそうl kの値自体にあまり意味はないはず

14

時刻 マシンID 連番

41bit 10bit 12bit

() httpwww2nictgojpaeriststspPubNtpqahtmlq2-2

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

Snowflakeが開発されるまで

l ツイートの流速増加とツイッターのシステム移行l 5億ツイート日(約6000ツイート秒) (1)

l 2012年10月時点l MySQLからCassandraやGizzard(Sharded MySQL)への移行

l CassandraがID生成をデフォルトで提供していないl ステータスIDの変遷 (2 3 4)

l 2006年5月~ 符号付き32bitl 2009年6月~ 符号無し32bitl 2009年9月~ 64bitl 2010年11月~ 64bit(現状のsnowflake)

l 要求l スケールする(分散できる)

9

(1) Report Twitter hits half a billion tweets a day(2) Twitpocalypse - TwitterメッセージIDの64ビット科- いよいよ明日に実施(3) Status IDs are changing on 21st September(4) Announcing Snowflake

生成するIDの構造

l 64bitを3つのブロックに分割l 時刻(41bit69年分)

l (おそらく)snowflakeの運用開始時刻からの経過時間(ミリ秒)l 2010年11月4日(epoch 1288834974657)が基点

l マシンID(10bit1024台分)l データセンターID(上位5bit)ワーカーID(下位5bit)l 起動時にZookeeperか設定ファイルから取得

l 連番(12bit4096個)l 同時刻同マシンでのID重複回避用ワーカー別

l 参考 バルス砲 25088 ツイート秒10

時刻 マシンID 連番

41bit 10bit 12bit

ツイートIDのデコード(デモ)

l ツイートID = 279622981959970816l 時刻 = 1355502288700 (2012-12-15 012448 +0900)l マシンID = 39

l データセンタID = 1l ワーカーID = 7

l 連番 = 011

生成するIDの特徴

l 64bit整数l ユニークl 時間とともにIDの値が増加する

l ステータスIDでざっくり時刻順にソートできる(k-sorted)l 目標精度は1秒l 1秒以内に投稿されたツイート間では順序を保証しない

l 実際の時刻順と逆になることもある

12

生成するIDの特徴 - k-sorted

l 系列α = (a1 an) が k-sorted であるとはl 全ての 1 ≦ i j ≦ n に対して i lt j-k ならば ai ≦ aj

l 概要l 0-sortedは普通のソートと等価l 距離 k 以内の要素間での順序は不問

l 例 (2 1 3)は1-sorted

l k-sortedの性質 (1 2)

l 系列αとkが与えられたときにk-soterdかどうかはO(n)で判定可能l 系列αが与えられたときに最も小さいkの値をO(n)で計算可能l 2つのk-sorted系列が与えられたときにそれらをマージした1つの

k-sorted系列をO(n)で計算可能l 系列αをk-sortedを満たすようにするにはO(n log k)で計算可能

13

a1 ai an ai-k+1

k

(1) Roughly Sorting A Generalization of Sorting(2) Roughly Sorting Sequential and Parallel Approach

生成するIDの特徴 - k-sortedのkの値

l 複数のマシンで完全に同じ時刻を参照できたと仮定するとl 222 -sorted 22bit = 10bit(マシンID)+12bit(連番)

l 精度 1ミリ秒l 232-sorted 32bit = 約10bit(時刻)+10bit(マシンID)+12bit(連番)

l 精度 1秒l 実際にはマシンID連番が疎なので k はそこまで大きくないはず

l 3000 ツイート秒なら連番は3くらいl NTPの精度はミリ秒単位 ()

l 現実的にはこちらの方が精度のネックになりそうl kの値自体にあまり意味はないはず

14

時刻 マシンID 連番

41bit 10bit 12bit

() httpwww2nictgojpaeriststspPubNtpqahtmlq2-2

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

生成するIDの構造

l 64bitを3つのブロックに分割l 時刻(41bit69年分)

l (おそらく)snowflakeの運用開始時刻からの経過時間(ミリ秒)l 2010年11月4日(epoch 1288834974657)が基点

l マシンID(10bit1024台分)l データセンターID(上位5bit)ワーカーID(下位5bit)l 起動時にZookeeperか設定ファイルから取得

l 連番(12bit4096個)l 同時刻同マシンでのID重複回避用ワーカー別

l 参考 バルス砲 25088 ツイート秒10

時刻 マシンID 連番

41bit 10bit 12bit

ツイートIDのデコード(デモ)

l ツイートID = 279622981959970816l 時刻 = 1355502288700 (2012-12-15 012448 +0900)l マシンID = 39

l データセンタID = 1l ワーカーID = 7

l 連番 = 011

生成するIDの特徴

l 64bit整数l ユニークl 時間とともにIDの値が増加する

l ステータスIDでざっくり時刻順にソートできる(k-sorted)l 目標精度は1秒l 1秒以内に投稿されたツイート間では順序を保証しない

l 実際の時刻順と逆になることもある

12

生成するIDの特徴 - k-sorted

l 系列α = (a1 an) が k-sorted であるとはl 全ての 1 ≦ i j ≦ n に対して i lt j-k ならば ai ≦ aj

l 概要l 0-sortedは普通のソートと等価l 距離 k 以内の要素間での順序は不問

l 例 (2 1 3)は1-sorted

l k-sortedの性質 (1 2)

l 系列αとkが与えられたときにk-soterdかどうかはO(n)で判定可能l 系列αが与えられたときに最も小さいkの値をO(n)で計算可能l 2つのk-sorted系列が与えられたときにそれらをマージした1つの

k-sorted系列をO(n)で計算可能l 系列αをk-sortedを満たすようにするにはO(n log k)で計算可能

13

a1 ai an ai-k+1

k

(1) Roughly Sorting A Generalization of Sorting(2) Roughly Sorting Sequential and Parallel Approach

生成するIDの特徴 - k-sortedのkの値

l 複数のマシンで完全に同じ時刻を参照できたと仮定するとl 222 -sorted 22bit = 10bit(マシンID)+12bit(連番)

l 精度 1ミリ秒l 232-sorted 32bit = 約10bit(時刻)+10bit(マシンID)+12bit(連番)

l 精度 1秒l 実際にはマシンID連番が疎なので k はそこまで大きくないはず

l 3000 ツイート秒なら連番は3くらいl NTPの精度はミリ秒単位 ()

l 現実的にはこちらの方が精度のネックになりそうl kの値自体にあまり意味はないはず

14

時刻 マシンID 連番

41bit 10bit 12bit

() httpwww2nictgojpaeriststspPubNtpqahtmlq2-2

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

ツイートIDのデコード(デモ)

l ツイートID = 279622981959970816l 時刻 = 1355502288700 (2012-12-15 012448 +0900)l マシンID = 39

l データセンタID = 1l ワーカーID = 7

l 連番 = 011

生成するIDの特徴

l 64bit整数l ユニークl 時間とともにIDの値が増加する

l ステータスIDでざっくり時刻順にソートできる(k-sorted)l 目標精度は1秒l 1秒以内に投稿されたツイート間では順序を保証しない

l 実際の時刻順と逆になることもある

12

生成するIDの特徴 - k-sorted

l 系列α = (a1 an) が k-sorted であるとはl 全ての 1 ≦ i j ≦ n に対して i lt j-k ならば ai ≦ aj

l 概要l 0-sortedは普通のソートと等価l 距離 k 以内の要素間での順序は不問

l 例 (2 1 3)は1-sorted

l k-sortedの性質 (1 2)

l 系列αとkが与えられたときにk-soterdかどうかはO(n)で判定可能l 系列αが与えられたときに最も小さいkの値をO(n)で計算可能l 2つのk-sorted系列が与えられたときにそれらをマージした1つの

k-sorted系列をO(n)で計算可能l 系列αをk-sortedを満たすようにするにはO(n log k)で計算可能

13

a1 ai an ai-k+1

k

(1) Roughly Sorting A Generalization of Sorting(2) Roughly Sorting Sequential and Parallel Approach

生成するIDの特徴 - k-sortedのkの値

l 複数のマシンで完全に同じ時刻を参照できたと仮定するとl 222 -sorted 22bit = 10bit(マシンID)+12bit(連番)

l 精度 1ミリ秒l 232-sorted 32bit = 約10bit(時刻)+10bit(マシンID)+12bit(連番)

l 精度 1秒l 実際にはマシンID連番が疎なので k はそこまで大きくないはず

l 3000 ツイート秒なら連番は3くらいl NTPの精度はミリ秒単位 ()

l 現実的にはこちらの方が精度のネックになりそうl kの値自体にあまり意味はないはず

14

時刻 マシンID 連番

41bit 10bit 12bit

() httpwww2nictgojpaeriststspPubNtpqahtmlq2-2

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

生成するIDの特徴

l 64bit整数l ユニークl 時間とともにIDの値が増加する

l ステータスIDでざっくり時刻順にソートできる(k-sorted)l 目標精度は1秒l 1秒以内に投稿されたツイート間では順序を保証しない

l 実際の時刻順と逆になることもある

12

生成するIDの特徴 - k-sorted

l 系列α = (a1 an) が k-sorted であるとはl 全ての 1 ≦ i j ≦ n に対して i lt j-k ならば ai ≦ aj

l 概要l 0-sortedは普通のソートと等価l 距離 k 以内の要素間での順序は不問

l 例 (2 1 3)は1-sorted

l k-sortedの性質 (1 2)

l 系列αとkが与えられたときにk-soterdかどうかはO(n)で判定可能l 系列αが与えられたときに最も小さいkの値をO(n)で計算可能l 2つのk-sorted系列が与えられたときにそれらをマージした1つの

k-sorted系列をO(n)で計算可能l 系列αをk-sortedを満たすようにするにはO(n log k)で計算可能

13

a1 ai an ai-k+1

k

(1) Roughly Sorting A Generalization of Sorting(2) Roughly Sorting Sequential and Parallel Approach

生成するIDの特徴 - k-sortedのkの値

l 複数のマシンで完全に同じ時刻を参照できたと仮定するとl 222 -sorted 22bit = 10bit(マシンID)+12bit(連番)

l 精度 1ミリ秒l 232-sorted 32bit = 約10bit(時刻)+10bit(マシンID)+12bit(連番)

l 精度 1秒l 実際にはマシンID連番が疎なので k はそこまで大きくないはず

l 3000 ツイート秒なら連番は3くらいl NTPの精度はミリ秒単位 ()

l 現実的にはこちらの方が精度のネックになりそうl kの値自体にあまり意味はないはず

14

時刻 マシンID 連番

41bit 10bit 12bit

() httpwww2nictgojpaeriststspPubNtpqahtmlq2-2

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

生成するIDの特徴 - k-sorted

l 系列α = (a1 an) が k-sorted であるとはl 全ての 1 ≦ i j ≦ n に対して i lt j-k ならば ai ≦ aj

l 概要l 0-sortedは普通のソートと等価l 距離 k 以内の要素間での順序は不問

l 例 (2 1 3)は1-sorted

l k-sortedの性質 (1 2)

l 系列αとkが与えられたときにk-soterdかどうかはO(n)で判定可能l 系列αが与えられたときに最も小さいkの値をO(n)で計算可能l 2つのk-sorted系列が与えられたときにそれらをマージした1つの

k-sorted系列をO(n)で計算可能l 系列αをk-sortedを満たすようにするにはO(n log k)で計算可能

13

a1 ai an ai-k+1

k

(1) Roughly Sorting A Generalization of Sorting(2) Roughly Sorting Sequential and Parallel Approach

生成するIDの特徴 - k-sortedのkの値

l 複数のマシンで完全に同じ時刻を参照できたと仮定するとl 222 -sorted 22bit = 10bit(マシンID)+12bit(連番)

l 精度 1ミリ秒l 232-sorted 32bit = 約10bit(時刻)+10bit(マシンID)+12bit(連番)

l 精度 1秒l 実際にはマシンID連番が疎なので k はそこまで大きくないはず

l 3000 ツイート秒なら連番は3くらいl NTPの精度はミリ秒単位 ()

l 現実的にはこちらの方が精度のネックになりそうl kの値自体にあまり意味はないはず

14

時刻 マシンID 連番

41bit 10bit 12bit

() httpwww2nictgojpaeriststspPubNtpqahtmlq2-2

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

生成するIDの特徴 - k-sortedのkの値

l 複数のマシンで完全に同じ時刻を参照できたと仮定するとl 222 -sorted 22bit = 10bit(マシンID)+12bit(連番)

l 精度 1ミリ秒l 232-sorted 32bit = 約10bit(時刻)+10bit(マシンID)+12bit(連番)

l 精度 1秒l 実際にはマシンID連番が疎なので k はそこまで大きくないはず

l 3000 ツイート秒なら連番は3くらいl NTPの精度はミリ秒単位 ()

l 現実的にはこちらの方が精度のネックになりそうl kの値自体にあまり意味はないはず

14

時刻 マシンID 連番

41bit 10bit 12bit

() httpwww2nictgojpaeriststspPubNtpqahtmlq2-2

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

その他

l マシンIDや連番の実際の値 ()

l データセンターIDは1l ワーカーIDは0~4l 連番は0~2

l モノトニックタイムl 設定で変更できない単調増加な時刻

l linuxではclock_gettime()などで取得可能l 時刻が巻き戻ると厄介

l IDの一意性を保証するのが面倒になるl snowflakeでは巻き戻りが発生したときはエラー

l 最後にIDを生成した時刻を記憶しておくだけ

15() はてな匿名ダイアリー snowflakeの実際

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

ツイートID生成システムSnowflakeとは(再掲)

l ユニークなIDを生成するネットワークサービスl ツイッターのツイートID(ステータスID)の割り当てに使われているl ツイッター社がOSSで公開中 ()

l 特徴l 64 bitのIDを生成

l ざっくり時刻順l 速い

l 10000 ID秒 のスピードでIDを生成できる(1プロセスあたり)l レスポンス 2 ms (+ネットワークのレイテンシ)

l スケールするl 複数のマシンプロセスで協調動作しないl 並べただけスケールする(はず)

16() httpsgithubcomtwittersnowflake

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

リアルタイム検索システムEarlybirdの概要

l ツイッターリアルタイム検索エンジンl Java製l オープンソースの全文検索ライブラリLuceneを魔改造l 転置インデックスl クエリ言語(Boolean query)

l ANDORNOTl フレーズクエリl ワイルドカードクエリは未対応

l 2010年10月にMySQLベースの検索システムから移行l 出典

l Earlybird Real-Time Search at Twitter ICDE 2012l Michael Busch Krishna Gade Brian Larson Patrick Lok

Samuel Luckenbill and Jimmy Lin17

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

Earlybirdの性能の実績値

l ツイートの登録速度l 3000 ツイート秒 (2012年10月時点で6000 ツイート秒)

l ツイート登録後すぐに検索可能にl ~10秒以内l 検索対象は6~9日以内のツイートのみ

l 検索性能l 低レイテンシ(平均50 ms)

l 高スループット(20億件日 ≒ 2300qps)

18

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

Earlybirdのアーキテクチャ

19

bull クエリのパースbull 複数のEarlybirdへ問い合わせ

bull Userのローカルソーシャルグラフを渡すbull 問い合わせ結果のマージ

bull ツイートのトークナイズbull メタ情報(言語など)を付与

bull 動的更新の通知bull リツイート数の更新bull お気に入りの更新bull

bull ツイートの検索bull ランキング

bull リツイート数bull お気に入り数bull bull Userのローカルソーシャルグラフ

bull 登録先のツイートbull ハッシュでパーティションbull ハッシュの方式は不明

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

Active Index(1個)bull検索(読込)+文書登録(書込)bull更新が速いデータ構造bull一杯になったら裏で最適化bull 1600万件で67GB程度

Earlybirdの構成

20

Earlybird

Optimized Index(11個)bull検索(読込)専用bull 224 ≒ 1600万件インデックスbull圧縮(圧縮率55程度)

bull 1600万件で37GB程度

bull 更新するインデックスを限定bull 1億件台

bull 12インデックス台bull マシンスペック

bull クアッドコア2つbull RAM 72GB

bull 64GBをJVMのヒープに割当

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

辞書の構成(12)

l 辞書l 単語とPosting List(その単語を含む文書IDのリスト)を紐付ける

l 自作ハッシュテーブルで実装l オープンアドレス法をArrayで実装l Java標準のHashMapはGCと相性が悪い

l チェーンで繋いだオブジェクト達の寿命が長いl 辞書に含める情報

l (0) 単語IDl (1) その単語のPosting Listの長さl (2) その単語のPosting Listの末尾へのポインタl それぞれ別々の配列で管理(詳細は次スライド)

l 単語IDを配列のインデックスとしてアクセスするl 速度とメモリ効率を上げるため(Java)

21

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

辞書の構成(22)

22

pfiseminar 0

なう 1

単語ID

(1) Posting Listの長さ

(2) 末尾へのポインタ

4 77

単語

辞書

転置インデックス

単語の数

0 1

4

「pfiseminar」に対応するPosting List

「なう」に対応するPosting List

77

自作ハッシュテーブル

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

Active Index

l 要求l 文書登録(書込)処理が高速 (全サーバで6000ツイート秒)l 検索(読込)処理も並列処理l 時刻降順に検索したい (とにかく最近の情報が重要)

l 特徴l (1) Posting Listは文書ID昇順l (2) Postingは32bit整数l (3) Posting Listのメモリはまとめて確保

l 削除の対応方法は不明l 削除フラグを持ってフィルタリングしているとか

23

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

Active Index - (1) Posting Listは文書ID昇順

l 利点l 文書登録時にはPosting Listの末尾に追加するだけ

l 検索時にはPosting Listの末尾から逆順に辿るだけ

l 欠点l Posting Listの差分圧縮と相性が悪い

l 検索時にPosting Listを逆順に辿れる差分圧縮は複雑‒ ブロックベースのPForDelataとか

l 文書登録のレイテンシが増加l Active IndexでのPosting List圧縮は諦め

24

2 7 11 15pfiseminar pfiseminarなう

文書ID 15

pfiseminar2 7 11 15pfiseminar

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

Active Index - (2) Postingは32bit整数

l 文書ID(24bit)l 1インデックス辺り224(≒ 1600万)件が上限

l 単語位置(8bit)l 140文字なので8bitで十分l 1件にある単語が複数回出現するときは別のPostingとして扱う

l 利点l コンパクトl Posting Listが整数配列になりメモリの事前割り当てが容易

l ブロック単位でまとめて割り当てちゃうl キャッシュにも優しい

25

文書ID単語位置

24bit8bitビットレイアウトは違うかも

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

Active Index - (3) Posting Listのメモリはまとめて

l 4種類のpooll 1poolあたり215 posting(必要に応じて拡張)複数のsliceからなるl sliceのサイズが異なる(21 24 27 211)l sliceを繋げて長いPosting Listを実現

l sliceのサイズが小さい方からslice単位で順に割り当てて行くl sliceの最初の要素は前のsliceの末尾へのポインタ(32bit)

l 文書集合中の単語の分布はジップの法則でモデル化しているl 長いPosting Listが少数短いPosting Listが多数l 工夫しないとメモリ効率が悪く速度が遅くなってしまう

l この実装ではPosting Listの拡張時にメモリコピーが発生しない26

pool 3

pool 2

pool 1

pool 0

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

Active Index - (3) Posting Listのメモリはまとめて

l sliceのポインタl 32bitでpostingと同じサイズ

27

offset in slice slice index 11

11bit 19bit 2bit

pool 3

offset in slice slice index 10

7bit 23bit 2bit

pool 2

offset slice index 01

4bit 26bit 2bit

pool 1

o slice index 00

1bit 29bit 2bit

pool 0

pool index

ビットレイアウトは違うかも

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

Optimized Index

l 要求l 検索(読込)処理のみl 文書登録(書込)処理は受け付けない

l 特徴l Active Indexが一杯(223件)になったら裏でOptimized Indexを構築l Optimized Index構築後スワップ(古いインデックスは削除)l 短いPosting Listは時刻降順にソート

l 検索時には先頭から順方向に辿るl 長いPosting List(長さ1000以上)はブロック単位で圧縮

l PForDeltaやSimple9と似たような感じl Active Indexの55くらいのメモリ使用量

l 1600万件67GBが37GB程度に28

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

Optimized Index - 長いPosting Listの圧縮

l 時刻降順のPosting Listを適当に区切ってブロック単位で圧縮l 固定長ブロック256byteを複数並べたもの

l 先頭4byte ブロックのスキップ用l ブロック先頭の生posting1つ

l 次の4byte ブロックのヘッダ(解凍時に必要)l 圧縮されている文書数 nl 圧縮のビット幅 b = ceil(max(gap)) + ceil(max(pos))

‒ n (ceil(max(gap)) + ceil(max(pos))) lt= 1984(= 2488)

l 残り248byte 圧縮l n個の(文書IDの差分 単語位置)の組を圧縮したもの

29

posting header (文書IDの差分 単語位置)の組n個を圧縮したもの

256byteblock

posting header

4byte 4byte 248byte 4byte 4byte

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

まとめ

l ツイートID生成システムSnowflakeの解説l ツイートID構造と生成方法

l ざっくり時刻順速いスケール

l リアルタイム検索システムEarlybirdの解説l 5億ツイート日(約6000ツイート秒)で増え続けるツイートを即時に検索できるシステム

l アーキテクチャの概要l インデックスの構成

l Active Indexl Optimized Index

30

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved