CloudSpanner使ってみた

# はじめに

* 最近、[ISUCON13](https://isucon.net/archives/57801192.html)に参加しまして、パフォーマンスについての意識が高くなっており、業務で実際活かすならどんなパターンが取れるだろうかと考えるようになりました
    * 業務では、ISUCONとは違い、もう少し予算があり、性能の良いサーバーの選択肢も取れると思うので、どんな選択肢が取れるかを少しずつ勉強しています
    * 今回は、DBサーバーの選択肢の一つとして、[Cloud Spanner](https://cloud.google.com/spanner?hl=ja)を触ってみたので紹介します！

## [Cloud Spanner](https://cloud.google.com/spanner?hl=ja)の特徴

* 強整合性を保証するRDBの特徴とグローバルに水平スケーリングできるNoSQLデータベースの特徴を併せ持つ
    * 強整合性: 読み取り処理で必ず最新の書き込み情報を読み取れる性質 (在庫状況や決済などで必須)
    * 水平スケーリング: データを分割してサーバーに保存することで、サーバーの数だけ負荷を分散して処理できる
        * 垂直スケーリングだとサーバーのスペックを上げることでスケールさせるが、1台のパワーでは限界が来てしまうため、大規模データ処理では水平スケーリングが必要になる
* 自動メンテナンス
    * 同期レプリケーションとメンテナンスは自動実行
        * ゼロダウンタイムでトラフィックを処理
* 自動シャーディング
    * データを自動的に分散し、中断することなくスケールアップ、スケールダウンが可能

その他、PostgreSQL Interfaceが使えたり、99.999%の可用性を実現していたりと色々な特徴があります。

### [料金](https://cloud.google.com/spanner?hl=ja#pricing-module)

* 下記二つの合計で料金が決定します (シングルリージョン構成の場合で算出しています)
    * 割り当てたコンピューティング容量 (100処理ユニット1時間あたり$0.09)
    * ストレージ使用量 (1GB/月あたり$0.30)

例えば、100処理ユニットで、ストレージ30GBとして1ヶ月間フル稼働すると $73.8となります((0.09 * 24 * 30) + (0.30 * 30))

テーブル数の上限も処理ユニットによって決定されるので、実際にはもう少し処理ユニットは増えてくるかと思います。
(10ユニット毎に1テーブル)

### ユースケース

* 大量のトランザクションがあり、かつ強整合性が求められるケース(必ず最新の書き込み情報を読み取れる性質)
* ダウンタイムを可能な限り減らしたいケースなど

### 気を付けるポイント

[スキーマ設計の最適化](https://cloud.google.com/spanner/docs/whitepapers/optimizing-schema-design?hl=ja)にひと工夫が必要です。

* シーケンシャルな主キーを避ける
    * Auto Incrementのような形でIDを生成するとホットスポット(特定のサーバへのアクセス集中)が作られてしまい、パフォーマンスの影響が出てしまう
    * 主キーのレンジに沿ってデータが分割されて保存されるので、Auto Incrementだとレンジが偏ってしまい、負荷が分散しない
        * 分割されたデータをスプリットと呼び、このスプリットを各ノードに割り当てて読み込みや書き込み処理を行う
            * そのため、ノードを増やすと1ノードあたりのデータ量が減り、負荷分散される





## チュートリアル
* 無料トライアルインスタンスとして90日間であれば、費用を発生させずに利用することができます

1.インスタンス名の指定
![スクリーンショット 2023-11-30 22.22.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224825/9204b500-092f-40fa-d951-e5453ce66b3a.png)

2.構成の選択

* 無料トライアルインスタンスでは4つのリージョンからしか選べません
    * 一番近いのがジャカルタですので、ジャカルタを選択します

![スクリーンショット 2023-12-01 1.23.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224825/2cc33291-a682-99b7-ab86-495b73b14d69.png)

3.チュートリアルの実行
* インスタンスを作成すると下記のようにチュートリアルが出てくると思うので、指示に従って進めていきましょう
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224825/b9129bed-cae9-cc0e-edf0-085324e12b9a.png" alt="スクリーンショット 2023-12-01 1.29.30.png" width="300px" />

* 権限の許可
    * Cloud Shellを開きます(Google Cloudのオンラインの開発環境)
        * `gcloud auth application-default \login`
        * 進めていくと、URLが表示されるので、クリックして権限を許可します
        * 出てきたAuthorization Codeをshellにペーストします
        * これで、gcloudコマンドを利用するのに必要な認証が完了しました
![スクリーンショット 2023-12-01 1.32.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224825/32446adb-f520-9c29-3bc7-1f72a6324c57.png)

* データベースの初期化
    * 下記のコマンドを入力します。instance_idは最初に登録したidです
    * 入力が完了すると、finance-dbという名前のデータベースが作成されます
```
gcloud spanner samples init \
    finance --instance-id \
    <instance_id>

</instance_id>
```

* 下記のテーブルが作成されているはずです
    * テーブル名をクリックすると、スキーマを参照することができます
    * DDLステートメントを直接記述して、テーブルの作成や変更、インデックスの作成などもコンソール上からできます
        * 咄嗟に変更を加えたり試したいときに便利そうですね！
![スクリーンショット 2023-12-01 1.40.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224825/ea54504a-ee82-7332-c18f-6fd3c2eaec0f.png)

### クライアントライブラリを使用してのDB接続

* 実際に、プログラムからCloud Spannerに接続をしてみます
    * [こちら](https://cloud.google.com/spanner/docs/getting-started/ruby?hl=ja)に沿って進めます

```shell
mkdir test_spanner
cd test_spanner
gem install google-cloud-spanner
gcloud auth application-default login
touch spanner.rb
```

* spanner.rbに下記のように記載し `ruby spanner.rb`で実行します
    *  すると、AccountIdの値が取れるはずです

```ruby spanner.rb
project_id  = "GoogleCloudのプロジェクトid"
instance_id = "インスタンスid"
database_id = "データベースid"
   
require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id
   
client.execute("SELECT AccountId FROM Account").rows.each do |row|
  puts "#{row[:AccountId].read}"
end
```

かなり簡単に接続できますね！
また、[localでのエミュレート](https://cloud.google.com/spanner/docs/emulator?hl=ja)もあるので、実際に開発する際にはエミュレートを利用するのが良いかと思います。


## まとめ
* 使うのにはひと工夫が必要
    * パフォーマンス発揮には主キーの設計が大事
* 設計がうまくできれば、メンテナンス性に優れ、運用が楽

以上です！　最後までご覧いただきありがとうございました！

## 参考文献

* [Spanner](https://qiita.com/kumagi/items/7dbb0e2a76484f6c522b)
* [Cloud Spannerの概要と設計上の要点など](https://qiita.com/pa_pa_geno/items/c5fc5ea0d5daf415080d)
* [Cloud Spannerの主キーの設計](https://medium.com/google-cloud-jp/cloud-spanner%E3%81%AE%E4%B8%BB%E3%82%AD%E3%83%BC%E3%81%AE%E8%A8%AD%E8%A8%88-bfb5ce2f54b9)
* [Google SpannerとMySQLのトランザクションの違い](https://recruit.gmo.jp/engineer/jisedai/blog/spanner_introduction/)
* [Cloud Spannerを徹底開設!](https://blog.g-gen.co.jp/entry/cloud-spanner-explained)
* [Google Cloud Spannerを使った際に感じた良かった点と注意点](https://tech.liberent.co.jp/entry/2021/12/01/000000)