1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Go言語で学ぶ Web アプリケーション開発4:[デプロイ & DB負荷対策]

Last updated at Posted at 2025-03-04

はじめに

前回の記事では、Dockerを使ったコンテナ化とgRPCを導入し、よりスケーラブルなWebアプリケーションを構築しました。
今回は、簡易的なHerokuへのデプロイ方法と、大量のToDoデータを扱う際のパフォーマンス最適化(負荷対策)について解説します。

対象読者

  • Goで開発したWebアプリをデプロイしたい方
  • Webアプリのパフォーマンスを改善し、大量データに対応できる設計を学びたい方

目次

  1. Heroku へのデプロイ
    • Herokuのセットアップ
    • HerokuにGoアプリをデプロイする手順
  2. ToDoアプリの負荷対策
    • インデックス最適化(データベース)
    • キャッシュ戦略(Redis の活用)
    • 非同期処理(Go の Goroutine を活用)
    • ページネーション(大量データの取得を最適化)

1. Herokuへのデプロイ

1.1 Herokuのセットアップ

まず、Heroku CLIをインストールして、Herokuアカウントにログインします。

# Heroku CLI をインストール(Mac)
brew install heroku

# ログイン
heroku login

1.2 HerokuにGoアプリをデプロイする手順

  1. Heroku用のProcfileを作成
    HerokuはProcfileを使ってアプリの起動方法を定義します。

    Procfile

    web: ./main
    
  2. Heroku用のgo.modの作成

    go mod init example.com/myapp
    go mod tidy
    
  3. Herokuにアプリをプッシュ & デプロイ

    git init
    git add .
    git commit -m "Initial commit"
    heroku create my-go-todo-app
    git push heroku main
    
  4. 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を活用したデプロイ方法とか、負荷テストの実施について補足がてらにまとめようかな...

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?