データベース101:ソーシャルメディアの「いいね」がデータベースにどう保存されているか

インスタグラム、ツイッター、フェイスブックやその他のソーシャルメディアプラットフォームは、どのようにして自分の投稿に対する「いいね」を追跡しているか、考えたことありますか?この投稿で解き明かしましょう!

もしデータベースについてこれから学び始めるなら、私が以前のSQLやMySQLの経験をはるかに超え、多くのデータベースのパラダイムが存在することを探求する私の初めの投稿、データベース101:ビギナーのためのデータ整合性から読んでみるのが良いスタートです。データベース101シリーズで私の学習を追跡しています。

目次

1. プロローグ

最近、「CityJS」というイベントで話すよう招待されました。でも、僕はPHPの人。JSは一切やりませんが、挑戦を受け入れました。それを成し遂げるには、高いスケーラビリティと低レイテンシを持つデータベースがどのように機能するかを示す良い例を見つける必要がありました。

そこで、同僚に例を尋ねました。彼はどのプラットフォームでも良いから、カウンターのように大きな数字を探すようにと言いました。その時に、任意のメトリクスがこの例に適合することに気づきました。いいね、閲覧数、コメント、フォローなど、カウンターとしてクエリできます。この記事では、それらに適したデータモデリングをどう行うかについての私の研究を見つけることができます。

2. 研究しよう

一番最初に決めることが大事ですよね?プレゼンテーションで何をカバーするか決めた後、データモデルの構築方法を理解する必要がありました。

posts テーブルとそれぞれの投稿にいいねした人を関連付ける post_likes テーブルが必要となるでしょう。今のところは、いいねのカウンターを処理するには十分のようです。

いいねの合計を数える最初のクエリのセットはこんな感じでした:

もし SELECT count(*) FROM social.post_likes でクエリするだけなら、それでうまくいきますよね?

まあ、それも動きましたが、何千ものいいねを持つ投稿でテストしたとき、予想したほどパフォーマンスが良くありませんでした。いいねの数が増えるにつれて、クエリもどんどん遅くなります...

「でも、ScyllaDBなら簡単に何千もの行を扱えるはず…なぜパフォーマンスが良くないのでしょうか?」と今思っているかもしれませんね(そうでなければ忘れてください)。

ScyllaDBには魅力的な機能がたくさんありますが、悪いデータモデリングの問題は解決しません。もっと速くする方法を考える必要があります。

3. データ型を研究する

さて、データは保存される必要があり、投稿にいいねした人との関連性が必要ですが、カウントには使えません。では、posts テーブルに新しい行を integer として作成し、いいねがあるたびに増減させたらどうでしょうか?

うん、これは良いアイディアに思えますが、問題が一つあります:posts テーブルの変更の追跡が必要で、そこにデータをINSERTまたはUPDATEし始めると、データベースに無意味なレコードがいくつもできるでしょう。

ScyllaDBを使うと、何かを更新する必要があるたびに、実際には新しいデータを作成しています。

scylla@cqlsh:socials> INSERT INTO socials.posts (id, user_id, description, image_url, created_at, likes) VALUES (4d18bb8c-9c57-44fe-827a-4a2d65f331e5, 3edd5f1d-67e9-4a3e-af1a-9adbb41e2129, 'Such a cool event P99 Conf!', 'https://i.imgur.com/Xp8gi7t.jpg', '2023-04-23 15:02:49', 1);

scylla@cqlsh:socials> INSERT INTO socials.posts (id, user_id, description, image_url, created_at, likes) VALUES (4d18bb8c-9c57-44fe-827a-4a2d65f331e5, 3edd5f1d-67e9-4a3e-af1a-9adbb41e2129, 'Such a cool event P99 Conf!', 'https://i.imgur.com/Xp8gi7t.jpg', '2023-04-23 15:02:50', 2);

scylla@cqlsh:socials> INSERT INTO socials.posts (id, user_id, description, image_url, created_at, likes) VALUES (4d18bb8c-9c57-44fe-827a-4a2d65f331e5, 3edd5f1d-67e9-4a3e-af1a-9adbb41e2129, 'Such a cool event P99 Conf!', 'https://i.imgur.com/Xp8gi7t.jpg', '2023-04-23 15:02:51', 3);

それから、

scylla@cqlsh:socials> SELECT * from posts;

 id                                 | user_id                           | created_at                    | description               | image_url                     | likes
--------------------------------------+--------------------------------------+---------------------------------+-----------------------------+---------------------------------+-------
 4d18bb8c-9c57-44fe-827a-4a2d65f331e5 | 3edd5f1d-67e9-4a3e-af1a-9adbb41e2129 | 2023-04-23 15:02:48.000000+0000 | Such a cool event P99 Conf! | https://i.imgur.com/Xp8gi7t.jpg |    1
 4d18bb8c-9c57-44fe-827a-4a2d65f331e5 | 3edd5f1d-67e9-4a3e-af1a-9adbb41e2129 | 2023-04-23 15:02:50.000000+0000 | Such a cool event P99 Conf! | https://i.imgur.com/Xp8gi7t.jpg |    2
 4d18bb8c-9c57-44fe-827a-4a2d65f331e5 | 3edd5f1d-67e9-4a3e-af1a-9adbb41e2129 | 2023-04-23 15:02:51.000000+0000 | Such a cool event P99 Conf! | https://i.imgur.com/Xp8gi7t.jpg |    3

あなたのデータの変更を追跡する必要があります。なので、増加ごとに新しい行ができることになります。クラスタリングキーを変更しない限り、またはタイムスタンプを気にしない限りです(すごく愚かなアイディアですが)。

それから、ScyllaDBのドキュメントを見つけて、counter という我々のニーズに合うタイプがあり、それがアトミック であることも発見しました!

OK、我々のニーズに合っていますが、データモデリングに合っていません。このタイプを使うにはいくつかのルールに従う必要がありますが、今は我々にとって問題を引き起こしているルールにフォーカスしましょう:

  • カウンターの列が含まれるテーブルの他の列は、プライマリキーの列でなければならず(これは更新できません)。
  • 他の種類の列を含めることはできません。
  • カウンターデータ型があるテーブルを扱うにはUPDATEクエリを使う必要があります。
  • 値を増やしたり減らしたりすることはできますが、特定の値を設定することはできません。

この制限は、カウンターと非カウンターの更新を同じ操作で行わせることによる正しい処理を保証するためです。

なので、カウンターは posts テーブルには使えない... OKですね、私たちは解決策を見つけているようです。

4. きちんとモデリングする

counter タイプは他のデータ型と「混ぜてはいけない」という情報をもとに、残されたオプションは、このタイプのデータを格納するために**新しいテーブルを作成することです。

なので、post_analytics という新しいテーブルを作成しました。このテーブルは counter タイプだけを持つことになります。今のところ、既に作成された 多対多 関係(post_likes)にのみ取り組むので、いいねだけを扱います。

この例でおそらく実行する次のクエリーはこのようなものです:

## 投稿を「いいね」したとき

UPDATE socials.post_analytics SET likes = likes + 1 WHERE post_id = 4d18bb8c-9c57-44fe-827a-4a2d65f331e5;

INSERT INTO socials.post_likes (post_id, user_id, liked_at) VALUES (4d18bb8c-9c57-44fe-827a-4a2d65f331e5, 3edd5f1d-67e9-4a3e-af1a-9adbb41e2129, '2023-04-23 15:02:50');

# 投稿の「いいね」を取り消す時

DELETE FROM socials.post_likes WHERE post_id = 4d18bb8c-9c57-44fe-827a-4a2d65f331e5 AND user_id = 3edd5f1d-67e9-4a3e-af1a-9adbb41e2129;

UPDATE socials.post_analytics SET likes = likes - 1 WHERE post_id = 4d18bb8c-9c57-44fe-827a-4a2d65f331e5;

今、新たな未解決の疑問が頭に浮かんでいるかもしれませんね。"だから、私のデータに関連する新しいカウンターが必要なたびに新しいテーブルが必要ですか?" ええと、それはあなたのケース次第です。ソーシャルメディアの場合、投稿を見た人を保存したいなら、おそらくsession_idやその他のものを持つ post_viewers テーブルが必要になるでしょう。

joins せずに単純なクエリを実行できるようにすることは、count(*) クエリよりも遥かに速くなる可能性があります。

5. 最終的な考察

CityJSステージでたくさんのナンセンスなデータモデリングを話している私

新しいデータモデリングの方法だけでなく、CityJSのプレゼンテーションを作成してこのユースケースを構築するためにTypeScriptを学ぶことによって、たくさんのことを学びました。

すべてが私にとって真新しいので、学んでいることを共有し続けることを最善を尽くします。コメントで私を正すことを躊躇しないでくださいね!議論は新しいことを学ぶ最善の方法です。

この投稿を「いいね」して、ソーシャルで私をフォローして、水筒を満たしてくださいね(笑)

Twitter DanielHe4rt PT-BR
Twitter DanielHe4rt EN
Twitchチャンネル

こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。
https://dev.to/danielhe4rt/database-101-how-social-media-likes-are-stored-in-a-database-3oii