一、大表分区
二、创建索引
创建评估
sql
-- 检查查询计划,看是否需要索引
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123;
-- 检查列的基数(不同值的数量)
SELECT COUNT(DISTINCT user_id) FROM orders;
SELECT COUNT(DISTINCT status) FROM orders; -- 低基数列可能不适合索引
创建索引
0、基本语法
sql
-- 基本语法
CREATE [UNIQUE] INDEX [CONCURRENTLY] [索引名] ON 表名 [USING 索引类型] (列名 [选项]);
-- 最简单的形式
CREATE INDEX idx_users_email ON users(email);
1、B-tree索引(默认)
sql
-- 单列索引
CREATE INDEX idx_orders_user_id ON orders(user_id);
-- 多列索引(复合索引)
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
-- 唯一索引,也可多列
CREATE UNIQUE INDEX idx_users_email_unique ON users(email);
-- 带条件的部分索引
CREATE INDEX idx_active_users ON users(email) WHERE status = 'active';
2、其他索引
sql
-- Hash索引(等值查询)
CREATE INDEX idx_users_id_hash ON users USING HASH (id);
-- GIN索引(全文搜索、数组、JSONB)
CREATE INDEX idx_products_tags ON products USING GIN (tags);
CREATE INDEX idx_docs_content ON documents USING GIN (to_tsvector('english', content));
-- GiST索引(地理空间、范围类型)
CREATE INDEX idx_locations_coords ON locations USING GIST (coords);
-- BRIN索引(超大型表,数据天然有序)
CREATE INDEX idx_logs_created_at ON logs USING BRIN (created_at);
3、高级选项
sql
-- 普通创建索引会锁表,阻止写操作
-- 并发创建允许在创建索引时继续读写
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
-- 为频繁更新的列预留空间
CREATE INDEX idx_products_stock ON products(stock) WITH (fillfactor = 70);
-- 将索引创建在特定表空间(如SSD盘)
CREATE INDEX idx_large_table_date ON large_table(created_at) TABLESPACE fast_ssd;
查看索引
sql
-- 查看表上的所有索引
SELECT * FROM pg_indexes WHERE tablename = 'users';
-- 更详细的信息(包括索引大小)
SELECT
i.relname as index_name,
pg_size_pretty(pg_relation_size(i.oid)) as index_size,
idx_scan as index_scans
FROM pg_class t
JOIN pg_index ON t.oid = pg_index.indrelid
JOIN pg_class i ON i.oid = pg_index.indexrelid
WHERE t.relname = 'users';
删除索引
sql
-- 删除索引
DROP INDEX idx_users_email;
-- 如果索引存在则删除(避免报错)
DROP INDEX IF EXISTS idx_users_email;
-- PostgreSQL 14+ 支持并发删除索引(不阻塞读写)
DROP INDEX CONCURRENTLY idx_users_email;
-- 删除依赖该索引的对象(如使用了该索引的约束)
DROP INDEX idx_users_email CASCADE;
三、预计算之创建物化视图
与普通视图不同,物化视图实际存储了结果数据,PostgreSQL会将其视为一个独立的、只读的表,因此,直接查询物化视图速度更快,另外,可以在其上创建索引来加速针对该物化视图的查询。而且,即使创建物化视图的源表上有索引,这些索引也不会自动传递给物化视图,必须为物化视图单独创建索引。当对应的,普通视图只是一个逻辑映射,它本身没有物理存储,每次查询时都要实时执行定义普通视图时的sql语句。为普通视图编写的查询,其性能取决于视图定义查询语句和底层物理表的数据量及索引。如果底层表有合适的索引,查询视图时就能用上。当然,物化视图也有缺点:1、数据通常是定时刷新,其新鲜度取决于刷新频率,一般无法获得最新鲜的数据;2、postgresql本身并不提供调度工具,需要第三发的调度器去刷新数据;
sql
-- 创建物化视图
CREATE MATERIALIZED VIEW IF NOT EXISTS v_scene_site_cell
AS
-- 23万 虚拟子表,提取"场景类型1"、"场景名称"、"基站号_扇区号"
select distinct ts.f_first_type, f_scene_name, f_site_cell_id
-- 3.5万(场景信息【静态表】)
from t_ten_scene ts
-- 37.5万(场景~扇区映射【静态表】)
left join t_ten_scene_sector tss on ts.f_id = tss.f_ten_scene_id
left join (
-- 31.6万(4G扇区信息【静态表】)
select f_site_id || '_' || f_cell_id f_site_cell_id, f_id from t_lte_sector where f_delete_flag is null
union
-- 37.1万(5G扇区信息【静态表】)
select f_site_id || '_' || f_cell_id f_site_cell_id, f_id from t_five_sector where f_delete_flag is null
) ss on tss.f_sector_id = ss.f_id
where f_site_cell_id is not null;
SELECT * from v_scene_site_cell;
sql
-- 先创建唯一索引
CREATE UNIQUE INDEX idx_uniq_scene_site_cell ON v_scene_site_cell(f_first_type, f_scene_name, f_site_cell_id);
-- 再进行并发刷新
REFRESH MATERIALIZED VIEW CONCURRENTLY v_scene_site_cell;
sql
-- 创建索引用于查询加速
CREATE INDEX idx_scene_site_cell_site_cell_id ON v_scene_site_cell(f_site_cell_id)
四、使用缓存
包括本地缓存和远程缓存(如redis)。使用缓存的有点是:查询速度极快。缺点是:1、当第一次查询或缓存过期后需要重新查库,速度较慢;2、缓存通常是定时更新,无法查到最新鲜的数据;3、本地缓存空间资源比较有限,不适合大数据量;