yum install -y vim lrzsz tree wget gcc gcc-c++ readline-devel zlib-devel
进入/usr/local/目录下:cd /usr/local
下载 tar 包:curl -O https://ftp.postgresql.org/pub/source/v16.2/postgresql-16.2.tar.gz
解压:tar -xzvf postgresql-16.2.tar.gz
编译安装:
1 2 3 4 5
cd /usr/local/postgresql-16.2 ./configure --prefix=/usr/local/pgsql-16.2 # /usr/local/pgsql-16.2 为安装目录 make && make install # Two thousand years later,出现「PostgreSQL installation complete.」代表安装成功
test -x $DAEMON || { echo"$DAEMON not found" if [ "$1" = "stop" ] thenexit 0 elseexit 5 fi }
if [ -e "$PG_OOM_ADJUST_FILE" -a -n "$PG_CHILD_OOM_SCORE_ADJ" ] then DAEMON_ENV="PG_OOM_ADJUST_FILE=$PG_OOM_ADJUST_FILE PG_OOM_ADJUST_VALUE=$PG_CHILD_OOM_SCORE_ADJ" fi
declare begin case i_mode when'INVALID'thenreturn0; when'AccessShareLock'thenreturn1; when'RowShareLock'thenreturn2; when'RowExclusiveLock'thenreturn3; when'ShareUpdateExclusiveLock'thenreturn4; when'ShareLock'thenreturn5; when'ShareRowExclusiveLock'thenreturn6; when'ExclusiveLock'thenreturn7; when'AccessExclusiveLock'thenreturn8; elsereturn0; endcase; end;
$$ language plpgsql strict;
with t_wait as (select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.objid,a.objsubid, a.pid,a.virtualtransaction,a.virtualxid,a,transactionid,b.query,b.xact_start,b.query_start, b.usename,b.datname from pg_locks a,pg_stat_activity b where a.pid=b.pid andnot a.granted), t_run as (select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.objid,a.objsubid, a.pid,a.virtualtransaction,a.virtualxid,a,transactionid,b.query,b.xact_start,b.query_start, b.usename,b.datname from pg_locks a,pg_stat_activity b where a.pid=b.pid and a.granted) select r.locktype,r.mode r_mode,r.usename r_user,r.datname r_db,r.relation::regclass,r.pid r_pid, r.page r_page,r.tuple r_tuple,r.xact_start r_xact_start,r.query_start r_query_start, now()-r.query_start r_locktime,r.query r_query,w.mode w_mode,w.pid w_pid,w.page w_page, w.tuple w_tuple,w.xact_start w_xact_start,w.query_start w_query_start, now()-w.query_start w_locktime,w.query w_query from t_wait w,t_run r where r.locktype isnotdistinctfrom w.locktype and r.database isnotdistinctfrom w.database and r.relation isnotdistinctfrom w.relation and r.page isnotdistinctfrom w.page and r.tuple isnotdistinctfrom w.tuple and r.classid isnotdistinctfrom w.classid and r.objid isnotdistinctfrom w.objid and r.objsubid isnotdistinctfrom w.objsubid and r.transactionid isnotdistinctfrom w.transactionid and r.pid <> w.pid orderby f_lock_level(w.mode)+f_lock_level(r.mode) desc,r.xact_start;
现在可以排在前面的就是锁级别高的等待,优先干掉这个。
-[ RECORD 1 ]-+----------------------------------------------------------
select indexrelname, pg_size_pretty(pg_relation_size(indexrelid)) as size from pg_stat_user_indexes where schemaname='public'orderby pg_relation_size('public'||'.'||indexrelname) desc;
SELECT pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size, pg_size_pretty(pg_indexes_size(c.oid)) AS index_size, pg_size_pretty(pg_total_relation_size(c.oid) - pg_indexes_size(c.oid)) AS data_size, nspname AS schema_name, relname AS table_name FROM pg_class c LEFTJOIN pg_namespace n ON n.oid = c.relnamespace WHERE relkind ='r' AND nspname NOTLIKE'pg_%' AND nspname !='information_schema' ORDERBY pg_total_relation_size(c.oid) DESC;
selectcount(*) from pg_stat_activity where state <>'idle'and (backend_xid isnotnullor backend_xmin isnotnull) and now()-xact_start >interval'3600 sec'::interval;
select*from pg_locks wherenot granted;
select*from pg_class where oid=[上面查出来的relation];
select*from pg_database where oid=[上面查出来的database];
select oid from pg_class where relname='可能锁表了的表';
select pid from pg_locks where relation='上面查出的oid';
select pg_cancel_backend(pid);
select pg_terminate_backend(pid);
select*from pg_stat_activity;
update pg_database set datname ='destniationDb'where datname ='sourceDb';
SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE datname='test_db'AND pid<>pg_backend_pid();
\COPY (SELECT'DROP TABLE IF EXISTS "'|| tablename ||'" CASCADE;'from pg_tables WHERE schemaname ='public') TO'/tmp/sql_output.sql';
create index [XXX] where [XXX]
show statement_timeout;
AlTER DATABASE mydatabse SET statement_timeout='60s';
ALTER ROLE guest SET statement_timeout='5min';
子查询优化
PG 的子查询实际有两种,分为子连接(Sublink)和子查询(SubQuery),按子句的位置不同,出现在 from 关键字后的是子查询,出现在 where/on 等约束条件中或投影中的子句是子连接。
子查询:select a.* from table_a a, (select a_id from table_b where id=1) b where b.a_id = a.id;
子连接:select * from table_a where id in(select a_id from table_b where id=1);
在简单的子连接查询下,PG 数据库查询优化器一般会将其转化为内连接的方式:select a.* from table_a a, table_b b where a.id=b.a_id and b.id=1;,正常索引没问题情况下这两种方式都能得一样的结果,最终执行的都是索引内连接结果。但在某些情况下,PG 查询优化器在子连接的 SQL 下,子连接的查询会走索引,而主查询会顺序扫描(Seq Scan),原因是当 table_a 的数据量很大时,索引值又有很多重复的,同时查询优化器也不知道子连接返回的具体数据,这时查询优化器可能会认为顺序扫描更快,从而不走索引,导致耗时增加,所以为减少查询优化器的不确定性,最好是直接使用内连接的方式代替 in 语句。 当然,对于特别复杂的查询业务,还是开启事务,分多次查询,在代码层做一些业务逻辑处理更合适,别让数据库把事情全做了,这也能减轻数据库的压力。 PG 查询计划执行路径可以看看: PostgreSQL 查询语句优化,postgresql通过索引优化查询速度操作
ALTER ROLE readonly_group SET default_transaction_read_only TO'on';
createuser reader with password 'reader'in role readonly_group;
dropuser reader;
grant readonly_group to reader;
GRANTSELECTONALL TABLES IN SCHEMA public TO readonly_group; GRANTSELECTONALL SEQUENCES IN SCHEMA public TO readonly_group; GRANTEXECUTEONALL FUNCTIONS IN SCHEMA public TO readonly_group;
GRANTINSERT, UPDATE, DELETEONALL TABLES IN SCHEMA public TO write_group; GRANT USAGE ONALL SEQUENCES IN SCHEMA public TO write_group;
# 主备机不同步时,re_wind恢复结点 wal_log_hints = on # 设置最大流复制数(从库数) max_wal_senders = 3 wal_keep_segments = 64 # 支持从库读,以及从库再拉从库 hot_standby = on
设置主库:pg_hba.conf
1 2 3 4 5 6
# Allow replication connections from localhost, by a user with the # replication privilege. local replication all trust host replication all 127.0.0.1/32 trust host replication all ::1/128 trust host replication all 0.0.0.0/0 md5
psql -d postgresql://owner_user:pswd@host:port/db_name -t -A -F"," -c " SELECT DISTINCT 'ALTER TABLE ' || quote_ident(nsp.nspname) || '.' || quote_ident(cls.relname) || ' ADD CONSTRAINT ' || quote_ident(con.conname) || ' FOREIGN KEY (' || array_to_string(ARRAY( SELECT quote_ident(att.attname) FROM pg_attribute att WHERE att.attnum = ANY(con.conkey) AND att.attrelid = cls.oid), ', ') || ') REFERENCES ' || quote_ident(f_nsp.nspname) || '.' || quote_ident(f_cls.relname) || ' (' || array_to_string(ARRAY( SELECT quote_ident(att.attname) FROM pg_attribute att WHERE att.attnum = ANY(con.confkey) AND att.attrelid = f_cls.oid), ', ') || ') ON DELETE ' || CASE con.confdeltype WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END || ' ON UPDATE ' || CASE con.confupdtype WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END || ';' FROM pg_constraint con JOIN pg_class cls ON con.conrelid = cls.oid JOIN pg_namespace nsp ON cls.relnamespace = nsp.oid JOIN pg_class f_cls ON con.confrelid = f_cls.oid JOIN pg_namespace f_nsp ON f_cls.relnamespace = f_nsp.oid WHERE con.contype = 'f';" > db_name_fkeys.sql
pg_dump -d postgresql://user:pswd@host:port/db_name --data-only -F d -j 4 -f ./db_name_data_dir
新建数据库实例
1
pg_ctl init -D ~/new_db_data
导入数据库全局用户/权限
1
psql -U superuser -p port -f db_name_user.sql
新建数据库
1
create database new_db_name owner owner_user
导入数据库全部表结构
1
psql -U superuser -p port -f db_name_schema.sql
移除新库外键约束
1 2 3 4 5 6 7 8 9 10 11 12
psql -d postgresql://owner_user:pswd@host:port/db_name <<EOF DO \$\$ DECLARE r RECORD; BEGIN FOR r IN (SELECT conname, conrelid::regclass FROM pg_constraint WHERE contype = 'f') LOOP EXECUTE 'ALTER TABLE ' || r.conrelid || ' DROP CONSTRAINT ' || r.conname; END LOOP; END \$\$; EOF
proc_name="create index" functionwait_create_idx() { whiletrue; do proc_cnt=`ps aux | grep "$proc_name" | wc -l` if [ $proc_cnt -le 10 ]; then break fi sleep 60 done }
常见问题:
当自增主键报 duplicate key value violates unique constraint 主键冲突时,一般是因为存在手动分配 id 的数据(复制表或着手动插入分配了 id),自增主键 seqence TABLE_COLUMN_seq 没有更新,新插入一个值自增 id 和数据库已插入的分配 id 冲突,此时需要执行 SELECT setval('TABLE_COLUMN_seq', (SELECT max(COLUMN) FROM "TABLE")) 更新自增主键;
yum install -y vim lrzsz tree wget gcc gcc-c++ readline-devel zlib-devel
进入/usr/local/目录下:cd /usr/local
下载 tar 包:curl -O https://ftp.postgresql.org/pub/source/v16.2/postgresql-16.2.tar.gz
解压:tar -xzvf postgresql-16.2.tar.gz
编译安装:
1 2 3 4 5
cd /usr/local/postgresql-16.2 ./configure --prefix=/usr/local/pgsql-16.2 # /usr/local/pgsql-16.2 为安装目录 make && make install # Two thousand years later,出现「PostgreSQL installation complete.」代表安装成功
test -x $DAEMON || { echo"$DAEMON not found" if [ "$1" = "stop" ] thenexit 0 elseexit 5 fi }
if [ -e "$PG_OOM_ADJUST_FILE" -a -n "$PG_CHILD_OOM_SCORE_ADJ" ] then DAEMON_ENV="PG_OOM_ADJUST_FILE=$PG_OOM_ADJUST_FILE PG_OOM_ADJUST_VALUE=$PG_CHILD_OOM_SCORE_ADJ" fi
declare begin case i_mode when'INVALID'thenreturn0; when'AccessShareLock'thenreturn1; when'RowShareLock'thenreturn2; when'RowExclusiveLock'thenreturn3; when'ShareUpdateExclusiveLock'thenreturn4; when'ShareLock'thenreturn5; when'ShareRowExclusiveLock'thenreturn6; when'ExclusiveLock'thenreturn7; when'AccessExclusiveLock'thenreturn8; elsereturn0; endcase; end;
$$ language plpgsql strict;
with t_wait as (select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.objid,a.objsubid, a.pid,a.virtualtransaction,a.virtualxid,a,transactionid,b.query,b.xact_start,b.query_start, b.usename,b.datname from pg_locks a,pg_stat_activity b where a.pid=b.pid andnot a.granted), t_run as (select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.objid,a.objsubid, a.pid,a.virtualtransaction,a.virtualxid,a,transactionid,b.query,b.xact_start,b.query_start, b.usename,b.datname from pg_locks a,pg_stat_activity b where a.pid=b.pid and a.granted) select r.locktype,r.mode r_mode,r.usename r_user,r.datname r_db,r.relation::regclass,r.pid r_pid, r.page r_page,r.tuple r_tuple,r.xact_start r_xact_start,r.query_start r_query_start, now()-r.query_start r_locktime,r.query r_query,w.mode w_mode,w.pid w_pid,w.page w_page, w.tuple w_tuple,w.xact_start w_xact_start,w.query_start w_query_start, now()-w.query_start w_locktime,w.query w_query from t_wait w,t_run r where r.locktype isnotdistinctfrom w.locktype and r.database isnotdistinctfrom w.database and r.relation isnotdistinctfrom w.relation and r.page isnotdistinctfrom w.page and r.tuple isnotdistinctfrom w.tuple and r.classid isnotdistinctfrom w.classid and r.objid isnotdistinctfrom w.objid and r.objsubid isnotdistinctfrom w.objsubid and r.transactionid isnotdistinctfrom w.transactionid and r.pid <> w.pid orderby f_lock_level(w.mode)+f_lock_level(r.mode) desc,r.xact_start;
现在可以排在前面的就是锁级别高的等待,优先干掉这个。
-[ RECORD 1 ]-+----------------------------------------------------------
select indexrelname, pg_size_pretty(pg_relation_size(indexrelid)) as size from pg_stat_user_indexes where schemaname='public'orderby pg_relation_size('public'||'.'||indexrelname) desc;
SELECT pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size, pg_size_pretty(pg_indexes_size(c.oid)) AS index_size, pg_size_pretty(pg_total_relation_size(c.oid) - pg_indexes_size(c.oid)) AS data_size, nspname AS schema_name, relname AS table_name FROM pg_class c LEFTJOIN pg_namespace n ON n.oid = c.relnamespace WHERE relkind ='r' AND nspname NOTLIKE'pg_%' AND nspname !='information_schema' ORDERBY pg_total_relation_size(c.oid) DESC;
selectcount(*) from pg_stat_activity where state <>'idle'and (backend_xid isnotnullor backend_xmin isnotnull) and now()-xact_start >interval'3600 sec'::interval;
select*from pg_locks wherenot granted;
select*from pg_class where oid=[上面查出来的relation];
select*from pg_database where oid=[上面查出来的database];
select oid from pg_class where relname='可能锁表了的表';
select pid from pg_locks where relation='上面查出的oid';
select pg_cancel_backend(pid);
select pg_terminate_backend(pid);
select*from pg_stat_activity;
update pg_database set datname ='destniationDb'where datname ='sourceDb';
SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE datname='test_db'AND pid<>pg_backend_pid();
\COPY (SELECT'DROP TABLE IF EXISTS "'|| tablename ||'" CASCADE;'from pg_tables WHERE schemaname ='public') TO'/tmp/sql_output.sql';
create index [XXX] where [XXX]
show statement_timeout;
AlTER DATABASE mydatabse SET statement_timeout='60s';
ALTER ROLE guest SET statement_timeout='5min';
子查询优化
PG 的子查询实际有两种,分为子连接(Sublink)和子查询(SubQuery),按子句的位置不同,出现在 from 关键字后的是子查询,出现在 where/on 等约束条件中或投影中的子句是子连接。
子查询:select a.* from table_a a, (select a_id from table_b where id=1) b where b.a_id = a.id;
子连接:select * from table_a where id in(select a_id from table_b where id=1);
在简单的子连接查询下,PG 数据库查询优化器一般会将其转化为内连接的方式:select a.* from table_a a, table_b b where a.id=b.a_id and b.id=1;,正常索引没问题情况下这两种方式都能得一样的结果,最终执行的都是索引内连接结果。但在某些情况下,PG 查询优化器在子连接的 SQL 下,子连接的查询会走索引,而主查询会顺序扫描(Seq Scan),原因是当 table_a 的数据量很大时,索引值又有很多重复的,同时查询优化器也不知道子连接返回的具体数据,这时查询优化器可能会认为顺序扫描更快,从而不走索引,导致耗时增加,所以为减少查询优化器的不确定性,最好是直接使用内连接的方式代替 in 语句。 当然,对于特别复杂的查询业务,还是开启事务,分多次查询,在代码层做一些业务逻辑处理更合适,别让数据库把事情全做了,这也能减轻数据库的压力。 PG 查询计划执行路径可以看看: PostgreSQL 查询语句优化,postgresql通过索引优化查询速度操作
ALTER ROLE readonly_group SET default_transaction_read_only TO'on';
createuser reader with password 'reader'in role readonly_group;
dropuser reader;
grant readonly_group to reader;
GRANTSELECTONALL TABLES IN SCHEMA public TO readonly_group; GRANTSELECTONALL SEQUENCES IN SCHEMA public TO readonly_group; GRANTEXECUTEONALL FUNCTIONS IN SCHEMA public TO readonly_group;
GRANTINSERT, UPDATE, DELETEONALL TABLES IN SCHEMA public TO write_group; GRANT USAGE ONALL SEQUENCES IN SCHEMA public TO write_group;
# 主备机不同步时,re_wind恢复结点 wal_log_hints = on # 设置最大流复制数(从库数) max_wal_senders = 3 wal_keep_segments = 64 # 支持从库读,以及从库再拉从库 hot_standby = on
设置主库:pg_hba.conf
1 2 3 4 5 6
# Allow replication connections from localhost, by a user with the # replication privilege. local replication all trust host replication all 127.0.0.1/32 trust host replication all ::1/128 trust host replication all 0.0.0.0/0 md5
psql -d postgresql://owner_user:pswd@host:port/db_name -t -A -F"," -c " SELECT DISTINCT 'ALTER TABLE ' || quote_ident(nsp.nspname) || '.' || quote_ident(cls.relname) || ' ADD CONSTRAINT ' || quote_ident(con.conname) || ' FOREIGN KEY (' || array_to_string(ARRAY( SELECT quote_ident(att.attname) FROM pg_attribute att WHERE att.attnum = ANY(con.conkey) AND att.attrelid = cls.oid), ', ') || ') REFERENCES ' || quote_ident(f_nsp.nspname) || '.' || quote_ident(f_cls.relname) || ' (' || array_to_string(ARRAY( SELECT quote_ident(att.attname) FROM pg_attribute att WHERE att.attnum = ANY(con.confkey) AND att.attrelid = f_cls.oid), ', ') || ') ON DELETE ' || CASE con.confdeltype WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END || ' ON UPDATE ' || CASE con.confupdtype WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END || ';' FROM pg_constraint con JOIN pg_class cls ON con.conrelid = cls.oid JOIN pg_namespace nsp ON cls.relnamespace = nsp.oid JOIN pg_class f_cls ON con.confrelid = f_cls.oid JOIN pg_namespace f_nsp ON f_cls.relnamespace = f_nsp.oid WHERE con.contype = 'f';" > db_name_fkeys.sql
pg_dump -d postgresql://user:pswd@host:port/db_name --data-only -F d -j 4 -f ./db_name_data_dir
新建数据库实例
1
pg_ctl init -D ~/new_db_data
导入数据库全局用户/权限
1
psql -U superuser -p port -f db_name_user.sql
新建数据库
1
create database new_db_name owner owner_user
导入数据库全部表结构
1
psql -U superuser -p port -f db_name_schema.sql
移除新库外键约束
1 2 3 4 5 6 7 8 9 10 11 12
psql -d postgresql://owner_user:pswd@host:port/db_name <<EOF DO \$\$ DECLARE r RECORD; BEGIN FOR r IN (SELECT conname, conrelid::regclass FROM pg_constraint WHERE contype = 'f') LOOP EXECUTE 'ALTER TABLE ' || r.conrelid || ' DROP CONSTRAINT ' || r.conname; END LOOP; END \$\$; EOF
proc_name="create index" functionwait_create_idx() { whiletrue; do proc_cnt=`ps aux | grep "$proc_name" | wc -l` if [ $proc_cnt -le 10 ]; then break fi sleep 60 done }
常见问题:
当自增主键报 duplicate key value violates unique constraint 主键冲突时,一般是因为存在手动分配 id 的数据(复制表或着手动插入分配了 id),自增主键 seqence TABLE_COLUMN_seq 没有更新,新插入一个值自增 id 和数据库已插入的分配 id 冲突,此时需要执行 SELECT setval('TABLE_COLUMN_seq', (SELECT max(COLUMN) FROM "TABLE")) 更新自增主键;