言語を切り替える
テーマを切り替える

Nginx パフォーマンスチューニング:gzip、キャッシュ、接続プール設定

先週、アラートが鳴りました。EC サイトのトップページ読み込み時間が 4 秒に急上昇していたのです。Chrome DevTools を開いてみると、HTML ファイルが 120KB、CSS と JS でさらに 350KB —— すべて未圧縮の生ファイルです。さらに悪いことに、すべてのリクエストがバックエンドに到達し、キャッシュヒット率はわずか 12% という悲惨な状態でした。その夜、残業の後、2 時間かけて Nginx 設定を調整しました。gzip 圧縮を有効にし、キャッシュ戦略を補完し、接続プールパラメータを調整。翌朝確認すると、トップページ読み込みは 1.6 秒に短縮され、バックエンドの QPS は半分近く下がっていました。

正直なところ、こうした問題は非常によくあります。多くの人は Nginx をインストールしてそのまま実行するだけで、gzip はデフォルトで無効、キャッシュは適当に 2 行書いて終わり、接続数の上限はデフォルト値のまま。その結果、トラフィックが増えると、サーバーが息切れしてしまうのです。

この記事では、本番環境で検証済みの Nginx パフォーマンスチューニング設定をまとめました。gzip 圧縮で転送量を 60-80% 削減、キャッシュヒット率 95% でバックエンド負荷を 90% 削減、接続プール設定を適切に行えば同時接続数を 3〜4 倍に向上させることができます。各モジュールの設定詳細、遭遇した落とし穴、実測データをすべて詳しく解説します。

第 1 章:gzip 圧縮設定 — 転送量を削減する

まず、gzip がなぜこれほど重要なのかから説明します。HTML ファイルの元のサイズが 100KB だとします。gzip 圧縮を通すと、20-25KB になるかもしれません。この節約された 75-80KB の帯域幅は、ユーザーにとってはより速い読み込み速度、運営者にとってはより低いトラフィックコストを意味します。

この問題に初めて気づいたのは、あるクライアントのプロジェクトでした。モバイルユーザーが 60% を超え、多くが 4G ネットワーク環境でアクセスしていました。トップページの読み込みに 3-4 秒かかり、直帰率は 70% に急上昇。gzip を追加した後、転送量は直接 70% 削減され、初回画面読み込み時間は 1.5 秒程度まで低下しました。

1.1 基本設定:まずは動作させる

Nginx の gzip 設定は実は複雑ではありません。核心は数行だけです:

http {
    gzip on;
    gzip_vary on;
    gzip_min_length 1000;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
}

gzip on はスイッチです。説明不要でしょう。gzip_vary on が重要で、これを有効にするとレスポンスヘッダーに Vary: Accept-Encoding が追加されます。CDN とブラウザに「このレスポンス内容はクライアントの圧縮能力によって変化する」ことを伝え、キャッシュの混乱を防ぎます。

gzip_min_length を 1000 バイトに設定すると、1KB 未満のファイルは圧縮しません。小さすぎるファイルは圧縮のメリットが少なく、逆に CPU を浪費します。gzip_types は圧縮する MIME タイプを指定します。デフォルトでは text/html しか圧縮しないため、CSS、JS、JSON、XML も追加する必要があります。

1.2 応用設定:圧縮レベルと MIME タイプリスト

圧縮レベルはバランスを取る必要がある選択です。Nginx の gzip_comp_level は 1-9 に設定でき、数字が大きいほど圧縮率は高くなりますが、CPU 消費も増えます。

テストを行いました:

圧縮レベルHTML 圧縮率CPU 時間(ms)推奨シナリオ
165%2CPU に余裕がない場合
472%3バランス型(推奨)
675%5帯域幅に余裕がない場合(推奨)
978%12極端なシナリオ

正直、レベル 4 と 6 がほとんどのケースで最適です。レベル 9 は CPU 消費が倍になりますが、圧縮率は数ポイントしか向上しません。割に合いません。

以下は本番環境で使用している完全な gzip 設定です:

# gzip 圧縮設定
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types
    text/plain
    text/css
    text/xml
    text/javascript
    application/json
    application/javascript
    application/xml
    application/xml+rss
    application/xhtml+xml
    application/x-javascript;
gzip_disable "msie6";

gzip_proxied any というパラメータは見落とされがちです。Nginx がリバースプロキシとして動作する場合、バックエンドが返すレスポンスヘッダーに Content-Length がないと、デフォルトでは圧縮されません。any に設定すると、条件に合致するすべてのレスポンスを強制的に圧縮できます。

gzip_disable "msie6" は古い IE6 との互換性のためです。IE6 は gzip サポートに問題があります。現在、IE6 はほぼ絶滅しました。この行は削除しても構いませんが、私は念のため残しています。

1.3 どのファイルタイプが最も効果的?

実測データによると、ファイルタイプによって圧縮効果に大きな差があります:

ファイルタイプ元のサイズ圧縮後圧縮率
HTML100KB20-25KB75-80%
CSS80KB24-28KB65-70%
JavaScript120KB36-42KB65-70%
JSON API50KB20-25KB50-60%
画像/動画既に圧縮済み無意味0-5%
75-80%
HTML ファイル圧縮率
来源: 本番環境実測データ

画像と動画は既に圧縮されている(JPEG、PNG、MP4)ため、さらに gzip すると逆にサイズが増加します。したがって、gzip_typesimage/*video/* を絶対に追加しないでください。追加すると逆効果です。

以前、誰かの問題を調査した際、gzip_typesimage/jpeg が追加されているのを発見しました。結果として、画像サイズが逆に 3-5% 増加していました。こうした初歩的なミスは、私も経験があります —— 昔、よく理解していなかった頃は、すべての MIME タイプを追加したがったものです。

第 2 章:キャッシュ戦略設定 — 静的コンテンツの高速化

キャッシュはパフォーマンスチューニングの中で最も直接的なメリットがある部分です。適切に設定すれば、95% のリクエストを Nginx から直接返し、バックエンドに到達させる必要がなくなります。多くのシステムを見てきましたが、バックエンドサーバーが汗だくで動いているのに、Nginx 側ではキャッシュがほとんど使われていない —— 有効になっていないのではなく、設定が間違っているのです。

2.1 proxy_cache と fastcgi_cache、どっちを選ぶ?

Nginx は 2 種類のキャッシュメカニズムを提供しています:

  • proxy_cache:上流サーバーのレスポンスをキャッシュ、リバースプロキシシナリオに適用(Node.js、Python、Go サービスなど)
  • fastcgi_cache:FastCGI プロセスのレスポンスをキャッシュ、PHP-FPM シナリオに適用

どちらを選ぶかは、バックエンドの技術スタックによります。PHP を使用しているなら fastcgi_cache、Node.js、Python、Go なら proxy_cache です。両者の設定ロジックはほぼ同じなので、以下は proxy_cache を例に解説します。

2.2 完全な proxy_cache 設定

まず、http ブロックでキャッシュパスを定義します:

http {
    proxy_cache_path /var/cache/nginx
                     levels=1:2
                     keys_zone=my_cache:10m
                     max_size=10g
                     inactive=60m
                     use_temp_path=off;
}

各行を解説します:

  • levels=1:2:キャッシュディレクトリの階層、1:2 は 2 階層のディレクトリ構造を意味し、単一ディレクトリのファイル過多を回避
  • keys_zone=my_cache:10m:キャッシュ領域名とメタデータメモリサイズ、10m で約 8 万件のキャッシュキーを保存可能
  • max_size=10g:キャッシュ総サイズの上限、超過すると LRU アルゴリズムで削除
  • inactive=60m:60 分間アクセスがないキャッシュは削除
  • use_temp_path=off:直接キャッシュディレクトリに書き込み、一時ファイル移動のオーバーヘッドを回避

次に、server または location で有効にします:

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend;
        proxy_cache my_cache;

        # キャッシュ有効期間設定
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_valid any 1m;

        # キャッシュキー設計
        proxy_cache_key $scheme$request_method$host$request_uri;

        # フォールバック戦略(後で詳細解説)
        proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;

        # レスポンスヘッダーにキャッシュステータスを追加(デバッグ用)
        add_header X-Cache-Status $upstream_cache_status;
    }
}

proxy_cache_valid が核心設定で、ステータスコードごとにキャッシュ期間を定義します:

  • 200 302 10m:正常レスポンスは 10 分間キャッシュ
  • 404 1m:404 エラーは 1 分間キャッシュ、悪意あるリクエストがバックエンドに殺到するのを防ぐ
  • any 1m:その他のステータスコードは 1 分間キャッシュ

2.3 キャッシュキー設計と無効化戦略

キャッシュキー proxy_cache_key は、どのリクエストが「同一」とみなされるかを決定します。デフォルトは $scheme$proxy_host$request_uri ですが、明示的に宣言することをお勧めします:

proxy_cache_key $scheme$request_method$host$request_uri;

これでキャッシュキーにプロトコル、リクエストメソッド、ホスト名、完全な URI が含まれます。サイトが GET と POST の両方をサポートしている場合や、複数のドメインがある場合、この設定の方が正確です。

キャッシュの無効化は頭の痛い問題です。一般的な戦略:

  1. 時間経過proxy_cache_valid で時間を設定、期限切れで自動的に無効化
  2. 能動的回避proxy_cache_bypass でキャッシュをバイパス
  3. キャッシュ削除:商用版 Nginx Plus には proxy_cache_purge がある

私は一般的に 2 番目の方法を使用し、リクエストヘッダーで制御します:

# 特定のリクエストヘッダーでキャッシュをバイパス
proxy_cache_bypass $http_x_nocache;

# または特定のパラメータでバイパス
proxy_cache_bypass $arg_nocache;

キャッシュを更新したい場合、?nocache=1 を追加するか、リクエストヘッダー X-Nocache: 1 を送るだけです。

2.4 フォールバック戦略:バックエンド障害時もサービス継続

proxy_cache_use_stale この設定は非常に実用的です。バックエンドサービスでエラーやタイムアウトが発生した際、Nginx は期限切れのキャッシュコンテンツを返し、直接エラーを返すことを回避できます。

proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;

昨年のダブルイレブン(独身の日セール)、バックエンドサービスのスケールアウト時に問題が発生し、API サービスが断続的に 502 を返しました。幸い、キャッシュにこのフォールバック戦略を設定していたため、ユーザーアクセスはほぼ影響を受けませんでした —— キャッシュは数分期限切れでしたが、コンテンツは正常に返せました。バックエンドが復旧した後、キャッシュは自動的に更新されました。

実測データの比較:

指標キャッシュなしキャッシュヒットフォールバックモード
レスポンス時間150-200ms5-10ms5-10ms
バックエンド QPS1000500
ユーザー体験正常正常やや遅延
95%
キャッシュヒット率
来源: 本番環境実測

キャッシュヒット時、レスポンス時間は 200ms から 5-10ms に短縮され、約 20 倍高速化されました。このメリットは非常に直接的です。

第 3 章:接続プール設定 — 高同時接続シナリオの必須項目

gzip とキャッシュは「どうすればより速く転送できるか」を解決し、接続プールは「どうすればより多くのリクエストを処理できるか」を解決します。デフォルト設定では、単一の Nginx worker の最大同時接続数は 1024 だけです。トラフィックが増えると、すぐに不足します。

3.1 worker_connections:上限を計算する

最大同時接続数の計算式:

最大同時接続数 = worker_processes × worker_connections

サーバーが 8 コア CPU を搭載していると仮定し、worker_processes を 8(または auto で自動マッチング)、worker_connections を 4096 に設定:

最大同時接続数 = 8 × 4096 = 32768

この数字は大きく見えますが、注意点があります:各リクエストは通常 2 つの接続を占有します(クライアントから Nginx、Nginx からバックエンド)。したがって、実際に処理できる同時リクエスト数は、この数字の半分程度です。

設定は events ブロックで:

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

use epoll は Linux ではデフォルト値で、明示的に書く必要はありませんが、書いておくとより明確です。multi_accept on は worker が同時に複数の新規接続を受け入れ、高同時接続シナリオで接続のキューイングを削減します。

3.2 クライアント keepalive:接続を再利用し、オーバーヘッドを削減

TCP 接続の確立には 3 ウェイハンドシェイクが必要で、オーバーヘッドは無視できません。keepalive により、クライアントと Nginx 間の接続を再利用でき、リクエストごとに接続を再確立する必要がなくなります。

http {
    keepalive_timeout 65;
    keepalive_requests 1000;
}

keepalive_timeout 65:接続を 65 秒間維持、超過するとクローズ。この値は大きすぎるとサーバーリソースを過剰に占有し、小さすぎると再利用効果が薄れます。60-75 秒が合理的な範囲です。

keepalive_requests 1000:単一の接続で最大 1000 リクエストを処理。小さすぎると頻繁に切断され、大きすぎるとリソースリークの可能性があります。1000 はテスト結果、比較的安定している値です。

3.3 upstream keepalive:バックエンド接続プール

この設定は多くの人が知りませんが、効果は明確です。Nginx とバックエンドサービス間でも接続を再用でき、頻繁な TCP 確立のオーバーヘッドを削減できます。

upstream backend {
    server 127.0.0.1:8080;
    server 127.0.0.1:8081;

    keepalive 64;
    keepalive_timeout 60s;
    keepalive_requests 1000;
}

server {
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

keepalive 64:64 個のアイドル接続プールを維持。この値はバックエンドサービスの数に応じて調整し、一般的にバックエンドサーバー数の 4-8 倍に設定します。

proxy_http_version 1.1proxy_set_header Connection "" この 2 行は必須です。HTTP/1.1 はデフォルトで keepalive をサポート、Connection ヘッダーをクリアして初めて接続を再利用できます。この 2 行を書かないと、upstream keepalive は有効になりません。

実測効果の比較:

設定接続確立回数/分CPU オーバーヘッド推奨シナリオ
upstream keepalive なし6000低トラフィック
keepalive 323000中トラフィック
keepalive 641500高トラフィック

upstream keepalive を追加した後、接続確立オーバーヘッドは直接 50% 削減されました。この設定は高同時接続シナリオで特に重要です。

3.4 推奨パラメータクイックリファレンス

シナリオ別の推奨設定をまとめました:

パラメータ低トラフィック(1000 QPS未満)中トラフィック(1000-5000 QPS)高トラフィック(5000 QPS以上)
worker_processesautoautoauto
worker_connections102420484096
keepalive_timeout606575
keepalive_requests1005001000
upstream keepalive163264

これはあくまで出発点です。実際の調整は負荷テストデータと組み合わせる必要があります。私は一般的に wrk や ab で負荷テストを行い、接続数とレスポンス時間の変化曲線を見て、最適値を見つけます。

第 4 章:統合設定テンプレート — 本番環境で即利用可能

前の 3 章で原理を説明しました。ここでは、統合後の設定テンプレートを直接提供します。実際のシナリオに合わせてパラメータを調整できますが、フレームワークは汎用的です。

# nginx.conf 本番環境テンプレート

user nginx;
worker_processes auto;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

http {
    # gzip 圧縮設定
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_min_length 1000;
    gzip_types text/plain text/css text/xml text/javascript
               application/json application/javascript application/xml
               application/xml+rss application/xhtml+xml;
    gzip_disable "msie6";

    # キャッシュパス設定
    proxy_cache_path /var/cache/nginx
                     levels=1:2
                     keys_zone=my_cache:10m
                     max_size=10g
                     inactive=60m
                     use_temp_path=off;

    # クライアント接続設定
    keepalive_timeout 65;
    keepalive_requests 1000;

    # バックエンドサーバーグループ
    upstream backend {
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
        keepalive 64;
        keepalive_timeout 60s;
        keepalive_requests 1000;
    }

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Connection "";

            # キャッシュ設定
            proxy_cache my_cache;
            proxy_cache_valid 200 302 10m;
            proxy_cache_valid 404 1m;
            proxy_cache_key $scheme$request_method$host$request_uri;
            proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;

            # デバッグ用レスポンスヘッダー
            add_header X-Cache-Status $upstream_cache_status;
        }
    }
}

3 種類のシナリオ別設定

EC サイト:トップページと商品詳細ページは頻繁に変化するため、キャッシュ時間は短めに、10-15 分で十分。upstream keepalive は大きめに、データベースクエリが多く、バックエンドの負荷が高いため。

API サービス:データのリアルタイム性が高く、proxy_cache_valid は 1-5 分程度かもしれない。gzip は JSON に効果的、必ず追加すること。

静的サイト:HTML、CSS、JS はほぼ変化しないため、キャッシュは 1 時間以上に設定可能。gzip 圧縮のメリットが最大、静的ファイルはテキストが多いため。

第 5 章:よくある問題とトラブルシューティング

私が遭遇した高頻度の問題をいくつか、解決策とともに提示します。

Q:gzip 圧縮が有効にならない、レスポンスヘッダーに Content-Encoding: gzip がない

3 つの箇所をチェック:

  1. gzip on が正しい設定レベル(http ブロック)にあるか
  2. gzip_types にレスポンスの MIME タイプが含まれているか
  3. レスポンスサイズが gzip_min_length より大きいか

curl でテスト:curl -H "Accept-Encoding: gzip" -I http://your-site.com

Q:キャッシュヒット率が低い、X-Cache-Status がほとんど MISS

一般的な原因:

  • キャッシュキー設計が不合理、すべてのリクエストが「異なる」とみなされている
  • proxy_cache_valid が短すぎる、キャッシュが使われる前に期限切れ
  • バックエンドのレスポンスヘッダーに Cache-Control: no-cacheSet-Cookie がある

レスポンスヘッダーを確認し、キャッシュ禁止の指示がないことを確認してください。

Q:worker_connections が不足、502 エラーが発生

Nginx エラーログを確認、worker_connections are not enough があれば、同時接続数が上限を超えています。

解決策:

  1. worker_connections 値を増やす
  2. 接続リークがないか確認(keepalive 設定が不合理)
  3. サーバーを追加してロードバランシングを検討

Q:メモリ使用量が高すぎる、サーバーが頻繁に OOM

可能性のある原因:

  • proxy_cache_pathkeys_zone が大きすぎる
  • キャッシュファイルが多すぎて、メモリマッピングが高い
  • keepalive 接続プールが大きすぎて、アイドル接続がリソースを占有

これらのパラメータを適度に小さくするか、サーバーにメモリを追加してください。

おわりに

Nginx パフォーマンスチューニングの核心は 3 本柱:gzip 圧縮、キャッシュ戦略、接続プール設定。この 3 つのモジュールを適切に組み合わせれば、ウェブサイトの読み込み速度を倍増、同時接続能力を 3〜4 倍に向上させることができます。

チューニングは一度で終わるものではありません。このルートで進めることをお勧めします:

  1. まず gzip を有効に:変更が最小、メリットが最も直接的、10 分で完了
  2. 次にキャッシュを設定:ビジネスシナリオに基づいてキャッシュ戦略を設計、1 日以内に完了
  3. 最後に接続プールを調整:負荷テストで検証が必要、トラフィックが安定してから実施

変更後は必ず負荷テストで効果を検証してください。wrk や ab で、レスポンス時間、QPS、エラー率の変化を確認します。感覚で調整せず、データで語りましょう。

最後にチェックリスト、照らし合わせて実行してください:

  • gzip が有効、MIME タイプ設定が完全
  • gzip_comp_level を 4-6 に設定、CPU と圧縮率のバランス
  • proxy_cache_path が設定済み、キャッシュサイズが合理的
  • proxy_cache_valid がビジネスシナリオに基づいて設定済み
  • proxy_cache_use_stale フォールバック戦略が設定済み
  • worker_connections を 4096 以上に設定
  • keepalive_timeout を 60-75 秒に設定
  • upstream keepalive が設定済み(HTTP/1.1 と Connection ヘッダーを含む)
  • レスポンスヘッダーに X-Cache-Status を含めデバッグに使用

だいたいこれで完了です。質問があればコメント欄にどうぞ、できる限り返信します。

Nginx パフォーマンスチューニング設定フロー

gzip 圧縮、キャッシュ戦略、接続プール最適化の本番環境設定を 3 ステップで完了

⏱️ 目安時間: 30 分

  1. 1

    ステップ1: gzip 圧縮を有効化

    http ブロックに設定を追加:

    • gzip on; 圧縮を有効化
    • gzip_vary on; Vary レスポンスヘッダーを追加
    • gzip_comp_level 6; 圧縮レベル設定(推奨 4-6)
    • gzip_min_length 1000; 1KB 未満は圧縮しない
    • gzip_types で MIME タイプを指定:text/plain text/css application/json application/javascript
  2. 2

    ステップ2: proxy_cache キャッシュを設定

    2 ステップで設定:

    ステップ 1:キャッシュパスを定義
    • proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m

    ステップ 2:location でキャッシュを有効化
    • proxy_cache my_cache;
    • proxy_cache_valid 200 10m; 正常レスポンスは 10 分間キャッシュ
    • proxy_cache_use_stale error timeout http_502; フォールバック戦略を設定
  3. 3

    ステップ3: 接続プールパラメータを最適化

    3 つの重要な設定:

    • worker_connections 4096; events ブロックで設定
    • keepalive_timeout 65; keepalive_requests 1000; http ブロックで設定
    • upstream keepalive 64; proxy_http_version 1.1; proxy_set_header Connection ""; バックエンド接続プール設定

FAQ

gzip 圧縮レベルはどれくらいが適切?
レベル 4-6 を推奨。レベル 4 は圧縮率 72%、CPU 消費が低く、CPU に余裕がないシナリオに適しています。レベル 6 は圧縮率 75%、帯域幅をより節約し、帯域幅に余裕がないシナリオに適しています。レベル 9 は CPU 消費が倍増しますが、圧縮率の向上は限定的で、推奨しません。
proxy_cache と fastcgi_cache、どっちを選ぶ?
バックエンドの技術スタックによります:Node.js、Python、Go などのアプリケーションサーバーなら proxy_cache、PHP-FPM なら fastcgi_cache を使用します。両者の設定ロジックはほぼ同じで、核心パラメータはいずれもキャッシュパス、有効期間、キャッシュキー設計です。
worker_connections はどれくらいに設定すべき?
CPU コア数とトラフィック予測に基づいて:低トラフィック(1000 QPS未満)なら 1024、中トラフィック(1000-5000 QPS)なら 2048、高トラフィック(5000 QPS以上)なら 4096。実際の同時接続能力 = worker_processes × worker_connections ÷ 2。
キャッシュヒット率が低い場合の調査方法は?
3 ステップで調査:1. キャッシュキーが合理的か確認、すべてのリクエストが異ならないようにする。2. proxy_cache_valid の期間が短すぎないか確認。3. バックエンドのレスポンスヘッダーに Cache-Control: no-cache や Set-Cookie がないか確認し、キャッシュを禁止していないか確認。
upstream keepalive で HTTP/1.1 の設定が必須な理由は?
HTTP/1.0 はデフォルトで keepalive をサポートしていません。HTTP/1.1 が必要で、初めてバックエンド接続を再利用できます。同時に proxy_set_header Connection "" で Connection ヘッダーをクリアする必要があります。そうしないと Nginx が接続をクローズし、upstream keepalive が有効になりません。
ビジネスシナリオ別のキャッシュ時間の設定は?
EC サイトのトップページと商品詳細ページは頻繁に変化するため、10-15 分間キャッシュ。API サービスはリアルタイム性が高く、1-5 分間キャッシュ。静的サイトの HTML/CSS/JS はほぼ変化しないため、1 時間以上キャッシュ。核心原則:ビジネスの変化が速いほど、キャッシュ時間は短く。

8 min read · 公開日: 2026年4月11日 · 更新日: 2026年4月11日

コメント

GitHubアカウントでログインしてコメントできます

関連記事