PUROGU LADESU

ポエムがメインのブログです。

postgresql パフォーマンス状態 レスポンス調査

パフォーマンス状態

  1. 今何が起きているか(リアルタイム)

  2. pg_stat_activity — 現在実行中のクエリ、接続数、ロック待ちなどを確認

SELECT pid, usename, state, query, now() - query_start AS duration
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY duration DESC;
  • 長時間実行中のクエリを見つける
SELECT pid, now() - pg_stat_activity.query_start AS duration, query
FROM pg_stat_activity
WHERE (now() - pg_stat_activity.query_start) > interval '5 minutes';
  • ロック待ちの確認
SELECT * FROM pg_locks WHERE NOT granted;
  1. クエリ単位の統計(拡張機能が必要)

pg_stat_statements — どのクエリが遅い/頻繁に呼ばれているかを集計。デフォルトでは無効なので postgresql.conf で shared_preload_libraries = 'pg_stat_statements' を設定して再起動が必要です。

CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

SELECT query, calls, total_exec_time, mean_exec_time, rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
  1. テーブル・インデックスの使用状況

pg_stat_user_tables — シーケンシャルスキャンが多発していないかなど

SELECT relname, seq_scan, idx_scan, n_live_tup, n_dead_tup
FROM pg_stat_user_tables
ORDER BY seq_scan DESC;

pg_stat_user_indexes — 使われていないインデックスの発見

SELECT relname, indexrelname, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0;
  1. 個別クエリの実行計画
EXPLAIN (ANALYZE, BUFFERS) SELECT ...;

BUFFERS を付けるとキャッシュヒット率(shared hit/read)も見えるので、実際にディスクI/Oが発生しているか判断できます。

  1. データベース全体のヒット率
SELECT datname,
       blks_hit::float / nullif(blks_hit + blks_read, 0) AS cache_hit_ratio
FROM pg_stat_database;

99%前後が目安。低い場合は shared_buffers の見直しなどを検討します。

  1. デッドタプル系(バキュームの目安)

デッドタプルの割合が10〜20%を超えてきたら要注意。更新頻度の高いテーブルは自動バキュームの頻度を調整する。

SELECT schemaname, relname, n_dead_tup, n_live_tup,
       ROUND((n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0)) * 100, 2) AS dead_tuple_percent,
       last_autovacuum, autovacuum_count
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;

レスポンス調査

  1. いつ・どこで悪化したかを特定 急激な悪化か、じわじわの悪化かで原因が変わります 急激: デプロイ、統計情報の陳腐化、ロック競合、突発的な負荷増加、インデックス破損など じわじわ: テーブル肥大化(bloat)、インデックス肥大化、autovacuum不足、データ量増加による実行計画の変化など
-- pg_stat_database でリセット以降の傾向を確認(定期的にスナップショットを取っておくと比較しやすい)
SELECT datname, xact_commit, xact_rollback, blks_read, blks_hit,
       tup_returned, tup_fetched, deadlocks, temp_files, temp_bytes
FROM pg_stat_database;

temp_files / temp_bytes が増えていたら、work_mem 不足でソートやハッシュがディスクに溢れている可能性があります。

  1. 今まさに何が起きているか
-- 実行中クエリとその経過時間
SELECT pid, usename, state, wait_event_type, wait_event,
       now() - query_start AS duration, query
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY duration DESC;

wait_event が埋まっていれば、CPUではなく「何かを待っている」状態です。よくあるパターン:

Lock 系 → ロック競合(ステップ3へ) IO 系 → ディスクI/Oがボトルネック Client 系 → アプリ側がクエリ結果を受け取れていない(ネットワークやアプリ側の問題)

  1. ロック競合の確認
SELECT blocked_locks.pid AS blocked_pid,
       blocked_activity.query AS blocked_query,
       blocking_locks.pid AS blocking_pid,
       blocking_activity.query AS blocking_query
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks
    ON blocking_locks.locktype = blocked_locks.locktype
    AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;

長時間トランザクションが居座っていると、他のクエリを巻き込んで全体が遅くなることがよくあります。

  1. 特定のクエリが遅いのか、全体が遅いのか

pg_stat_statements が有効なら、直近で mean_exec_time が上がっているクエリを探します。

SELECT query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 20;

特定クエリが遅い場合は EXPLAIN (ANALYZE, BUFFERS) で実行計画を確認 → インデックスが使われていない、統計が古い(ANALYZE していない)、行数見積もりが大きくズレている、などをチェック。

  1. bloat(肥大化)・autovacuumの状態
SELECT relname, n_live_tup, n_dead_tup,
       round(n_dead_tup::numeric / nullif(n_live_tup + n_dead_tup, 0) * 100, 2) AS dead_ratio,
       last_autovacuum, last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;

dead tupleの比率が高い、または last_autovacuum が古い場合は、autovacuumが追いついていない可能性があります(頻繁なUPDATE/DELETEが多いテーブルで起きやすい)。

  1. リソース面(OS/インフラ側)

DB内部だけでなく、サーバー側も確認:

CPU使用率、ディスクI/O(iostatなど) メモリ、swap発生の有無 ディスク容量(残り少ないと突然遅くなることがある) コネクション数が max_connections に近づいていないか

postgresqlロック調査

regclass は、PostgreSQLのオブジェクト(テーブル・インデックスなど)を識別するための特殊なデータ型です。 ::regclassでOIDに変換することが出来る

基本

-- modeでロック状態確認、grantedがfalseだと待っている

SELECT * FROM pg_locks;

-- stateでプロセスの状態確認、 query, application_nameも原因特定情報になる

SELECT * FROM pg_stat_activity;

テーブルaccountsがロックされているか

-- granted = false の行が「ロック待ちしている」ことを示します。
SELECT pid, locktype, relation::regclass, mode, granted
FROM pg_locks
WHERE relation = 'accounts'::regclass;

誰が何をロックしているか(実用版)

-- granted = false の行が「ロック待ちしている」ことを示します。
SELECT
    pl.pid,
    pa.usename,
    pa.query,
    pl.locktype,
    pl.mode,
    pl.granted,
    pc.relname AS table_name
FROM pg_locks pl
JOIN pg_stat_activity pa ON pl.pid = pa.pid
LEFT JOIN pg_class pc ON pl.relation = pc.oid
WHERE pl.pid != pg_backend_pid()  -- 自分自身は除外
ORDER BY pl.granted, pl.pid;

誰が誰をブロックしているか(最重要)

-- granted = false の行が「ロック待ちしている」ことを示します。
SELECT
    blocked_locks.pid AS blocked_pid,
    blocked_activity.query AS blocked_query,
    blocking_locks.pid AS blocking_pid,
    blocking_activity.query AS blocking_query,
    blocked_locks.granted
FROM pg_locks blocked_locks
JOIN pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_locks blocking_locks
    ON blocking_locks.locktype = blocked_locks.locktype
    AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database
    AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
    AND blocking_locks.pid != blocked_locks.pid
    AND blocking_locks.granted
JOIN pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;

長時間実行中のクエリ

-- idle in transactionはバグなので強制終了する必要あり
SELECT pid, usename, state, wait_event_type, wait_event,
       xact_start, -- transaction開始時間
       now() - xact_start AS xact_age,
       now() - state_change AS state_age,
       now() - query_start AS query_age,
       query,
       application_name
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY xact_start;

どのプロセスか調べる

SELECT pid,
       usename,        -- 接続しているDBユーザー
       datname,        -- 接続先データベース
       application_name, -- 接続元アプリ名(psql, アプリ名など)
       client_addr,    -- 接続元IPアドレス
       state,          -- active / idle / idle in transaction など
       wait_event_type, -- Lock待ち / Client待ち ..
       query,          -- 直近または実行中のSQL文
       query_start,    -- そのクエリの開始時刻
       xact_start,     -- トランザクションの開始時刻
       backend_start   -- 接続自体の開始時刻
FROM pg_stat_activity
WHERE pid = 22728;  -- 調べたいPID

ブロックされているプロセスを取得

-- 誰が(pid)誰に(blocked_by)何時間(duration)ブロックされているか
SELECT pid, 
       state,
       query, 
       pg_blocking_pids(pid) AS blocked_by,
       now() - xact_start AS duration
FROM pg_stat_activity
WHERE cardinality(pg_blocking_pids(pid)) > 0;

強制解除

SELECT pg_terminate_backend(12345);

設定変更

-- 画面処理系とバッチ処理系はロールを分ける
statement_timeout   1本のクエリが異常に長時間実行され続けること
idle_in_transaction_session_timeout ロックの原因側(A)を、放置状態から強制的に片付ける
lock_timeout    待たされている側(B)を、永遠の待機から解放する

-- Webアプリ用ユーザーだけに恒久的に適用
ALTER ROLE web_app_user SET lock_timeout = '5s';
ALTER ROLE web_app_user SET statement_timeout = '30s';
ALTER ROLE web_app_user SET idle_in_transaction_session_timeout = '60s';

-- サーバー全体に適用(要リロード)
ALTER SYSTEM SET lock_timeout = '5s';
-- ALTER SYSTEM RESET lock_timeout
SELECT pg_reload_conf();

設定確認(全体/conf)

-- sourceを見ればどこの設定が効いているか分かる
SELECT name, setting, source, context
FROM pg_settings
WHERE name IN ('lock_timeout', 'statement_timeout', 'idle_in_transaction_session_timeout', 'deadlock_timeout', 'log_lock_waits', 'log_min_duration_statement');

設定確認(ロール)

SELECT * FROM pg_db_role_setting;

AutoHotKey2

ChangeKeyV2.ahk

; AutoHotkey
; Remapping the Keyboard
; https://www.autohotkey.com/docs/v2/KeyList.htm
; https://www.autohotkey.com/docs/v2/Send.htm

; 無変換+JIKLで矢印キーの動作
vk1D & i::Send("{Blind}{Up}")
vk1D & j::Send("{Blind}{Left}")
vk1D & k::Send("{Blind}{Down}")
vk1D & l::Send("{Blind}{Right}")

; 無変換+Aで前の単語、無変換+;で次の単語の動作(Ctrlを付ける)
vk1D & h::Send("{Blind}^{Left}")
vk1D & vkBB::Send("{Blind}^{Right}")

; 無変換+GでHOME、無変換+:でENDの動作
vk1D & g::Send("{Blind}{Home}")
vk1D & vkBA::Send("{Blind}{End}")
vk1D & n::Send("{Blind}{Home}")
vk1D & vkBF::Send("{Blind}{End}")

; 無変換+OでBackSpace、無変換+UでReturnの動作
vk1D & o::Send("{Blind}{BS}")
vk1D & u::Send("{Blind}{Enter}")

; 無変換を全角半角にする
vk1D::Send("{Blind}{vkF3}")

; CapsLockで数字入力(解除はCapsLock+Shift)
; sc03A & sc079::Send(".")
; sc03A & Space::Send("0")
; sc03A & m::Send("1")
; sc03A & vkBC::Send("2")
; sc03A & vkBE::Send("3")
; sc03A & j::Send("4")
; sc03A & k::Send("5")
; sc03A & l::Send("6")
; sc03A & u::Send("7")
; sc03A & i::Send("8")
; sc03A & o::Send("9")

; Win+tでvk確認
; #t::
; {
;     key  := "/"
;     name := GetKeyName(key)
;     vk   := GetKeyVK(key)
;     sc   := GetKeySC(key)
;     MsgBox(Format("Name:`t{}`nVK:`t{:X}`nSC:`t{:X}", name, vk, sc))
; }

iTunesでiPhoneをバックアップせずに同期する方法|簡単な手順で時間を節約!

iTunesを使ってiPhoneの音楽や写真を同期する際、毎回バックアップが始まって時間がかかる…
そんな経験はありませんか?

バックアップは重要ですが、

> とりあえず同期だけしたい!

という場合もありますよね。

今回は、**iTunesでバックアップを行わずに、同期だけ行う方法**をご紹介します。
とても簡単なので、ぜひ覚えておいてください。

■ 手順

  1. iTunesで「同期」を開始する
  2. iPhone側でロック解除を求められる
  3. iPhoneでパスコードを入力せず「キャンセル」を押す
  4. iTunes側でも「キャンセル」を押す
  5. バックアップがスキップされ同期のみ実行される

> iPhone側とiTunes側の両方で「キャンセル」するのがポイントです。

■ まとめ

iTunesでのバックアップは便利ですが、急いでいる時や
同期だけすぐに行いたい時には今回の方法が非常に便利です。

ちょっとした操作でスムーズに作業できますので、ぜひ活用してみてください!

Macbookが熱暴走する問題

何かよくわからないが、夜の20時ごろになるとMacbookProのファンが轟音を立てて周りだし、
バッテリーもみるみるうちに減っていく。
キーボードの上部に触れると熱くて触れなくなる。
このまま爆発してしまうのではないかといつも不安だ。

で、どうやらこれがchromeのせいではないかという情報を見かけたので、
設定をいじってみたところ改善が見られた。

設定 > システム > グラフィックアクセラレーションが使用可能な場合は使用する
をオフにする。

これで今までのような減少は起きなくなった。

やはりMacは難しい。Macは簡単とか言ってる人たち全く意味がわからない。
5年くらい使ってるがいろんなサードパティーソフトを入れて調整しないと使いにくいです。
設定画面もVenturaになってからようやくiOSライクになって見やすくなったが、
逆にiOSのまんまでPCに最適化されていない。
Windowsは10,11になってかなり洗練されています。

ちなみに使用MacMacbook Pro 2020 13inchです。

タッチバーが気に食わなかったが、Airの熱暴走も激しかったので致し方なく。
予想どおり次のモデルでは廃止になりました。
上司から言われて仕方なく作った感が至るところにあります。

キーボードもこのモデルより前のはペラペラで打ちにくかった。

あとこのアルミボディ。冬は最初冷たくて触りたくないです。
熱暴走すると熱すぎてさわれません。樹脂製にして欲しい。

気に入ってるところは、スピーカーの音質くらいかな。

脱線しました。

【SpringBoot】Lombokの有効化

IntelliJを使用します。

1. プラグインをインストール

設定 -> プラグイン -> lombok -> install

これでsetterがインテリセンスで有効になります。

2. Eneble Annotation Processingを有効化

Build, Execution, Deployment -> Compiler -> Annotation Processers

EnableAnnotationProcessing をチェック


IntelliJ IDEAにLombokを設定する #Java - Qiita

【SpringBoot】メッセージを外出しする

intelliJを使います。

1. 文字化け対策
???となってしまい読み込めないので設定を変更する。

以下の設定にチェックを入れる
設定 > エディター > ファイルエンコーディング > ネイティブコードからASCIIコードへの自動変換を行う

2. 多言語化ファイルが読み込めない

・application.propertiesに設定を追加

spring.messages.basename=i18n/messages

i18nフォルダにmessages_ja.propertiesの形式でファイルを配置すると認識する。

・デフォルトのpropertiesファイルを設置する

basenameで指定したフォルダに、messages.propertiesファイルを作成する。中身は空で良い。

これがないとmessageSource.getMessageで指定したロケールのメッセージが取得できない。

というか中身を辛煮するのでなく、デフォルトの言語をこれに設定すべき。