Reduce Supabase disk IO

This commit is contained in:
2569718930@qq.com
2026-05-29 17:22:33 +08:00
parent 9e3a4e2f45
commit 2269aaefd7
63 changed files with 4637 additions and 410 deletions
+98
View File
@@ -0,0 +1,98 @@
-- Supabase Disk IO diagnostics for PolyWeather.
-- Run sections independently if your project does not expose every stats view.
-- 1) Tables with high sequential scan pressure.
select
schemaname,
relname,
seq_scan,
seq_tup_read,
idx_scan,
n_tup_ins,
n_tup_upd,
n_tup_del,
n_dead_tup
from pg_stat_user_tables
where schemaname = 'public'
order by seq_tup_read desc
limit 30;
-- 2) Largest public tables and indexes.
select
relname,
pg_size_pretty(pg_total_relation_size(relid)) as total_size,
pg_size_pretty(pg_relation_size(relid)) as table_size,
pg_size_pretty(pg_indexes_size(relid)) as index_size
from pg_catalog.pg_statio_user_tables
where schemaname = 'public'
order by pg_total_relation_size(relid) desc
limit 30;
-- 3) Slow/high-read statements, if pg_stat_statements is available.
select
calls,
round(total_exec_time::numeric, 2) as total_exec_ms,
round(mean_exec_time::numeric, 2) as mean_exec_ms,
rows,
shared_blks_read,
shared_blks_hit,
temp_blks_read,
temp_blks_written,
left(query, 500) as query_sample
from pg_stat_statements
order by shared_blks_read desc, total_exec_time desc
limit 30;
-- 4) Dead tuple pressure that can trigger heavier autovacuum work.
select
schemaname,
relname,
n_live_tup,
n_dead_tup,
last_vacuum,
last_autovacuum,
last_analyze,
last_autoanalyze
from pg_stat_user_tables
where schemaname = 'public'
order by n_dead_tup desc
limit 30;
-- 5) Indexes that still read the most disk blocks.
select
s.schemaname,
s.relname,
s.indexrelname,
s.idx_scan,
s.idx_tup_read,
s.idx_tup_fetch,
io.idx_blks_read,
io.idx_blks_hit,
pg_size_pretty(pg_relation_size(s.indexrelid)) as index_size
from pg_stat_user_indexes s
join pg_statio_user_indexes io
on io.indexrelid = s.indexrelid
where s.schemaname = 'public'
order by io.idx_blks_read desc, s.idx_scan desc
limit 50;
-- 6) Large non-unique indexes with no recorded scans since stats reset.
-- Treat this as a candidate list only; confirm with production traffic history
-- before dropping anything.
select
s.schemaname,
s.relname,
s.indexrelname,
s.idx_scan,
pg_size_pretty(pg_relation_size(s.indexrelid)) as index_size,
i.indisunique,
i.indisprimary
from pg_stat_user_indexes s
join pg_index i
on i.indexrelid = s.indexrelid
where s.schemaname = 'public'
and s.idx_scan = 0
and not i.indisprimary
and not i.indisunique
order by pg_relation_size(s.indexrelid) desc
limit 30;
+89
View File
@@ -0,0 +1,89 @@
-- PolyWeather Supabase Disk IO mitigation indexes.
-- Run this in the Supabase SQL Editor for an existing production project.
drop index if exists public.idx_subscriptions_user_status_expiry;
create index if not exists idx_subscriptions_user_status_expiry
on public.subscriptions(user_id, expires_at desc)
include (id, starts_at, plan_code, source)
where status = 'active';
drop index if exists public.idx_profiles_email;
create index if not exists idx_profiles_email
on public.profiles(email)
include (id);
drop index if exists public.idx_profiles_id_lookup;
create index if not exists idx_profiles_id_lookup
on public.profiles(id)
include (email, created_at);
drop index if exists public.idx_subscriptions_status_expiry;
create index if not exists idx_subscriptions_status_expiry
on public.subscriptions(expires_at asc)
include (user_id, starts_at, plan_code)
where status = 'active';
drop index if exists public.idx_subscriptions_user_created;
create index if not exists idx_subscriptions_user_created
on public.subscriptions(user_id, created_at desc)
include (id, status, plan_code, source, starts_at, expires_at, updated_at);
drop index if exists public.idx_payments_created_at;
create index if not exists idx_payments_created_at
on public.payments(created_at desc)
include (id, user_id, amount, currency, chain, tx_hash, status);
drop index if exists public.idx_user_wallets_user_chain;
create index if not exists idx_user_wallets_user_chain
on public.user_wallets(user_id, chain_id, is_primary desc, verified_at desc)
include (id, address)
where status = 'active';
drop index if exists public.idx_user_wallets_chain_address_owner;
create index if not exists idx_user_wallets_chain_address_owner
on public.user_wallets(chain_id, address)
include (user_id, status);
drop index if exists public.idx_wallet_link_challenges_lookup;
drop index if exists public.idx_payment_intents_user_status;
drop index if exists public.idx_payment_intents_status_updated;
create index if not exists idx_payment_intents_status_updated
on public.payment_intents(status, updated_at desc)
include (user_id)
where status in ('submitted', 'confirmed');
create index if not exists idx_payment_intents_user_status_updated
on public.payment_intents(user_id, status, updated_at desc);
drop index if exists public.idx_payment_intents_submitted_tx_updated;
create index if not exists idx_payment_intents_submitted_tx_updated
on public.payment_intents(updated_at asc)
include (id, user_id, tx_hash, chain_id)
where status = 'submitted' and tx_hash is not null;
create index if not exists idx_payment_intents_user_created
on public.payment_intents(user_id, created_at desc);
drop index if exists public.idx_payment_intents_tx_hash;
create index if not exists idx_payment_intents_tx_hash
on public.payment_intents(tx_hash)
include (id, user_id)
where tx_hash is not null;
create index if not exists idx_payment_transactions_intent
on public.payment_transactions(intent_id, created_at desc);
drop index if exists public.idx_payment_transactions_tx_hash_intent;
create index if not exists idx_payment_transactions_tx_hash_intent
on public.payment_transactions(tx_hash)
include (intent_id);
analyze public.subscriptions;
analyze public.profiles;
analyze public.payments;
analyze public.user_wallets;
analyze public.wallet_link_challenges;
analyze public.payment_intents;
analyze public.payment_transactions;
+53 -8
View File
@@ -12,6 +12,14 @@ create table if not exists public.profiles (
updated_at timestamptz not null default now()
);
create index if not exists idx_profiles_email
on public.profiles(email)
include (id);
create index if not exists idx_profiles_id_lookup
on public.profiles(id)
include (email, created_at);
create table if not exists public.subscriptions (
id bigserial primary key,
user_id uuid not null references auth.users(id) on delete cascade,
@@ -25,7 +33,18 @@ create table if not exists public.subscriptions (
);
create index if not exists idx_subscriptions_user_status_expiry
on public.subscriptions(user_id, status, expires_at desc);
on public.subscriptions(user_id, expires_at desc)
include (id, starts_at, plan_code, source)
where status = 'active';
create index if not exists idx_subscriptions_status_expiry
on public.subscriptions(expires_at asc)
include (user_id, starts_at, plan_code)
where status = 'active';
create index if not exists idx_subscriptions_user_created
on public.subscriptions(user_id, created_at desc)
include (id, status, plan_code, source, starts_at, expires_at, updated_at);
create table if not exists public.payments (
id bigserial primary key,
@@ -40,6 +59,10 @@ create table if not exists public.payments (
updated_at timestamptz not null default now()
);
create index if not exists idx_payments_created_at
on public.payments(created_at desc)
include (id, user_id, amount, currency, chain, tx_hash, status);
create table if not exists public.entitlement_events (
id bigserial primary key,
user_id uuid not null references auth.users(id) on delete cascade,
@@ -65,7 +88,13 @@ create table if not exists public.user_wallets (
);
create index if not exists idx_user_wallets_user_chain
on public.user_wallets(user_id, chain_id, status, is_primary desc, verified_at desc);
on public.user_wallets(user_id, chain_id, is_primary desc, verified_at desc)
include (id, address)
where status = 'active';
create index if not exists idx_user_wallets_chain_address_owner
on public.user_wallets(chain_id, address)
include (user_id, status);
create table if not exists public.wallet_link_challenges (
id bigserial primary key,
@@ -79,9 +108,6 @@ create table if not exists public.wallet_link_challenges (
created_at timestamptz not null default now()
);
create index if not exists idx_wallet_link_challenges_lookup
on public.wallet_link_challenges(user_id, chain_id, address, nonce, created_at desc);
create table if not exists public.payment_intents (
id uuid primary key default gen_random_uuid(),
user_id uuid not null references auth.users(id) on delete cascade,
@@ -103,11 +129,26 @@ create table if not exists public.payment_intents (
updated_at timestamptz not null default now()
);
create index if not exists idx_payment_intents_user_status
on public.payment_intents(user_id, status, created_at desc);
create index if not exists idx_payment_intents_status_updated
on public.payment_intents(status, updated_at desc)
include (user_id)
where status in ('submitted', 'confirmed');
create index if not exists idx_payment_intents_user_status_updated
on public.payment_intents(user_id, status, updated_at desc);
create index if not exists idx_payment_intents_submitted_tx_updated
on public.payment_intents(updated_at asc)
include (id, user_id, tx_hash, chain_id)
where status = 'submitted' and tx_hash is not null;
create index if not exists idx_payment_intents_user_created
on public.payment_intents(user_id, created_at desc);
create index if not exists idx_payment_intents_tx_hash
on public.payment_intents(tx_hash);
on public.payment_intents(tx_hash)
include (id, user_id)
where tx_hash is not null;
create table if not exists public.payment_transactions (
id bigserial primary key,
@@ -128,6 +169,10 @@ create table if not exists public.payment_transactions (
create index if not exists idx_payment_transactions_intent
on public.payment_transactions(intent_id, created_at desc);
create index if not exists idx_payment_transactions_tx_hash_intent
on public.payment_transactions(tx_hash)
include (intent_id);
create or replace function public.sync_profile_from_auth()
returns trigger
language plpgsql