🍰
Go言語で学ぶ Webアプリケーション開発4:[デプロイ & DB負荷対策]
はじめに
前回の記事では、Dockerを使ったコンテナ化とgRPCを導入し、よりスケーラブルなWebアプリケーションを構築しました。
今回は、簡易的なHerokuへのデプロイ方法と、大量のToDoデータを扱う際のパフォーマンス最適化(負荷対策)について解説します。
対象読者
- Goで開発したWebアプリをデプロイしたい方
- Webアプリのパフォーマンスを改善し、大量データに対応できる設計を学びたい方
目次
- Heroku へのデプロイ
- Herokuのセットアップ
- HerokuにGoアプリをデプロイする手順
- ToDoアプリの負荷対策
- インデックス最適化(データベース)
- キャッシュ戦略(Redis の活用)
- 非同期処理(Go の Goroutine を活用)
- ページネーション(大量データの取得を最適化)
1. Herokuへのデプロイ
1.1 Herokuのセットアップ
まず、Heroku CLIをインストールして、Herokuアカウントにログインします。
# Heroku CLI をインストール(Mac)
brew install heroku
# ログイン
heroku login
1.2 HerokuにGoアプリをデプロイする手順
-
Heroku用のProcfileを作成
HerokuはProcfile
を使ってアプリの起動方法を定義します。Procfile
web: ./main
-
Heroku用の
go.mod
の作成go mod init example.com/myapp go mod tidy
-
Herokuにアプリをプッシュ & デプロイ
git init git add . git commit -m "Initial commit" heroku create my-go-todo-app git push heroku main
-
Herokuでアプリを実行 & 確認
heroku open
2. ToDo アプリの負荷対策
2.1 インデックス最適化(データベース)
- ToDoの数が増えてくると、データベースの検索速度が遅くなる。
- インデックスを設定することで、検索を高速化できる。
import "gorm.io/gorm"
type Todo struct {
ID uint `gorm:"primaryKey"`
Task string `json:"task" gorm:"index"`
Status bool `json:"status"`
}
-
gorm:"index"
をつけることで、データベース側でTask
フィールドの検索が最適化される。
2.2 キャッシュ戦略(Redisの活用)
- 頻繁に取得するデータをRedisにキャッシュすることで、DB負荷を軽減する。
-
github.com/go-redis/redis/v8
を利用。
Redisの導入
go get github.com/go-redis/redis/v8
Redisクライアントのセットアップ
import (
"context"
"github.com/go-redis/redis/v8"
)
var ctx = context.Background()
var rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
キャッシュを使った ToDo 取得
func GetTodos(c *gin.Context) {
cached, err := rdb.Get(ctx, "todos").Result()
if err == nil {
c.JSON(http.StatusOK, cached)
return
}
var todos []models.Todo
database.DB.Find(&todos)
rdb.Set(ctx, "todos", todos, time.Minute*10)
c.JSON(http.StatusOK, todos)
}
-
Redis
にデータがあればキャッシュから返し、なければDBから取得してキャッシュに保存。
2.3 非同期処理(Goroutineの活用)
- 新しいToDoの登録時に、非同期でログを記録する。
func CreateTodo(c *gin.Context) {
var newTodo models.Todo
if err := c.ShouldBindJSON(&newTodo); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
database.DB.Create(&newTodo)
go func(task string) {
log.Printf("New ToDo created: %s", task)
}(newTodo.Task)
c.JSON(http.StatusCreated, newTodo)
}
-
go func()
を使うことで、非同期でログの記録を行い、メインスレッドのレスポンス速度を落とさないようにできる。
2.4 ページネーション(大量データの取得を最適化)
- ページネーションを導入し、1回のAPIリクエストで取得するデータ量を制限。
func GetTodosPaginated(c *gin.Context) {
var todos []models.Todo
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
limit := 10
offset := (page - 1) * limit
database.DB.Limit(limit).Offset(offset).Find(&todos)
c.JSON(http.StatusOK, todos)
}
-
GET /api/todos?page=2
のようにページ番号を指定すると、10件ずつデータを取得するようになる。
まとめ
項目 | 説明 |
---|---|
Heroku デプロイ | Heroku CLIを使って簡単にGoアプリをデプロイ |
インデックス最適化 | データベース検索を高速化し、レスポンス遅延を軽減 |
Redis キャッシュ | 頻繁にアクセスするデータをキャッシュし、DB負荷を削減 |
非同期処理 (Goroutine) | ToDoの作成時にログ記録を非同期で実行し、API応答速度を向上 |
ページネーション | 一度に大量のデータを取得せず、適切なデータ量を取得 |
本記事では、簡単なデプロイ方法と、大量のToDoデータを扱う際の負荷対策について解説しました。
次は、Kubernetesを活用したデプロイ方法とか、負荷テストの実施について補足がてらにまとめようかな...
Discussion