本文介绍如何在centos7或rocky9下安装postgresql15,并配置物理复制的全过程。postgresql安装采用shell脚本安装,一键执行,通俗易懂。
序号 | IP | 操作系统 | 用途 | 备注 |
---|---|---|---|---|
01 | 10.10.0.41 | rockylinux9.3 | 主库 | |
02 | 10.10.0.42 | rockylinux9.3 | 从库 |
将附录5.1脚本拷贝执行:
sh install.sh
验证:
/usr/pgsql-15/bin/psql -h /opt/apps/postgresql/run -p 5432 -U postgresql -d postgres
将附录5.1 脚本拷贝:并注释init()方法内的pg_init db_init
#初始化方法
function init(){
os_init;
pg_install;
#pg_init;
sh_init;
#db_init;
}
init
在从库执行。
pg_basebackup -h 10.10.0.41 -U repuser -D /root/bak -p 5432 --wal-method=stream
从库执行。
rm -rf /opt/apps/postgresql/data
cp -r /root/bak /opt/apps/postgresql/data
chown -R postgresql:postgresql /opt/apps/postgresql/data
从库执行。
/opt/apps/postgresql/data/postgresql.auto.conf
# Do not edit this file manually!
# It will be overwritten by the ALTER SYSTEM command.
primary_conninfo='host=10.10.0.41 port=5432 user=repuser password=repuser123456'
recovery_target_timeline='latest'
su - postgresql -c "touch /opt/apps/postgresql/data/standby.signal"
cd /opt/apps/postgresql && sh start.sh
日志目录:/opt/apps/postgresql/data/pg_log/
略。
主库并不直接存储从库的复制状态。我们可以通过查看主库的
pg_stat_replication
视图,它会列出所有已连接并正在进行复制的从库信息
[root@localhost scripts]# /usr/pgsql-15/bin/psql -h /opt/apps/postgresql/run -p 5432 -U postgresql -d postgres
psql (15.5)
Type "help" for help.
postgres=# \x
Expanded display is on.
postgres=# SELECT * FROM pg_stat_replication;
-[ RECORD 1 ]----+------------------------------
pid | 13823
usesysid | 16384
usename | repuser
application_name | walreceiver
client_addr | 10.10.0.42
client_hostname |
client_port | 38120
backend_start | 2024-01-25 13:00:52.184683+08
backend_xmin |
state | streaming
sent_lsn | 0/503EB18
write_lsn | 0/503EB18
flush_lsn | 0/503EB18
replay_lsn | 0/503EB18
write_lag |
flush_lag |
replay_lag |
sync_priority | 0
sync_state | async
reply_time | 2024-01-25 14:29:38.472977+08
postgres=#
[root@localhost scripts]# /usr/pgsql-15/bin/psql -h /opt/apps/postgresql/run -p 5432 -U postgresql -d postgres
psql (15.5)
Type "help" for help.
postgres=# \x
Expanded display is on.
postgres=# SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();
-[ RECORD 1 ]-----------+----------
pg_last_wal_receive_lsn | 0/503EB18
pg_last_wal_replay_lsn | 0/503EB18
postgres=#
#!/bin/bash
set -x
#定义变量
base_dir="/opt/apps"
user_name="postgresql"
port="5432"
appuser_name="pguser"
appuser_password="pguser123456"
appdb_name="pgdb"
repuser_name="repuser"
repuser_password="repuser123456"
default_python_version=$(python --version 2>&1 | cut -d' ' -f2 | cut -d. -f1)
if [ "$default_python_version" -eq "2" ]; then
appuser_password_encoded=$(python -c "import urllib; print(urllib.quote('$appuser_password'))")
elif [ "$default_python_version" -eq "3" ]; then
appuser_password_encoded=$(python -c "import urllib.parse; print(urllib.parse.quote('$appuser_password'))")
else
echo "无法识别的 Python 版本"
fi
# 获取OS版本信息
os_version=$(cat /etc/redhat-release | grep -oP '\d+' | head -1)
#获取Ip
host_ip=$(python -c "import socket;print([(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1])")
#操作系统初始化
function os_init(){
# 获取 firewalld 的详细状态信息
systemctl stop firewalld
systemctl disable firewalld
# 获取当前 SELinux 状态
selinux_status=$(getenforce)
# 判断 SELinux 状态并执行相应操作
if [ "$selinux_status" == "Disabled" ]; then
echo "当前 SELinux 状态为 disabled"
elif [ "$selinux_status" == "Enforcing" ]; then
setenforce 0
sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
echo "SELinux 已禁用并配置为 disabled"
elif [ "$selinux_status" == "Permissive" ]; then
sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
echo "SELinux 已配置为 disabled"
else
echo "未知的 SELinux 状态: $selinux_status"
fi
# 检查 limits.conf 文件是否包含所需配置
if grep -q "^\*\s*soft\s*nofile\s*65536" /etc/security/limits.conf && \
grep -q "^\*\s*hard\s*nofile\s*65536" /etc/security/limits.conf && \
grep -q "^\*\s*soft\s*nproc\s*65536" /etc/security/limits.conf && \
grep -q "^\*\s*hard\s*nproc\s*65536" /etc/security/limits.conf; then
echo "文件描述符限制已优化"
else
# 执行配置命令
echo "* soft nofile 65536" >> /etc/security/limits.conf
echo "* hard nofile 65536" >> /etc/security/limits.conf
echo "* soft nproc 65536" >> /etc/security/limits.conf
echo "* hard nproc 65536" >> /etc/security/limits.conf
echo "文件描述符限制已优化"
fi
# 检查 systemd 配置文件是否包含所需配置
if grep -q "^\s*DefaultLimitNOFILE=1000000" /etc/systemd/system.conf && \
grep -q "^\s*DefaultLimitNPROC=65535" /etc/systemd/system.conf; then
echo "Systemd 文件描述符和进程数限制已优化"
else
# 执行配置命令
cat >> /etc/systemd/system.conf << EOF
DefaultLimitNOFILE=1000000
DefaultLimitNPROC=65535
EOF
systemctl daemon-reexec
echo "Systemd 文件描述符和进程数限制已优化"
fi
#创建用户
if id "$user_name" &>/dev/null; then
echo "User $user_name already exists."
else
# 如果用户不存在,则创建用户
useradd "$user_name"
echo "User $user_name created successfully."
fi
#创建pg目录
mkdir -p $base_dir/$user_name/data
mkdir -p $base_dir/$user_name/run
mkdir -p $base_dir/$user_name/archive
mkdir -p $base_dir/$user_name/backup
mkdir -p $base_dir/$user_name/scripts
#更改目录权限
chown -R $user_name:$user_name $base_dir/$user_name
}
function pg_install(){
# 判断操作系统版本号,安装postgresql15-server
if [ "$os_version" == "7" ]; then
cat /etc/redhat-release
#配置epel源
curl -o /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo
#增加postgresql源
cat > /etc/yum.repos.d/postgresql.repo << EOF
[postgresql]
name=postgresql
baseurl=https://download.postgresql.org/pub/repos/yum/15/redhat/rhel-7.9-x86_64/
enabled=1
gpgcheck=0
EOF
#安装依赖包
rpm -ivh https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/l/libzstd-1.5.5-1.el7.x86_64.rpm
#安装postgresql15-server
yum install postgresql15-server postgresql15-contrib -y
elif [ "$os_version" == "9" ]; then
cat /etc/redhat-release
#增加postgresql源
cat > /etc/yum.repos.d/postgresql.repo << EOF
[postgresql]
name=http
baseurl=https://download.postgresql.org/pub/repos/yum/15/redhat/rhel-9.3-x86_64/
enabled=1
gpgcheck=0
EOF
#安装postgresql15-server
yum install postgresql15-server postgresql15-contrib -y
else
echo "This script is intended to run on CentOS 7."
fi
}
function pg_init(){
#initdb
su - $user_name -c "/usr/pgsql-15/bin/initdb --username=$user_name --encoding=UTF8 --lc-collate=C --lc-ctype=en_US.utf8 --data-checksums -D $base_dir/$user_name/data"
#配置pg_hba.conf文件
cat >> $base_dir/$user_name/data/pg_hba.conf << EOF
host replication $repuser_name 0.0.0.0/0 md5
host all all 0.0.0.0/0 md5
EOF
#配置postgresql.conf
cat > $base_dir/$user_name/data/postgresql.conf << EOF
# basic
listen_addresses='0.0.0.0'
port=$port # 端口
unix_socket_directories='$base_dir/$user_name/run' # socket目录
max_connections = 9999 # 最大连接数
superuser_reserved_connections = 10 # 给超级用户预留的连接数
shared_buffers = 1GB # 共享内存,一般设置为内存的1/4
effective_cache_size = 2GB # 查询优化器估计的可用于缓存数据文件系统的总内存量,一般设置为内存的1/2
max_worker_processes = 48 # 最大工作线程,和cpu核数一致
max_parallel_workers_per_gather = 4 # 单个查询在执行过程中可以使用的最大并行工作进程数,CPU核心数的1/12
max_parallel_workers = 24 # 整个数据库实例允许的最大并行进程数,CPU核心数的1/2到2/3之间
max_parallel_maintenance_workers = 6 # 维护操作期间允许的最大并行进程数,CPU核心数的1/8
work_mem = 16MB # 设置在写入临时磁盘文件之前查询操作(例如排序或哈希表)可使用的最大内存容量
maintenance_work_mem = 256MB # 在维护性操作(例如VACUUM、CREATE INDEX和ALTER TABLE ADD FOREIGN KEY)中使用的 最大的内存量
timezone = 'Asia/Shanghai' # 系统时区
hot_standby = on # 打开热备
# optimizer
default_statistics_target = 500 # 默认100,ANALYZE在pg_statistic中存储的信息量,增大该值,会增加ANALYZE的时间,但会让解释计划更精准
# wal
max_wal_size = 1GB # 建议与shared_buffers保持一致
min_wal_size = 80MB # 建议max_wal_size/12.5
wal_log_hints = on # 控制WAL日志记录的方式,建议打开
wal_level = replica # wal日志写入级别,要使用流复制,必须使用replica或更高级别
wal_sender_timeout = 60s # 设置WAL发送者在发送WAL数据时等待主服务器响应的超时时间
# archive
archive_mode = on # 开启归档日志
archive_command = 'gzip < %p > $base_dir/$user_name/archive/%f.gz'
# log 近7天轮询
log_destination = 'stderr' # 日志格式
logging_collector = on # 日志收集器
log_directory = 'pg_log' # 日志目录 $PGDATA/pg_log
log_filename = 'postgresql-%Y-%m-%d.log' # 日志名称格式
log_rotation_age = 43200 # 日志保留时间单位是分钟
log_file_mode = 0600 # 日志文件的权限
log_rotation_size = 0 # 日志的最大尺寸,设置为零时将禁用基于大小创建新的日志文件
log_truncate_on_rotation = on # 这个参数将导致PostgreSQL截断(覆盖而不是追加)任何已有的同名日志文件
log_min_duration_statement = 0 # 如果语句运行至少指定的时间量,将导致记录每一个这种完成的语句的持续时间
log_duration = on # 每一个完成的语句的持续时间被记录
log_lock_waits = on # 控制当一个会话为获得一个锁等到超过deadlock_timeout时,是否要产生一个日志消息
log_statement = 'mod' # 控制哪些 SQL 语句被记录。有效值是 none (off)、ddl、mod和 all(所有语句)。ddl记录所有数据定义语句,例如CREATE、ALTER和 DROP语句。mod记录所有ddl语句,外加数据修改语句例如INSERT, UPDATE、DELETE、TRUNCATE, 和COPY FROM
log_timezone = 'Asia/Shanghai' # 设置在服务器日志中写入的时间戳的时区
# sql
statement_timeout = 300000 # 语句执行超时时间 5分钟
idle_in_transaction_session_timeout = 300000 # 事务空闲超时时间 5分钟
idle_session_timeout = 1800000 # 会话空闲超时时间 30分钟
lock_timeout = 60000 # 等锁超时时间 1分钟
EOF
}
#启动和开机自启动
function sh_init(){
#启动服务
su - $user_name -c "/usr/pgsql-15/bin/pg_ctl -D $base_dir/$user_name/data -l $base_dir/$user_name/pg.log start"
#配置开机自启动
chmod +x /etc/rc.d/rc.local
cat >> /etc/rc.d/rc.local << EOF
su - $user_name -c "/usr/pgsql-15/bin/pg_ctl -D $base_dir/$user_name/data -l $base_dir/$user_name/pg.log start"
EOF
#创建启动、停止、重启脚本
cat > $base_dir/$user_name/start.sh << EOF
#!/bin/bash
su - $user_name -c "/usr/pgsql-15/bin/pg_ctl -D $base_dir/$user_name/data -l $base_dir/$user_name/pg.log start"
EOF
cat > $base_dir/$user_name/stop.sh << EOF
#!/bin/bash
su - $user_name -c "/usr/pgsql-15/bin/pg_ctl -D $base_dir/$user_name/data -l $base_dir/$user_name/pg.log stop"
EOF
cat > $base_dir/$user_name/restart.sh << EOF
#!/bin/bash
su - $user_name -c "/usr/pgsql-15/bin/pg_ctl -D $base_dir/$user_name/data -l $base_dir/$user_name/pg.log restart"
EOF
#创建备份脚本
cat > $base_dir/$user_name/scripts/backup.sh << EOF
#!/bin/bash
set -ex
cmd="/usr/bin/pg_dump -Fc -v --dbname=postgresql://$appuser_name:$appuser_password_encoded@$host_ip:$port/$appdb_name -f $base_dir/$user_name/backup/$appdb_name-\$(date +%Y-%m-%d).dmp"
\$cmd
find $backup_dir -name "*.dmp" -mtime +30 -exec rm -f {} \;
EOF
#添加定时任务脚本
cat > $base_dir/$user_name/scripts/crontab.sh << EOF
#!/bin/bash
# 检查是否存在 crontab,如果不存在,创建一个空的 crontab
if [ -z "$(crontab -l)" ]; then
echo "" | crontab -
fi
(crontab -l ; echo "0 23 * * * sh $base_dir/$user_name/scripts/backup.sh > $base_dir/$user_name/backup/backup.log 2>&1")|crontab -
EOF
chmod +x $base_dir/$user_name/scripts/crontab.sh
/bin/sh $base_dir/$user_name/scripts/crontab.sh
#创建恢复脚本
cat > $base_dir/$user_name/scripts/pg_restore.sh << EOF
#!/bin/bash
pg_restore -v --dbname=postgresql://$appuser_name:$appuser_password_encoded@$host_ip:$port/$appdb_name $base_dir/$user_name/backup/$appdb_name.dmp
EOF
#创建psql命令示例
cat > $base_dir/$user_name/scripts/psql.txt << EOF
/usr/pgsql-15/bin/psql -h $base_dir/$user_name/run -p $port -U $user_name -d postgres
EOF
}
function db_init(){
#创建init.sql文件
cat > $base_dir/$user_name/scripts/init.sql << EOF
create USER $repuser_name with login replication encrypted password '$repuser_password';
-- 创建数据库
CREATE DATABASE $appdb_name;
-- 连接到新创建的数据库
\c $appdb_name;
-- 创建用户
CREATE USER $appuser_name WITH PASSWORD '$appuser_password';
-- 赋予用户所有权限
GRANT ALL PRIVILEGES ON DATABASE $appdb_name TO $appuser_name;
-- 将 appdb 的所有权(OWNER)设置为 $appuser_name
ALTER DATABASE $appdb_name OWNER TO $appuser_name;
EOF
#创建drop.sql
cat > $base_dir/$user_name/scripts/drop.sql << EOF
drop database $appdb_name;
drop user $appuser_name;
EOF
#执行sql
/usr/pgsql-15/bin/psql -h $base_dir/$user_name/run -p $port -U $user_name -d postgres -f $base_dir/$user_name/scripts/init.sql
}
#初始化方法
function init(){
os_init;
pg_install;
pg_init;
sh_init;
db_init;
}
init
上述脚本包含了在Linux环境中安装和配置PostgreSQL 15数据库的详细步骤,同时涵盖了数据库初始化、用户与权限设置、日志管理、备份恢复策略以及操作系统层面的优化等内容。以下是详细的解析:
base_dir
(基础目录)、user_name
(PostgreSQL运行用户)、port
(数据库服务端口)、appuser_name
及appuser_password
(应用连接数据库的用户名和密码)、appdb_name
(应用数据库名)等变量。postgresql
的系统用户及其相关的目录结构,并赋予相应的权限。initdb
命令指定用户名、字符集、校验和、时区和其他参数。pg_hba.conf
文件以控制不同用户的认证方式,包括从任何IP地址通过md5认证进行复制和一般连接。postgresql.conf
以设置监听地址、端口、socket路径、最大连接数、内存相关参数(如shared_buffers、effective_cache_size、work_mem等),以及WAL相关参数(如wal_level、max_wal_size、min_wal_size等)。db_init
函数中,创建了一个SQL脚本用于初始化数据库和用户账号,包括创建一个具有特定权限的应用程序用户和数据库,并为复制设置一个专用用户。pg_dump
工具对数据库进行完整备份,并按日期命名备份文件,同时清理超过30天的旧备份。pg_restore
脚本。建表语句
CREATE TABLE "t1" (
"c1" INTEGER NULL DEFAULT NULL
);
安装psycopg包
若没有pip3命令请执行:yum install python3-pip -y
pip3 install psycopg
脚本示例:向主库中插入10000条数据
#!/usr/bin/python3
import psycopg
host = '10.10.0.41'
port = '5432'
database = 'pgdb'
user = 'pguser'
password = 'pguser123456'
# 创建连接和游标
conn = psycopg.connect(host=host, port=port, dbname=database, user=user, password=password)
cursor = conn.cursor()
# 假设有一批数据要插入,这里以列表形式表示
data_to_insert = [(i,) for i in range(1, 100001)]
# 批量插入数据的 SQL 语句模板
insert_query = """
INSERT INTO t1 (c1) VALUES (%s);
"""
# 执行批量插入操作
cursor.executemany(insert_query, data_to_insert)
# 提交事务
conn.commit()
# 关闭游标和连接
cursor.close()
conn.close()
print("数据已成功批量插入到 t1 表中!")