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

31
ツイートID生成と ツイッターリアルタイム検索 システムの話 PFIセミナー Eiichiro Iwata 20121220

Upload: preferred-infrastructure-preferred-networks

Post on 14-Dec-2014

14.873 views

Category:

Technology


0 download

DESCRIPTION

 

TRANSCRIPT

Page 1: ツイート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

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

自己紹介

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

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

本日の内容

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

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

突然ですが

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

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

ツイッター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

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

これ

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

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

本題

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

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

ツイート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

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

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

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

生成する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

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

ツイート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

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

生成する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

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

生成する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

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

生成する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

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

その他

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

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

ツイート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

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

リアルタイム検索システム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

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

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

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

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

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

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

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

辞書の構成(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

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

辞書の構成(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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

まとめ

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

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

Copyright copy 2006-2012

Preferred Infrastructure All Right Reserved