質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
データベース

データベースとは、データの集合体を指します。また、そのデータの集合体の共用を可能にするシステムの意味を含めます

データベース設計

データベース設計はデータベースの論理的や物理的な部分を特定する工程です。

データ構造

データ構造とは、データの集まりをコンピュータの中で効果的に扱うために、一定の形式に系統立てて格納する形式を指します。(配列/連想配列/木構造など)

ハッシュ

ハッシュは、高速にデータ検索を行うアルゴリズムのことです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

5415閲覧

ハッシュ値とUUIDを用いたデータ管理

koyamashinji

総合スコア45

データベース

データベースとは、データの集合体を指します。また、そのデータの集合体の共用を可能にするシステムの意味を含めます

データベース設計

データベース設計はデータベースの論理的や物理的な部分を特定する工程です。

データ構造

データ構造とは、データの集まりをコンピュータの中で効果的に扱うために、一定の形式に系統立てて格納する形式を指します。(配列/連想配列/木構造など)

ハッシュ

ハッシュは、高速にデータ検索を行うアルゴリズムのことです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2021/06/25 13:48

編集2021/06/25 13:50

以下のコードは、こちらのコード(※)の気になった部分をシンプルな形に変更した抜粋コードです。

内容は、インメモリでデータ管理するコードで、
以下の仕様が特徴です。
dataDataBaseに渡す
DataBasedataのハッシュ値 及び uuidを生成する
._data: {uuid, data} , ._index: {hash value, uuid}の形で管理する

わからないこと

pythonに限った話ではないかもしれないですが、
このように、ハッシュ値とUUIDを用いたデータ管理の目的、メリットがいまいち分かりません。

データを直接、リストや辞書オブジェクトに格納して管理するのでなく、
わざわざ ①ハッシュ値とUUID、②UUIDと対象データ、という異なる辞書に格納し、
①②をUUIDで紐づけることでどんな良いことがあるのか。


python

1from typing import Dict 2import uuid 3 4class DataBase: 5 def __init__(self) -> None: 6 self._data: Dict[uuid.UUID, Dict] = {} 7 self._index: Dict[int, uuid.UUID] = {} 8 9 def insert(self, 10 data: Dict[str, int]) -> None: 11 12 keyhash = hash(tuple(data)) 13 _id = uuid.uuid4() 14 self._data[_id] = data 15 self._index[keyhash] = _id 16 17db = DataBase() 18db.insert(data={'priceA': 5000}) 19db.insert(data={'priceB': 4000}) 20 21... 22

(※)参照元のコードは、API経由で入手したデータをインメモリで管理(READ, UPDATE, INSERT, DELETE等)するクライアント側処理です。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

★初期回答

※ 誤っている点もありますが、記録として残しておきます。後ろに追加した追加検証もご覧ください。(誤っている箇所は取り消し線を入れました)

完全に私の主観ですが、こういう事ではないでしょうか。

  • ① UUIDにより辞書型のキーが重複しない様にし
  • ② ハッシュ化により、データ同士の比較を容易にしている

UUIDについて

_id = uuid.uuid4() について
UUID4は乱数ベースなのでデバイスの違いなどによる偏りが出ないと予測します。
UUIDを使う意図としては重複しない事ですね。辞書型のキーとして使うならここは重要と思います。
データの中身は一緒でもキーが異なるので、同じデータをいくつも持てる事にもなります。

※このプロダクトだとキーは2種類。同一データでもユニークに扱うためのキーと、KV型データの中のキーに相当するものがあります。以降、ユニークキーデータキーと使い分けます。

ハッシュ化について

hash(tuple(data)) について
データをハッシュ化する事で、一定の型(組込hash()なので整数)になります。
格納するデータの型はオブジェクトも含んでまちまちだと思いますが、一緒かどうかという判定はラクに出来ると思います。
キーが違っていても同じデータというのはあるので、それらが一緒かどうかというのもハッシュを見るだけでOKという事になります。

※正しくは、データキーの重複に容易に気づける事でしょう

ただ、これは要件次第ではデメリットにもなると思っていまして
データとしてオブジェクトを格納する場合、一律でhash(tuple(data))としているのでオブジェクトの中の特定の値を見たいといった場合には、正しく比較が出来ない可能性もあります。
(例えばdata.hogeの値だけで同値かを判定する)

単に、is演算子的な比較をしたいのかどうかというのもありますね。
どちらにせよ、この辺は作りたいものに合わせて修正する必要があるかもしれません。

※botterの性質を考えると、デメリットは特に思いつきません

蛇足

Javaの話になりますが、全てのObjectにはEquals(obj)という関数があります。
これはObject自身のhash値を計算して、引数のobjと同値かどうかで判定しています。

質問に掲載したコードの意図はこれと似たようなものではないのかと思いました。

※機能の性質としてはあっているが、意図とは異なるでしょうね…orz

参考

Python 標準ライブラリ » 組み込み関数
https://docs.python.org/ja/3/library/functions.html#hash

Python インターネットプロトコルとサポート » uuid --- RFC 4122 に基づくUUID オブジェクト
https://docs.python.org/ja/3/library/uuid.html

モジュール java.base > パッケージ java.lang > クラスObject > java.lang.Object
https://docs.oracle.com/javase/jp/15/docs/api/java.base/java/lang/Object.html#equals(java.lang.Object)

★追加検証分

追記分です。

今回掲載したコードで目的を考えるなら、下記の性質から単サーバ複クライアントでも最新データに気づく為の仕掛けと考えると腑に落ちました。

  • DataBase._indexにはキーの数だけ要素が増える事
  • DataBase._dataの各要素に対するindexは、最新のidを指している事
  • ③ GitHubのプロダクトが仮想通貨用のbotterである事(時間とタイミングの勝負でしょうし)

検証コード

python3

1from typing import Dict 2import uuid 3 4class DataBase: 5 6 def __init__(self) -> None: 7 """ 8 初期化 9 """ 10 self._data: Dict[uuid.UUID, Dict] = {} 11 self._index: Dict[int, uuid.UUID] = {} 12 13 14 def insert(self, data: Dict[str, int]) -> None: 15 """ 16 KV型データを追加する 17 """ 18 _id = uuid.uuid4() 19 self._data[_id] = data 20 21 keyhash = hash(tuple(data)) 22 self._index[keyhash] = _id 23 24 def print_all_datas(self): 25 """ 26 データリストを出力する 27 """ 28 for i, _id in enumerate(self._data): 29 _data = self._data[_id] 30 _seed = tuple(_data) 31 _keyhash = hash(_seed) 32 _index = self._index[_keyhash] 33 print(f'{i} => id: {_id}, index: {_index}, hash: {_keyhash: >20}, seed: {_seed}, data: {_data}') 34 35 def print_all_indexes(self): 36 """ 37 ハッシュリストを出力する 38 """ 39 for i, _keyhash in enumerate(self._index): 40 _id = self._index[_keyhash] 41 print(f'{i} => hash: {_keyhash: >20}, latest_id: {_id}') 42 43db = DataBase() 44db.insert({'priceA': 5000}) 45db.insert({'priceB': 4000}) 46db.insert({'priceA': 3000}) 47db.insert({'priceA': 5000}) 48db.insert({'priceA': 7000}) 49 50print('### print_all_datas ###') 51db.print_all_datas() 52 53print('### print_all_indexes ###') 54db.print_all_indexes()

実行結果

log

1### print_all_datas ### 20 => id: 498a2423-8f65-42ac-90f7-d2e321f8b1ec, index: 689e09cb-dadd-4cb7-ad69-a6377c4a362f, hash: -261320819014024164, seed: ('priceA',), data: {'priceA': 5000} 31 => id: daaa98fb-cba8-4dbd-9299-172754780f01, index: daaa98fb-cba8-4dbd-9299-172754780f01, hash: 4814718565598271203, seed: ('priceB',), data: {'priceB': 4000} 42 => id: c0422535-cdae-4b92-a22a-09cf98dd928f, index: 689e09cb-dadd-4cb7-ad69-a6377c4a362f, hash: -261320819014024164, seed: ('priceA',), data: {'priceA': 3000} 53 => id: cf0d74f1-752f-43a6-898c-292c276d9bb3, index: 689e09cb-dadd-4cb7-ad69-a6377c4a362f, hash: -261320819014024164, seed: ('priceA',), data: {'priceA': 5000} 64 => id: 689e09cb-dadd-4cb7-ad69-a6377c4a362f, index: 689e09cb-dadd-4cb7-ad69-a6377c4a362f, hash: -261320819014024164, seed: ('priceA',), data: {'priceA': 7000} 7### print_all_indexes ### 80 => hash: -261320819014024164, latest_id: 689e09cb-dadd-4cb7-ad69-a6377c4a362f 91 => hash: 4814718565598271203, latest_id: daaa98fb-cba8-4dbd-9299-172754780f01

検証結果から分かる事

  • 登録したデータは重複キーも含めて全てユニークに持っている(UUIDをユニークキーとしている)
  • indexには、最新データを持つユニークキーの一覧が入っている
  • 各登録データは、hashを使ったデータキーによるアクセスをする事で、indexに入っている最新のユニークキーが事に気づける。クライアントとしては自分のユニークキーとは違うと気づける
  • 今回の掲載コードにはありませんが、hashが一致しなければ更新させないといった楽観的ロックの仕掛けと見る事も出来そうですね

以上です。長文失礼しました。

投稿2021/06/26 01:01

編集2021/06/26 11:04
neonemo

総合スコア191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

neonemo

2021/06/26 02:06 編集

hash(tuple(data)) この部分が意図した感じに動かないので、もうちょっと検証します。 追記:ちょっと検証しました。 掲載コードだけでは判断が難しいですが、DB処理である事を踏まえてGitHubのコードを見ると、 更新や削除処理でもハッシュを使用しています。 ハッシュではキーが一緒かどうかを判断しているようなので、一括処理の為の仕掛けに見えますね。 UUIDだけでは1件処理しか出来ませんし、データの中からキーだけを取り出すのも確かに面倒かなと思います。 これから用事があるので、夜にでもまた検証してみます。
koyamashinji

2021/06/26 03:02

有難うございます。考え方について大変勉強になります。追加見解がございましたら是非お待ちしています。
neonemo

2021/06/26 11:10

追記入れました。 どうしても推測の域は出ませんが、ハッシュで扱う事に一定の価値は見いだせたと思います。 後は使い方次第ですね。 koyamashinjiさんの要件にマッチしなければハッシュ部分は排除しても良いと思います。 1サバ1クラ前提なら多分要らないでしょうし。
koyamashinji

2021/06/27 01:31

検証及び非常に興味深い見解を有難うございます。たしかに、単サーバ複クライアント下での仕組みと考えると納得できます。自分にあった要件に合わせて実装してみたいと思います。 有難うございました。
guest

0

tuple(data)は辞書のキーだけのタプルなので、値が違っていてもキーが同じならhash(tuple(data))は等しくなります。

python

1d1 = {'a': 1, 'b': 10} 2d2 = {'a': 100, 'b': 100} 3 4print(hash(tuple(d1)) == hash(tuple(d2))) 5# True

なので、hash(tuple(data))を使ってキーが同じもの(型とかスキーマとかみたいなイメージ)を、まとめて扱おうとしていると推察されます。

python

1 def insert(self, 2 data: Dict[str, int]) -> None: 3 4 keyhash = hash(tuple(data)) 5 _id = uuid.uuid4() 6 self._data[_id] = data 7 self._index[keyhash] = _id

全てのdataにUUIDを振って self._data に保存して、
同じキーを持つものの最後にinsertされたものを self._index に保存しているように見えます。
なので、単純に紐付けをしているのではないです。

データを直接、リストや辞書オブジェクトに格納して管理するのでなく、
わざわざ ①ハッシュ値とUUID、②UUIDと対象データ、という異なる辞書に格納し、
①②をUUIDで紐づけることでどんな良いことがあるのか。

メリットについては、検索とかの他の機能でそれをどう使っているか次第ですね。
(参考元のコードは全部追えていません)

投稿2021/06/26 12:23

編集2021/06/26 13:01
bsdfan

総合スコア4794

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

koyamashinji

2021/06/27 01:31

ご回答、誠に有難うございます。勉強になりました。
bsdfan

2021/06/27 01:36

よく考えると、pythonのdictはタプルをキーにできるので、hashとる必要はないですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問