入力文字列に基づいて指定したビット範囲の数値を返すので、それを利用してアクセス先を振り分けることが出来る。
使い方としては、以下の用に接頭語を付けてキーを作成する。
private static Key createShardKey(String inPrefix, String inKeyName) { int bitSize = 8; long hashNum = Digest.getHashNum(inKeyName, bitSize); String key = inPrefix + String.format("_%05x", hashNum); return Datastore.createKey(Hoge.class, key); }
データの読み込みはキーの名前を文字列に格納し、クエリで取得する。
(ここでは検索用にキーとは別に文字列型のフィールドを用意していますが、キーに対して前方一致はが出来ればな良いのにな~、と思いました。
少しでも不要なインデックスを減らしたいので。)
Datastore.query(META) .filter(META.keyName.startsWith("Prefix")) .asList();
処理の内容としては、文字列をハッシュ変換し、その先頭から数値を取り出しています。
取り出す数値の範囲は出来るだけ偏りが無いように、ビットで指定します。
なので2, 4, 6, ...と2の倍数までの値となります。
package auction.service.util; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class Digest { /** * 指定したビット範囲内のハッシュを取得 * */ public static long getHashNum(String str, int bit) { byte[] hash = digest(str); long num = 0; /* ハッシュ値の先頭8byteをLongへ変換 */ for (int cnt_i = 0; cnt_i < 8; cnt_i++) { num = (num << 8) | (hash[cnt_i] & 0xff); } /* 指定ビットでマスク */ long mask = getMask(bit); num = num & mask; return num; } /** * ハッシュ文字列を取得 * */ public static String getHash(String str) { byte[] hash = digest(str); return hexToString(hash); } /** * 文字列をハッシュ化した結果を表示 * */ public static void printDigest(String str) { byte[] hash = digest(str); System.out.println("文字列:" + str); System.out.println(hash.length); System.out.println(hexToString(hash)); } /** * メッセージダイジェスト (固定長のハッシュ値) を取得 * */ private static byte[] digest( String inText ) { MessageDigest dig = null; try { dig = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return null; } dig.update(inText.getBytes()); return dig.digest(); } /** * ビットマスクを取得 * */ private static long getMask(int bit) { long mask = 0L; for (int cnt_i = 0; cnt_i < bit; cnt_i++) { mask = (mask << 1) + 1L; } return mask; } /** * バイト列を文字列へ変換 * */ private static String hexToString(byte[] bin) { StringBuilder sb = new StringBuilder(); int size = bin.length; for (int i = 0; i < size; i++) { String hex = String.format("%02x", bin[i]); sb.append(hex); } return sb.toString(); } }