データベースのIDを64ビットのハッシュ値にする

f:id:monex_engineer:20200330091044j:plain


こんにちは。マネックス・ラボの戸谷です。
今回は本番環境と検証環境など異なる環境での データの持ち方 に関してお話したいと思います。

本番環境と検証環境のデータについて

商用で公開している本番環境と、開発したものをテストする検証環境について、それぞれが利用しているデータは物理的に異なるように構成されていると思います。

しかし、たまにある要求として、あるデータを特定するための一意のキーを本番環境と検証環境で合わせたい、ということがあります。

上記のケースのように、異なる環境でデータのキーを同一にしたい場合、UUID を用いるなど、16進数に似た文字列で管理しがちになります。

パフォーマンスを気にしないのであれば、それでも良いのですが、使用しているデータベースが RDB で、大量なデータを検索する場合には、この記事でご紹介する方法が役に立つかもしれません。

IDを連番にしたくない

この記事で生成される ID は unsigned の数値に変換可能なため、リレーショナル・データベースなどで検索する際に、文字列よりも高速に検索することが可能で、環境ごとに同じIDを発行することが可能になります。

ID は数値型 (int, bigint) でシーケンシャルな形式で定義されることが多いと思いますが、数値型で連番で振られていく方式だと、環境ごとに、同じデータでも、異なるIDが振られてしまい、異なるシステム間で同じデータを管理する場合に制約となってしまうことがあります。

そのため、レコードごとに、ユニークとなるカラムをキーに、ハッシュ値を生成し、それをIDとする方式があります。

(※ただ、この方式はハッシュ値を利用するため、衝突の可能性を考慮して選択する必要があります)

64ビットのハッシュ値

ハッシュ生成アルゴリズムとして有名なのは MD5 ですが、MD5 の bit 数は 128ビットと長いため、データベースに格納する際に数値型に変換することができず、バイナリ型などにする必要があります。

そのため大量のデータを検索する際に、数値型と比較するとインデックスのサイズが大きくなってしまい、パフォーマンスが悪くなったりします。

この場合、MD5の半分の長さである64ビットのハッシュ値を利用すると数値型で格納することができます。
64ビットであれば、8 byte の数値型 (MySQLだとbigint) としてデータベースに格納することができます。

SipHash アルゴリズム

64ビットのハッシュ値を生成するアルゴリズムには色々な種類がありますが、ここでは SipHash をご紹介します。
qiita.com

GoogleのGuavaライブラリ

Javaから SipHash アルゴリズムを利用する場合、GoogleのGuavaライブラリが利用できます。
Mavenのライブラリとして提供されているため、下記のPOM定義でインポートする事が可能です。
(または Gradle)

コード例
<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>18.0</version>
</dependency>

サンプルコード

テキストをseedにしたlong値を生成するサンプルを示します。
下記のコードで一意なlong値を生成してDBのIDカラムに格納できます。

コード例
String seed = "hoge";
byte[] bytes = seed.getBytes(Charsets.UTF_8);
long longValue = Hashing.sipHash24().hashBytes(bytes).asLong();

IDをURLとして利用する場合などは、long型ではなく、16進数の文字列に変換してから利用すると良いかもしれません。

コード例
String hex = Long.toHexString(longValue);

戸谷 洋紀マネックス・ラボ マネージャー