32
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SQLが嫌いすぎてデータベースを自作した話

Last updated at Posted at 2022-08-03

追記

いくらSQLが嫌いでも、データベースを自作すると大変なことになりかねませんので、やめておきましょう(ぼくもう知らないもん)

目的

  1. 何のdbを選ぶのかは自分で選べるということです
  2. ちなみに自作したゆーとりますが、完成度はそんなに高くないです
  3. ちなみに少し脚色してまs((

関係知識

にらBOTという、Python製DiscordBOTを作っているのですが、もともと、データはpickleを使ってファイルに保存していたのですが、これがちょっとめんどくさいなーと思い、データベースに保存するように変えました。
最初は、Googleスプレッドシートに保存していましたが、セットアップが面倒なのと、データの読み書き速度が遅い事に難がありました。
てなわけで、セルフホスティングなデータベースを使用する事にしたのです。
さて、「セルフホスティングなデータベースと言えばなんでしょう?」と聞かれたら大半の方が「SQL!」と元気よく答えると思います。
ですがSQLには難点があります。それは...
構文を覚えるのが面倒!(毛嫌い)
てなわけで、自作のもうちょっと楽なデータベースを作ろうと思ったわけです。

実験

Plan

まず、データベース...というより「データを保存する形式」として有名なものがありますよね。
そう、JSONですよね???(だいぶ強引)
そして、データベースはサーバーがあって、そこと通信してデータをやり取りしますよね。なので、サーバーサイドとクライアントサイドで通信する機構が必要です。
そう!HTTPですよね!!!!
こうして、HTTP通信を使ってJSON形式でやりとりすると言うところまで決まりました。

さて、まずはサーバーサイドソフトの構造から決めましょう。
HTTPJSONをメインとするのにちょうどいい言語があります。
JSONという名前から察する方も沢山いるでしょう。
ご名答です。JavaScript...ではなくPythonです!!!
(ほら...JSONの最後の2文字とPythonの最後の2文字が同じでしょ...?)

なぜJavaScriptを使わなかったのか

今の時代、サーバーサイドとしてJavaScriptを使っていると、TypeScriptはどうした?^^と煽られてしまうのと、サーバーサイドのJavaScriptについて教養が浅かったからです。

てなわけで、PythonHTTPサーバーを建てるということで、前々から気になっていたSanicに触れる事にしました。

Do

そして出来上がったサーバーサイドソフトウェアがこちらでs((

Sanicのレスポンス処理で、JSONを返していくというような感じの、至って単純なものです。
なんせSimple and easy database system using HTTPですので...。

さて、ここからはクライアントサイドの構造です。
クライアントサイドは、普通にHTTP POSTをすればいいのですが、にらBOTで扱う時にラッパーがあれば楽だなと言う事でPythonラッパーを作っていきます。

そして、人生で初めてちゃんとクラスを扱う事になりました。
オブジェクト指向ですねぇ〜

client.py
class Client:
    def __init__(self, url: str = "http://localhost:8080"):
        self.url = url

    async def info(self) -> dict:
        ...

みたいな感じです。(適当)

なぜアドレスとポートに分けずURLとしたのかと言うと、私がリバースプロキシを使ってデータベースのURLをhttps://example.com/example_databaseみたいにしたせいで、これをそのまま入れるとexample.com/example_database:443となってしまって、良い方法も見つからなかったのでこうしました。

Check

ある程度期待の動作をしてくれました。
凄く嬉しい事です。
さて、「このままじゃポート(URL)が分かると、誰からでもデータをいじられてしまう」と言うのが議題に上がりました。
そして、にらBOTのエラーハンドルを適当にしているので、もしデータベースのエラーが起きてた場合に、アドレスが普通に表示されてしまうような気がするので、もう情報ダダ漏れ待ったなしです。
DiscordDeveloperの規定上、ユーザーの個人情報を保護するのは絶対なので、これではいけません。

Action

さて、パスワード保護をつける事にしましょう。
私の中で、パスワード保護は2種類の方法に分けています。

種類 説明
暗号化式 サーバー/クライアント側で暗号化して送信して、クライアント/サーバー側で復号する
認証式 正しいパスワードでのみデータの受信/送信が出来る

今回は、ちょうど良い暗号化モジュールがなかったので、認証式にしました。

client.py
class Client:
-   def __init__(self, url: str = "http://localhost:8080"):
-       self.url = url
+   def __init__(self, url: str = "http://localhost:8080", password: str = ""):
+       self.url = url
+       self.password = password

    async def info(self) -> dict:
        ...

こんな風にしてパスワード認証をつけました。
やはり認証は必要ですね。

結果の検討

Check(2度目)

ワイ「おっしゃこれで完成やろ!」
ワイ「BOTからデータいじれるか試してみよ!」
にら「出来たで」
ワイ「おっしゃ!これで完璧や!じゃあ実際にこれからはここにデータを入れよう!」
ワイ「{サーバーID: {チャンネルID: 設定項目 } }みたいなの送ってみるか。いやーJSONは辞書形式にも対応してくれてて嬉しいわ〜」
にら「送ったで」
ワイ「おっしゃ!あとはこれを読み込んでちゃんと設定項目が確認できれば...」
にら「データベースから読み込んだで。ちなみに、データの中にこのサーバーIDは見つからんかったで
ワイ「( ˙꒳​˙ )?????」

これはJSONの辞書形式において、int形式のキーに対応してない事が原因で、データを送る際に勝手に変換されてしまう事が原因でした。

送りたかったデータ
{
    1234567890: {
        9876543210: true,
        1528371918: false,
    }
}
実際に送られたデータ
{
    "1234567890": {
        "9876543210": true,
        "1528371918": false,
    }
}
実際に受け取ったデータ
{
    "1234567890": {
        "9876543210": true,
        "1528371918": false,
    }
}

てなわけで、辞書をintで参照したけどstrしかないのでそりゃあデータがないと言われるわけです。
解決策をいろいろ模索した結果、最終的にBOT側では「辞書形式で送らない」事にしました。

Action(2度目)

「いや待ちなされ奥さん、辞書で送らないってどう言うことよ」って思われた方も多いと思います。
勿論、後でeval()で戻せるように辞書をそのまんまstrに変えるってのは、Nonsense ですよね。
てなわけで、listで送る事にしました。
辞書形式とは、本当(?)は連想配列形式っていうことなので、楽に配列にする事ができます。
実際に試してみると分かります。

repl
>>> value = {12345: 'hello', 'hola': 67890}
>>> list_value = list(value.items())
>>> print(list_value)
[(12345, 'hello'), ('hola', 67890)]

ね?簡単でしょ?(某ペンギン風)

てなわけで、これを渡すようにしました。
そしてデータを読み込むときは、配列形式を辞書形式にするようにしました。

実際のコード
def listToDict(source) -> dict:
    temp = {}
    source = dict(source)
    for i in list(source.keys()):
        if i not in temp:
            temp[i] = {}
        temp[i].update(dict(source[i]))
    return temp

こんなのをutilにいろいろ突っ込んだりして、なんとかデータベースとちゃんとデータをやり取り出来るようになりました。

結果の整理

てなわけで、実際にデータベースを自作する事ができました。
詳しくは、各レポジトリを見てみてほしいです。

  • HTTP_db(自作したデータベース)

  • にらBOT(自作しているDiscord BOT)

感想

今回、このデータベース自作を通して、データベースを作るのは大変だと思いました。(小並感)
SQLの方が楽そうだと思いました。(本末転倒)
でもここまで来たら引き下がれませんでした。(謎のプライド)
多分そのうち限界が来たらSQL使います。(諦め)
色々学べる事があって楽しかったです。(ピーク・エンドの法則)
皆さんはSQL使いましょう(何がしたかったんだか)

よければ...

「いいね」とか「Star」よろしくね⭐︎

32
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?