これは、複数の MySQL サーバーにデータを分割する方法に関する技術的な研究です。このシャーディング アプローチは 2012 年の初めに完了し、現在でもコア データの保存に使用されているシステムです。
データを分割する方法について説明する前に、まずデータを理解しましょう。ムード照明、チョコレートで覆われたイチゴ、スタートレックの引用...
Pinteres は、興味のあるものすべてを発見するエンジンです。データの観点から見ると、Pinterest は人間が興味を持った画像の世界最大のコレクションです。 10 億のボードにわたってピンナーによって保存されたピンは 500 億以上あります。ユーザーは、他の人のピン (大まかに言うと、浅いコピー) と同様に、再度ピンを付け、他のピンナー、ボード、興味をフォローし、ホーム ページ上で登録済みのピンナーの投稿をすべて表示します。とても良い!さあ、スケールアップしてみましょう!
成長の痛み
2011 年、私たちは成功を収めました。いくつかの評価レポートでは、当社は他のスタートアップよりもはるかに速く成長しています。 2011 年 9 月に、当社の基本設備はすべて基準を超えました。私たちは数多くの NoSQL テクノロジーを適用しましたが、そのすべてが悲惨な結果をもたらしました。同時に、読み取りに多数の MySQL スレーブ サーバーが使用されるため、特にキャッシュに関して多くの迷惑なバグが発生しました。データ ストレージ モデル全体を再構築しました。これを効果的に行うために、私たちは要件を慎重に策定します。
ビジネス要件
当社のすべてのシステムは、非常に安定しており、操作が簡単で、拡張が簡単である必要があります。私たちは、サポートされるデータベースが小さなストレージ容量から開始され、ビジネスの発展に応じて拡張できることを期待しています。
ピンで生成されたすべてのコンテンツは、サイト上ですぐにアクセスできる必要があります。
特定の順序 (作成時間に従って、またはユーザー固有の順序など) でアートボードに表示される N ピンへのアクセスのリクエストをサポートします。いいねしたピン友達やピン友達のピンリストを特定の順番で表示することもできます。
わかりやすくするために、通常、更新により最良の結果が保証されます。最終的な整合性を確保するには、分散トランザクション ログなどの追加のものが必要です。これは興味深いものですが、単純ではありません。
ソリューションのアイデアと重要なポイント
このソリューションでは、大量のデータ スライスを複数のデータベース インスタンスに分散する必要があり、接続、外部キー、またはリレーショナル データベースの接続を使用できません。などの方法でデータ全体を統合します。よく考えてみると、相関サブクエリは異なるデータベース インスタンスにまたがることはできません。
私たちのソリューションにはデータ アクセスの負荷分散が必要です。私たちはデータ移行、特にレコードごとの移行を嫌います。これは非常にエラーが発生しやすく、関係の複雑さによってシステムに不必要な複雑さが追加されます。データを移行する必要がある場合は、論理ノードのセット全体を移行するのが最善です。
ソリューションの信頼性と迅速な実装を実現するには、分散データ プラットフォーム上で実装が最も簡単で堅牢な技術ソリューションを使用する必要があります。
各インスタンス上のすべてのデータは、データ バックアップとしてスレーブ インスタンスに完全にコピーされます。可用性の高い MapReduce (分散コンピューティング環境) である S3 を使用しています。フロントエンド ビジネス ロジックはバックグラウンド データにアクセスし、データベースのメイン インスタンスにのみアクセスします。フロントエンド ビジネスにスレーブ インスタンスへの読み取りおよび書き込みアクセスを決して与えないでください。マスター インスタンスとのデータ同期に遅延があるため、不可解なエラーが発生しますが、データがスライスされて分散されれば、フロントエンド ビジネスがスレーブ インスタンスからデータを読み書きする必要はなくなります。
最後に、すべてのデータ オブジェクトのグローバル一意識別子 (UUID) を生成および解析するための優れたソリューションを慎重に設計する必要があります。
当社のスライシング ソリューション
何はともあれ、ニーズを満たし、堅牢で、優れたパフォーマンスを備え、保守しやすいデータ分散ソリューションを設計する必要があります。言い換えれば、(広範な検証がなければ)ナイーブであることはできません。したがって、私たちの基本設計は MySQL に基づいて構築されています。成熟したテクノロジーを選択したことを見てください。設計の開始時には、MongoDB、Cassandra、Membase などの自動スケーリングの新機能を主張するデータベース製品は当然敬遠されます。これらの製品は、実装は簡単そうに見えますが、適用性が低いためです (多くの場合、説明できないエラーが発生し、クラッシュが発生しました)。
ナレーション: 基本から始めて、流行のものや新しいものは避け、現実的な方法で MySQL を学び、使用することを強くお勧めします。信じてください、すべての言葉に涙があふれます。
MySQL は、成熟した安定した使いやすいリレーショナル データベース製品です。当社が使用しているだけでなく、多くの有名な大企業も、大量のデータを保存するバックエンド データ サポートとして使用しています。 (注: 数年ほど前、MySQL は SUN とともに Oracle に買収され、Oracle という名前になりました。Google、Facebook などの多くの企業が MySQL のオープンソースの問題を懸念し、別の開発されたものに切り替えました) MySQL のオリジナルの作成者によるオープン ソース データベース MariaDB (MySQL の下) は、データベースへのシーケンシャル データ リクエスト、指定された範囲のデータのクエリ、および行 (レコード) レベルでのトランザクション処理に対する技術要件をサポートしています。 MySQL には多くの機能がありますが、それらは必要ありません。 MySQL 自体はモノリシック ソリューションであるため、データをスライスする必要があります。 (注釈: ここでの意味は、単一のインスタンスが大量のデータを管理するため、必然的にパフォーマンスの問題が発生するということです。現在、大量のデータ全体を個々のデータセットにスライスするには、個々のデータセットを分離するための強力な技術的ソリューションが必要です。モノリスは統合されています。問題を起こさずにパフォーマンスを向上させます) 以下は私たちの設計計画です:
8 台の EC2 サーバーで開始し、各サーバーは MySQL インスタンスを実行します:
各 MySQL サーバーは、災害復旧のために冗長ホストに複製されたマスター-マスターを使用します。当社のフロントエンド ビジネスは、メイン サービス インスタンスからのデータの読み取り/書き込みのみを行います。同じようにすることをお勧めします。これにより、多くのことが簡素化され、遅延による不具合が回避されます。 (注: マスター-マスター複製とは、MySQL データベース自体が提供する機能です。2 台のマシンが相互にバックアップするモードを指します。マスター-スレーブ バックアップなどの他のモードと比較して、2 台のマシンのデータは完全にバックアップされます)バックグラウンド同期、各マシンは独自の独立した IP を持ち、読み取り/書き込みアクセスのために同時にアクセスできます。ただし、元の記事の著者は、2 台のマシンは相互に冗長であり、プライマリ - プライマリ バックアップを使用しているにもかかわらず、両方ともただし、論理的にはマスターとスレーブを区別し、常にどちらか一方から読み取り/書き込みを行う必要があります。たとえば、図に示すように、MySQL001A と MySQL001B の間にはマスター間バックアップがありますが、読み取り/書き込みのみが可能です。 MySQL001A からのアクセス。別の例: 彼らは 16 台のマシンを使用し、他の 8 台のスレーブ マシンは EC2 かどうか)
各 MySQL インスタンスは複数のデータベースを持つことができます:
各データベースには、db00000、db00001、dbNNNN までの一意の名前を付ける方法があることに注意してください。各データベースはデータベースのシャードです。一度シャードにデータを配置すると、そのシャードからデータが移動されないように設計しました。ただし、シャードを他のマシンに移動することで、より多くの容量を得ることができます (これについては後で説明します)。
当社では、スライス データベースがどのマシン上にあるかを記録する構成データベース テーブルを維持しています。
[ {“range”: (0,511), “master”: “MySQL001A”, “slave”: “MySQL001B”}, {“range”: (512, 1023), “master”: “MySQL002A”, “slave”: “MySQL002B”}, ... {“range”: (3584, 4095), “master”: “MySQL008A”, “slave”: “MySQL008B”} ]
この構成テーブルは、スライス データベースが移行されるか、ホストが置き換えられる場合にのみ変更されます。たとえば、マスター インスタンス ホストがダウンした場合、そのスレーブ インスタンス ホストをマスター インスタンスに昇格させ、できるだけ早くスレーブ インスタンス ホストとして新しいマシンに置き換えます。構成スクリプトは ZooKeeper に保持されており、上記の変更が行われると、スクリプトは構成変更のためにスライシング サービスを維持するマシンに送信されます。 (注釈: フロントエンド ビジネスは論理マスター インスタンスからのデータの読み取りと書き込みのみを行うことを、原著者が常に強調してきた利点がわかります)。
各スライス データベースは、ピン、ボード、users_has_pins、users_likes_pins、pin_liked_by_user、その他のデータベース テーブルなど、同じデータベース テーブルとテーブル構造を維持します。デプロイメント時に同期的にビルドします。
データをスライス サーバーに分散するための設計計画
スライス ID (シャード ID)、データ型識別子、およびローカル ID (ローカル ID) を組み合わせて 64 -bit グローバルに一意の識別 (ID)。スライスID(シャードID)は16ビット(bit)、データ型識別子は10ビット(bit)、ローカルID(ローカルID)は36ビット(bit)を占めます。目の肥えた人なら、これがわずか 62 ビットであることにすぐに気づくでしょう。データの分散と統合に関する私の過去の経験から、拡張のためにいくつかのデータを保持しておくのは非常に貴重であることがわかります。したがって、2 ビットを保持 (0 に設定) しました。 (注釈: ここで説明します。以下の操作と説明によると、オブジェクトの一意の識別 ID は 64 ビットで、最上位 2 ビットは常に 0 で、その後に 36 ビットのローカル ID、その後に 10 ビットのローカル ID が続きます。タイプ識別、そして最後に 16 ビットのスライス識別子。ローカル識別子は 2^36 から最大 600 億を超える ID を表現できます。データ型は 2^10 から最大 1024 のオブジェクト タイプを表現でき、スライス識別子はさらに細分化できます。 2^16 から最大 65536 のスライス データベースに分割します。上記の解決策では 4096 スライス データベースをカットします)
ID = (shard ID << 46) | (type ID << 36) | (local ID<<0) 以 Pin: https://www.pinterest.com/pin/241294492511... 为例,让我们解构这个 Pin 对象的 全局 ID 标识 241294492511762325 : Shard ID = (241294492511762325 >> 46) & 0xFFFF = 3429 Type ID = (241294492511762325 >> 36) & 0x3FF = 1 Local ID = (241294492511762325 >> 0) & 0xFFFFFFFFF = 7075733
この Pin オブジェクトは 3429 スライス データベースにあることがわかります。 Pin オブジェクトのデータ型識別子が 1 で、そのレコードが 3429 スライス データベースのピン データ テーブルの 7075733 レコード行にあると仮定します。たとえば、スライス 3429 データベースが MySQL012A にあると仮定すると、次のステートメントを使用してそのデータ レコードを取得できます: (注釈: ここでは元の作成者が一般的な例を挙げています。前の例によれば、3429 は MySQL007A にあるはずです)。
conn = MySQLdb.connect(host=”MySQL012A”) conn.execute(“SELECT data FROM db03429.pins where local_id=7075733”)
有两种类型的数据:对象或关系。对象包含对象本身细节。 如 Pin 。
存储对象的数据库表
对象库表中的每个记录,表示我们前端业务中的一个对象,诸如:Pins(钉便签), users(用户),boards(白板)和 comments(注释),每个这样的记录在数据库表中设计一个标识 ID 字段(这个字段在表中作为记录的 自增主键「auto-incrementing primary key」 ,也就是我们前面提到的 局部 ID「 local ID」 ),和一个 blob 数据字段 -- 使用 JSON 保存对象的具体数据 --。
CREATE TABLE pins ( local_id INT PRIMARY KEY AUTO_INCREMENT, data TEXT, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB;
举例,一个 Pin 对象形状如下:
{“details”: “New Star Wars character”, “link”: “http://webpage.com/asdf”, “user_id”: 241294629943640797, “board_id”: 241294561224164665, …}
创建一个 Pin 对象,收集所有的数据构成 JSON blob 数据。然后,确定它的 切片 ID「 shard ID」 (我们更乐意把 Pin 对象的切片数据放到跟其所在 白板「 board」 对象相同的切片数据库里,这不是强制设计规则)。Pin 对象的数据类型标识为 1。连接到 切片 ID 指示的切片数据库,插入(insert)Pin 对象的 JOSON 数据到 Pin 对象数据库表中,MySQL 操作成功后将会返回 自增主键「auto-incrementing primary key」 给你,这个作为此 Pin 对象的 局部 ID「 local ID」。现在,我们有了 shard 、类型值、local ID 这些必要信息,就可以构建出此 Pin 对象的 64 位 ID 。(译注:原作者提到的,他们的前端业务所用到的每种对象都保存在一个对象数据库表里,每个对象记录都通过一个全局唯一 ID 去找到它,但这个全局唯一 ID 并不是数据库表中的 局部 ID,由于切片的缘故。原作者一直在讲这个设计及其原理。这样设计的目的为了海量数据切片提高性能,还要易用,可维护,可扩展。后面,作者会依次讲解到)
编辑一个 Pin 对象,使用 MySQL 事务「transaction」 在 Pin 对象的数据记录上 读出 -- 修改 -- 写回「read-modify-write」 Pin 对象的 JOSON 数据字段:
> BEGIN > SELECT blob FROM db03429.pins WHERE local_id=7075733 FOR UPDATE [修改 json blob] > UPDATE db03429.pins SET blob=’<修改后的 blob>’ WHERE local_id=7075733 > COMMIT
编辑一个 Pin 对象,您当然可以直接删除这个对象在 MySQL 数据库表中的数据记录。但是,请仔细想一下,是否在对象的 JSON 数据上加个叫做「 active」的域,把剔除工作交由前端中间业务逻辑去处理或许会更好呢。
(译注:学过关系数据库的应知道,自增主键在记录表中是固实,在里面删除记录,会造成孔洞。当多了,势必造成数据库性能下降。数据库只负责保存数据和高性能地查询、读写数据,其数据间的关系完全靠设计精良的对象全局 ID 通过中间件逻辑去维护 这样的设计理念一直贯穿在作者的行文中。只有理解了这点您才能抓住这篇文章的核心)
关系映射数据库表
关系映射表表示的是前端业务对象间的关系。诸如:一个白板(board)上有哪些钉便签(Pin), 一个钉便签(Pin)在哪些白板(board)上等等。表示这种关系的 MySQL 数据库表包括 3 个字段:一个 64 位的「from」ID, 一个 64 位的「to」ID 和一个顺序号。每个字段上都做索引方便快速查询。其记录保存在根据「from」字段 ID 解构出来的切片 ID 指示出的切片数据库上。
CREATE TABLE board_has_pins ( board_id INT, pin_id INT, sequence INT, INDEX(board_id, pin_id, sequence) ) ENGINE=InnoDB;
(译注:这里的关系映射指前端业务对象间的关系用数据库表来运维,并不指我上节注释中说到的关系数据库的关系映射。作者开篇就讲到,由于切片,不能做关系数据库表间的关系映射的,如一对一,一对多,多对多等关系关联)
关系映射表是单向的,如 board_has_pins(板含便签)表方便根据 board (白板)ID 查询其上有多少 Pin(钉便签)。若您需要根据 Pin(钉便签)ID 查询其都在哪些 board(白板)上,您可另建个表 pin_owned_by_board(便签属于哪些白板)表,其中 sequence 字段表示 Pin 在 board 上的顺序号。(由于数据分布在切片数据库上,我们的 ID 本身无法表示其顺序)我们通常将一个新的 Pin 对象加到 board 上时,将其 sequence 设为当时的系统时间。sequence 可被设为任意整数,设为当时的系统时间,保证新建的对象的 sequence 总是大于旧对象的。这是个方便易行的方法。您可通过下面的语句从关系映射表中查询对象数据集:
SELECT pin_id FROM board_has_pins WHERE board_id=241294561224164665 ORDER BY sequence LIMIT 50 OFFSET 150
语句会查出 50 个 pin_ids(便签 ID ), 随后可用这些对象 ID 查询其具体信息。
これらの関係は、board_id -> pin_ids -> pin オブジェクト (ホワイトボード ID -> Note ID -> Note オブジェクトから) など、ビジネス アプリケーション層でのみマッピングされます。この設計の優れた特徴は、これらのリレーショナル マップのペアを個別にキャッシュできることです。たとえば、pin_id -> pin オブジェクト (ノート ID -> ノート オブジェクト) の関係マッピングを memcache (メモリ キャッシュ) クラスター サーバーにキャッシュし、board_id -> pin_ids (ホワイトボード ID -> ノート ID) の関係をキャッシュします。クラスターサーバー上の Redis 上のマッピングキャッシュ。このように、これは当社の最適化キャッシュ技術戦略に非常に適しています。
サービス能力の向上
当社のシステムでは、サービス処理能力を向上させる主な方法が 3 つあります。最も簡単な方法は、マシンをアップグレードすることです (より大きなスペース、より高速なハードディスク速度、より多くのメモリ、システムのボトルネックを解決できるアップグレードであれば何でも)
もう 1 つの方法は、スライス範囲を拡大することです。当初は 4096 個のデータベースをスライスすることのみを設計していましたが、設計した 16 ビットのスライス ID と比較すると、16 ビットで 65536 個の数値を表現できるため、まだ多くのスペースがあります。ある時点で、8 つの MySQL データベース インスタンスを実行するためにさらに 8 台のマシンを用意し、4096 から 8192 までのスライス データベースを提供すると、新しいデータはこの範囲のスライス データベースにのみ保存されます。並列計算データベースは16個あり、サービス能力は必然的に向上します。
最後の方法は、スライシング データベース ホストを新しいスライシング ホスト (ローカル スライシング拡張) に移行して機能を向上させることです。たとえば、前の例の MySQL001A スライシング ホスト (0 ~ 511 の番号が付けられたスライシング データベースがある) を 2 つのスライシング ホストに拡張して分散したいとします。設計どおり、新しいマスターとマスターの相互バックアップ ホストのペアを新しいスライス ホスト (MySQL009A および B という名前) として作成し、MySQL001A からデータを完全にコピーします。
データのコピーが完了したら、スライス構成を変更します。MySQL001A は 0 から 255 までのスライス データベースのみを担当し、MySQL009A はスライス データベースのみを担当します。 256から511まで。現在、2 つのホストはそれぞれ、以前のホストが担当していたタスクの半分だけを担当するようになり、サービス機能が向上しました。
いくつかの機能の説明
古いシステムによって生成されたビジネス オブジェクト データの場合は、以下に従ってビジネス オブジェクト用にデータを生成する必要があります。新しいシステムの UUID では、UUID がどこに行くか (どのタイル データベースか) 決定するのはユーザー次第であることを認識する必要があります。 (注釈: スライス データベース上で古いデータの分散を計画できます) ただし、スライス データベースに配置されると、レコードを挿入する場合にのみ、データベースは挿入されたオブジェクトのローカル ID を返します。これにより、UUID が返されます。オブジェクトの構築が可能です。
(注釈: 移行するときは、UUID を介したビジネス オブジェクト間の関係の確立を考慮する必要があります)
すでに大量のデータがあるデータベース テーブルについては、次のテーブル構造を使用しました。変更コマンド (ALTER) -- フィールドの追加と同様に、非常に長くて面倒なプロセスであることはご存知でしょう。私たちの設計では、(データがすでに利用可能な場合には) MySQL 上で ALTERs レベルのコマンドを決して使用しないようにしています。私たちのビジネス システムである Pinterest で、最後に ALTER ステートメントを使用したのは約 3 年前です。オブジェクト テーブル内のオブジェクトについて、オブジェクト属性フィールドを追加する必要がある場合は、それをオブジェクト データの JOSON BLOB フィールドに追加します。新しいオブジェクトのプロパティのデフォルト値を設定できます。古いオブジェクトのデータにアクセスするときに、古いオブジェクトに新しいプロパティがない場合は、新しいプロパティのデフォルト値をオブジェクトに追加できます。リレーショナル マッピング テーブルの場合は、ニーズを満たす新しいリレーショナル マッピング テーブルを作成するだけです。あなたはこれをすべて知っています!システムを離陸させましょう!
MOD データベースのスライス
MOD シャードの名前は Mod Squad と似ていますが、実際にはまったく異なります。
一部のビジネス オブジェクトは、非 ID (非 ID) メソッドを通じてクエリおよびアクセスする必要があります。 (翻訳: この ID は、前の設計説明の 64 ビット UUID を指します) たとえば、ピナーが Facebook 登録アカウントを使用して当社のビジネス プラットフォームに登録し、ログインしたとします。彼らの Facebook ID をピナーの ID にマッピングする必要があります。私たちのシステムでは、Facebook ID は単なる 2 進数の文字列です。 (注釈: これは、システム プラットフォームの設計のように他のプラットフォームの ID を分解できないことを意味します。また、スライスの設計方法について話すこともできません。スライスを保存して、ID とマッピングするように設計するだけです。) したがって、次のことを行う必要があります。それらを保存する場合は、それぞれスライス データベースにも保存する必要があります。これを MOD シャードと呼びます。他の例には、IP アドレス、ユーザー名、ユーザーの電子メールが含まれます。
模转数据切片(mod shard)类似前述我们业务系统的数据切片设计。但是,你需要按照其输入的原样进行查询。如何确定其切片位置,需要用到哈希和模数运算。哈希函数将任意字串转换成定长数值,而模数设为系统已有切片数据库的切片数量,取模后,其必然落在某个切片数据库上。结果是其数据将保存在已有切片数据库上。举例:
shard = md5(“1.2.3.4") % 4096
(译注:mod shard 这个词,我网上找遍了,试图找到一个较准确权威的中文翻译!无果,因为 mod 这个词有几种意思,最近的是 module 模块、模组,同时它也是模运算符(%)。我根据原文意思,翻译为 模转 。或可翻译为 模式,但个人感觉意思模糊。不当之处,请指正。另,原作者举的例子是以 IP 地址举例的,哈希使用的是 md5,相比其它,虽老但性能最好)
在这个例子中分片是 1524。 我们维护一个类似于 ID 分片的配置文件:
[{“range”: (0, 511), “master”: “msdb001a”, “slave”: “msdb001b”}, {“range”: (512, 1023), “master”: “msdb002a”, “slave”: “msdb002b”}, {“range”: (1024, 1535), “master”: “msdb003a”, “slave”: “msdb003b”}, …]
因此,为了找到 IP 为 1.2.3.4 的数据,我们将这样做:
conn = MySQLdb.connect(host=”msdb003a”) conn.execute(“SELECT data FROM msdb001a.ip_data WHERE ip='1.2.3.4'”)
你失去了一些分片好的属性,例如空间位置。你必须从一开始就设置分片的密钥(它不会为你制作密钥)。最好使用不变的 id 来表示系统中的对象。这样,当用户更改其用户名时,您就不必更新许多引用。
最后的提醒
这个系统作为 Pinterest 的数据支撑已良好运行了 3.5 年,现在看来还会继续运行下去。设计实现这样的系统是直观、容易的。但是让它运行起来,尤其迁移旧数据却太不易了。若您的业务平台面临着急速增长的痛苦且您想切片自己的数据库。建议您考虑建立一个后端集群服务器(优先建议 pyres)脚本化您迁移旧数据到切片数据库的逻辑,自动化处理。我保证无论您想得多周到,多努力,您一定会丢数据或丢失数据之间的关联。我真的恨死隐藏在复杂数据关系中的那些捣蛋鬼。因此,您需要不断地迁移,修正,再迁移... 你需要极大的耐心和努力。直到您确定您不再需要为了旧数据迁移而往您的切片数据库中再操作数据为止。
这个系统的设计为了数据的分布切片,已尽最大的努力做到最好。它本身不能提供给你数据库事务 ACID 四要素中的 Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)哇呕!听起来很坏呀,不用担心。您可能不能利用数据库本身提供的功能很好地保证这些。但是,我提醒您,一切尽在您的掌握中,您只是让它运行起来,满足您的需要就好。设计简单直接就是王道,(译注:也许需要您做许多底层工作,但一切都在您的控制之中)主要是它运行起来超快! 如果您担心 A(原子性)、I(隔离性)和 C(一致性),写信给我,我有成堆的经验让您克服这些问题。
还有最后的问题,如何灾难恢复,啊哈? 我们创建另外的服务去维护着切片数据库,我们保存切片配置在 ZooKeeper 上。当单点主服务器宕掉时,我们有脚本自动地提升主服务器对应的从服务器立即顶上。之后,以最快的速度运行新机器顶上从服务器的缺。直至今日,我们从未使用过类似自动灾难恢复的服务。
推荐教程:《MySQL教程》
以上がMySQL がシャーディングを使用して 500 億のデータのストレージ問題を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。