分类 PostgreSQL 下的文章

postgres中统计多个数据库中指定表总行数shell脚本

postgres数据库中如统计N个数据库中users表数据总量

#!/bin/bash
#===============================================================================
# 脚本名称: count_db_users.sh
# 功能描述: 统计多个PostgreSQL数据库中用户表的用户总数
# 版本: 2.0
# 更新: 修复登录notice信息导致的解析错误
#===============================================================================

# 默认参数配置
DB_HOST="localhost"
DB_PORT="5432"
DB_USER="postgres"
DB_PASSWORD="xxx"
DATABASES=(test )
TABLE_NAME="users"
VERBOSE=0
OUTPUT_FILE=""
CONNECTION_TIMEOUT=10

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

#===============================================================================
# 函数: 显示帮助信息
#===============================================================================
show_help() {
    cat << EOF
${BLUE}PostgreSQL 多数据库用户统计工具 v2.0${NC}

用法: $0 [选项]

选项:
    -h HOST        PostgreSQL主机地址 (默认: localhost)
    -p PORT        PostgreSQL端口 (默认: 5432)
    -U USER        数据库用户名 (默认: postgres)
    -d "DB1 DB2"   要统计的数据库列表 (空格分隔,必需)
    -t TABLE       表名 (默认: users)
    -P PASSWORD    数据库密码 (不推荐,建议使用.pgpass)
    -v             详细模式
    -o FILE        输出结果到文件
    -T TIMEOUT     连接超时时间(秒) (默认: 10)
    --help         显示此帮助信息

示例:
    $0 -h localhost -p 5432 -U postgres -d "db1 db2 db3"
    $0 -d "prod_db test_db" -t user_accounts -v
    $0 -d "db1 db2" -o result.txt

EOF
}

#===============================================================================
# 函数: 打印消息
#===============================================================================
print_info() {
    echo -e "${GREEN}[INFO]${NC} $1"
}

print_warn() {
    echo -e "${YELLOW}[WARN]${NC} $1"
}

print_error() {
    echo -e "${RED}[ERROR]${NC} $1"
    if [[ -n "$2" ]]; then
        echo -e "${RED}详情: $2${NC}"
    fi
}

#===============================================================================
# 函数: 构建psql命令
#===============================================================================
build_psql_command() {
    local db="$1"
    local query="$2"
    local options="-h $DB_HOST -p $DB_PORT -U $DB_USER -d $db"
    
    # 添加静默选项,抑制通知和警告信息
    options="$options -q --no-psqlrc -v ON_ERROR_STOP=1"
    
    # 设置客户端消息级别(不显示NOTICE信息)
    options="$options -c 'SET client_min_messages = warning;'"
    
    # 超时设置
    options="$options -c statement_timeout = ${CONNECTION_TIMEOUT}s"
    
    # 添加查询
    options="$options -t -A -c \"$query\""
    
    echo "$options"
}

#===============================================================================
# 函数: 执行psql查询并返回纯数据
#===============================================================================
execute_query() {
    local db="$1"
    local query="$2"
    local result
    local exit_code
    
    # 使用临时文件避免子shell问题
    local tmp_file=$(mktemp 2>/dev/null || mktemp -t tmp 2>/dev/null)
    local tmp_error=$(mktemp 2>/dev/null || mktemp -t tmp 2>/dev/null)
    
    # 构建命令 - 重定向stderr到临时文件
    if [[ -z "$DB_PASSWORD" ]]; then
        psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$db" \
            -q --no-psqlrc \
            -v ON_ERROR_STOP=1 \
            -c "SET client_min_messages = warning;" \
            -c "SET statement_timeout = '${CONNECTION_TIMEOUT}s';" \
            -t -A -c "$query" \
            2>"$tmp_error" >"$tmp_file"
    else
        PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$db" \
            -q --no-psqlrc \
            -v ON_ERROR_STOP=1 \
            -c "SET client_min_messages = warning;" \
            -c "SET statement_timeout = '${CONNECTION_TIMEOUT}s';" \
            -t -A -c "$query" \
            2>"$tmp_error" >"$tmp_file"
    fi
    
    exit_code=$?
    
    # 读取输出
    result=$(cat "$tmp_file" 2>/dev/null | tr -d '\r' | sed '/^$/d')
    local error_msg=$(cat "$tmp_error" 2>/dev/null)
    
    # 清理临时文件
    rm -f "$tmp_file" "$tmp_error" 2>/dev/null
    
    # 检查是否执行成功
    if [[ $exit_code -ne 0 ]]; then
        # 过滤掉NOTICE信息(这些是警告,不是致命错误)
        if [[ -n "$error_msg" ]] && [[ ! "$error_msg" =~ ^NOTICE: ]]; then
            echo "ERROR|$error_msg"
            return 1
        fi
    fi
    
    # 如果没有结果,返回空值
    if [[ -z "$result" ]] || [[ "$result" =~ ^[[:space:]]*$ ]]; then
        echo "0"
        return 0
    fi
    
    echo "$result"
    return 0
}

#===============================================================================
# 函数: 检查数据库连接
#===============================================================================
check_connection() {
    local db="$1"
    local result
    result=$(execute_query "$db" "SELECT 1" 2>/dev/null)
    
    if [[ $? -eq 0 ]] && [[ "$result" == "1" ]]; then
        return 0
    else
        return 1
    fi
}

#===============================================================================
# 函数: 检查表是否存在
#===============================================================================
check_table_exists() {
    local db="$1"
    local table="$2"
    local result
    
    # 转义表名中的特殊字符
    local safe_table=$(echo "$table" | sed "s/'/''/g")
    
    result=$(execute_query "$db" \
        "SELECT COUNT(*) FROM information_schema.tables \
         WHERE table_schema NOT IN ('information_schema', 'pg_catalog') \
         AND table_name = '$safe_table';" 2>/dev/null)
    
    # 检查结果是否为数字且大于0
    if [[ "$result" =~ ^[0-9]+$ ]] && [[ "$result" -gt 0 ]]; then
        return 0
    else
        return 1
    fi
}

#===============================================================================
# 函数: 统计单个数据库的用户数
#===============================================================================
count_users_in_db() {
    local db="$1"
    local table="$2"
    local count=0
    local status="成功"
    local error_msg=""
    
    # 转义表名
    local safe_table=$(echo "$table" | sed 's/"/""/g')
    
    # 检查连接
    if ! check_connection "$db"; then
        status="失败"
        error_msg="无法连接到数据库"
        echo "0|$status|$error_msg"
        return 1
    fi
    
    # 检查表是否存在
    if ! check_table_exists "$db" "$table"; then
        status="失败"
        error_msg="表 '$table' 不存在"
        echo "0|$status|$error_msg"
        return 1
    fi
    
    # 统计用户数 - 使用双引号包裹表名以支持大小写敏感
    local query="SELECT COUNT(*) FROM \"$safe_table\";"
    local result
    result=$(execute_query "$db" "$query")
    
    # 检查是否返回错误
    if [[ "$result" =~ ^ERROR\| ]]; then
        status="失败"
        error_msg="${result#ERROR|}"
        echo "0|$status|$error_msg"
        return 1
    fi
    
    # 提取数字
    if [[ "$result" =~ ^[0-9]+$ ]]; then
        count="$result"
    else
        status="失败"
        error_msg="返回非数字结果: $result"
        echo "0|$status|$error_msg"
        return 1
    fi
    
    echo "$count|$status|"
    return 0
}

#===============================================================================
# 函数: 解析命令行参数
#===============================================================================
parse_arguments() {
    while [[ $# -gt 0 ]]; do
        case $1 in
            -h)
                DB_HOST="$2"
                shift 2
                ;;
            -p)
                DB_PORT="$2"
                shift 2
                ;;
            -U)
                DB_USER="$2"
                shift 2
                ;;
            -d)
                shift
                while [[ $# -gt 0 && ! "$1" =~ ^- ]]; do
                    DATABASES+=("$1")
                    shift
                done
                ;;
            -t)
                TABLE_NAME="$2"
                shift 2
                ;;
            -P)
                DB_PASSWORD="$2"
                shift 2
                ;;
            -T)
                CONNECTION_TIMEOUT="$2"
                shift 2
                ;;
            -v)
                VERBOSE=1
                shift
                ;;
            -o)
                OUTPUT_FILE="$2"
                shift 2
                ;;
            --help)
                show_help
                exit 0
                ;;
            *)
                print_error "未知参数: $1"
                show_help
                exit 1
                ;;
        esac
    done
    
    if [[ ${#DATABASES[@]} -eq 0 ]]; then
        print_error "未指定数据库列表!请使用 -d 参数指定至少一个数据库。"
        show_help
        exit 1
    fi
}

#===============================================================================
# 函数: 输出格式化结果
#===============================================================================
output_result() {
    local total=0
    local db_count=${#DATABASES[@]}
    local success_count=0
    local result_lines=()
    
    # 构建表头
    local header="| 序号 | 数据库名 | 用户数量 | 状态 | 备注 |"
    local separator="|------|----------|----------|------|------|"
    
    if [[ -n "$OUTPUT_FILE" ]]; then
        {
            echo "PostgreSQL 多数据库用户统计报告"
            echo "=================================="
            echo "统计时间: $(date '+%Y-%m-%d %H:%M:%S')"
            echo "主机: $DB_HOST:$DB_PORT"
            echo "数据库用户: $DB_USER"
            echo "表名: $TABLE_NAME"
            echo "数据库列表: ${DATABASES[*]}"
            echo ""
            echo "$header"
            echo "$separator"
        } >> "$OUTPUT_FILE"
    else
        print_info "开始统计 ${#DATABASES[@]} 个数据库..."
        echo ""
    fi
    
    # 遍历统计每个数据库
    local index=1
    for db in "${DATABASES[@]}"; do
        if [[ "$VERBOSE" -eq 1 ]]; then
            echo -ne "${BLUE}正在统计数据库 '$db'...${NC} "
        fi
        
        local result
        result=$(count_users_in_db "$db" "$TABLE_NAME")
        local count=$(echo "$result" | cut -d'|' -f1)
        local status=$(echo "$result" | cut -d'|' -f2)
        local error_msg=$(echo "$result" | cut -d'|' -f3-)
        
        # 清理可能的多余字符
        count=$(echo "$count" | tr -d '[:space:]')
        status=$(echo "$status" | tr -d '[:space:]')
        error_msg=$(echo "$error_msg" | head -c 50)  # 截断过长的错误信息
        
        # 构建输出行
        local line="| $index | $db | $count | $status | $error_msg |"
        result_lines+=("$line")
        
        # 累积总数
        if [[ "$status" == "成功" ]]; then
            total=$((total + count))
            success_count=$((success_count + 1))
            if [[ "$VERBOSE" -eq 1 ]]; then
                echo -e "${GREEN}✓ 用户数: $count${NC}"
            fi
        else
            if [[ "$VERBOSE" -eq 1 ]]; then
                echo -e "${RED}✗ 失败: $error_msg${NC}"
            fi
        fi
        
        if [[ -n "$OUTPUT_FILE" ]]; then
            echo "$line" >> "$OUTPUT_FILE"
        fi
        
        index=$((index + 1))
    done
    
    # 显示汇总结果
    echo ""
    if [[ -n "$OUTPUT_FILE" ]]; then
        {
            echo "$separator"
            echo ""
            echo "统计汇总"
            echo "--------"
            echo "总数据库数: $db_count"
            echo "成功统计: $success_count"
            echo "失败: $((db_count - success_count))"
            echo "用户总数: $total"
            echo ""
            echo "报告已保存至: $OUTPUT_FILE"
        } >> "$OUTPUT_FILE"
        print_info "报告已保存至: $OUTPUT_FILE"
    fi
    
    # 控制台输出
    echo "$header"
    echo "$separator"
    for line in "${result_lines[@]}"; do
        echo "$line"
    done
    echo "$separator"
    echo ""
    echo "${BLUE}统计汇总${NC}"
    echo "总数据库数: $db_count"
    echo "成功统计: $success_count"
    echo "失败: $((db_count - success_count))"
    echo -e "${GREEN}用户总数: $total${NC}"
}

#===============================================================================
# 主程序
#===============================================================================
main() {
    # 解析参数
    parse_arguments "$@"
    
    # 显示配置信息
    if [[ "$VERBOSE" -eq 1 ]]; then
        print_info "配置信息"
        echo "  主机: $DB_HOST"
        echo "  端口: $DB_PORT"
        echo "  用户: $DB_USER"
        echo "  超时: ${CONNECTION_TIMEOUT}s"
        echo "  数据库: ${DATABASES[*]}"
        echo "  表名: $TABLE_NAME"
        echo ""
    fi
    
    # 执行统计
    output_result
}

# 调用主程序
main "$@"

瀚高数据库安全版安装license

瀚高数据库安全版 V4.5.8 以及后续版本,license 文件可分别管控瀚高安全版数据库、
瀚高高可用集群(db_ha),瀚高读写分离集群(hg_proxy)。

上传 license
将 license文件上传到服务器任意目录下

检查 license
使用 hg_lic -c -F $filepath 来对指定 $filepath license 文件进行检查操作,该操作将会输出对 license 文件的检查结果,如果检查出现问题,将会提示 license 文件异常。如果一切正常将会输出 license 信息,包括 license 编号、license 状态、用户信息、授权方式、授权用途、申请日期、产品名称、产品版本、产品有效期。

chmod 0600 /opt/highgo/hgdb-see-4.5.8/etc/lic/hgdb.lic
hg_lic -c -F /opt/highgo/hgdb-see-4.5.8/etc/lic/hgdb.lic

加载license
使用 hg_lic -l -P $HGDB_HOME -F $filepath 来对指定文件 $filepath 进行加载操作,若配置了 $HGDB_HOME 环境变量,-P 参数可以省略。该操作会对许可证文件进行检查,如果许可证文件异常,将会提示。如果许可证一切正常,该操作会将许可证文件加载到数据库中。

PostgreSQL中的archive_command详解

什么是归档(Archiving)?

归档是指将过时或不再频繁使用的数据迁移到存储介质的过程。 PostgreSQL支持通过归档机制保存事务日志(WAL),以便在需要时进行数据库的恢复。这是确保数据安全和完整性的重要措施。

使用PostgreSQL的archive_command功能,你能够增强数据安全性,确保在发生故障时能够快速恢复。

- 阅读剩余部分 -

PostgreSQL常用操作

  • 创建数据库:CREATE DATABASE db_name template tpl_name;
  • 删除数据库:drop database xxx;
  • 创建用户:CREATE USER xxx WITH PASSWORD 'admin';
  • 删除用户:drop user username;
  • 授权:GRANT ALL PRIVILEGES ON DATABASE my_database TO my_user;

- 阅读剩余部分 -

thinkphp下mysql迁移至瀚高(postgreSQL)数据库

国产化需要将thinkphp系统由mysql迁移至瀚高数据库。

基本流程:

  1. 备份log相关表;清理log无用日志;
  2. 停用系统;
  3. 导出最新的sql文件(log等大表可考虑单独导出);
  4. 导入本地mysql数据库(如不支持远程连接);
  5. 处理相关代码与数据表(如处理user、desc、year、group字段等;处理group by、order by field、find_in_set、ifnull、convert、date_format方法等);
  6. 创建远程hg数据库
  7. 使用迁移工具迁移

- 阅读剩余部分 -

postgreSQL中的publications简介

在 PostgreSQL(PG)中,Publication(发布)是逻辑复制机制中的一个概念,用于定义哪些表的数据变更(INSERT、UPDATE、DELETE)可以发布到订阅者(Subscribers)。它主要用于 逻辑复制,允许在不同的 PostgreSQL 实例之间同步数据表的变更,特别适合进行数据复制、分发、数据迁移等场景。

- 阅读剩余部分 -

postgres删除指定数据库报错有其他session正在连接的解决办法

瀚高等基于postgres的数据库在删除数据库时常见报错信息:

dropdb -h localhost -p 5432 -U postgres sensen

dropdb: error: database removal failed: ERROR: database "sensen" is being accessed by other users
DETAIL: There is 1 other session using the database.

解决方法:
切换到psql下,执行:SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE datname='你的数据库名字' AND pid<>pg_backend_pid();
退出psql再次删除即可。

pgsql使用pgdump免密码备份

通过crontab执行shell脚本对数据库进行pgdump备份时,需要手动敲入密码的,解决方案:

1、通过配置 pg_hba.conf 文件实现
增加一行:host all all 192.168.1.1/32 trust(注:瀚高数据库不支持此方法)
192.168.1.1替换成自己备份服务器的ip即可。完成这个配置需要重启pgsql服务,这样192.168.1.1上发起的数据库连接就无需再输入密码。修改配置文件需要重启数据库服务。

连接类型

local 这条记录匹配通过 Unix 域套接字进行的联接企图, 没有这种类型的记录,就不允许 Unix 域套接字的联接。
host 这条记录匹配通过TCP/IP网络进行的联接尝试。他既匹配通过ssl方式的连接,也匹配通过非ssl方式的连接。 注意:要使用该选项你要在postgresql.conf文件里设置listen_address选项,不在listen_address里的IP地址是无法匹配到的。因为默认的行为是只在localhost上监听本地连接。
hostssl 这条记录匹配通过在TCP/IP上进行的SSL联接企图。 要使用该选项,服务器编译时必须使用–with-openssl选项,并且在服务器启动时ssl设置是打开的,具体内容可见这里。
hostnossl 这个和上面的hostssl相反,只匹配通过在TCP/IP上进行的非SSL联接企图。

允许访问的数据库

指定这一记录匹配的数据库名。值all指定它匹配所有数据库。可以提供多个数据库名,用逗号分隔它们。在文件名前面放一个@,可以指定一个含有数据库名的单独的文件。
用户名
指定这一记录匹配的数据库角色名。值all指定它匹配所有角色。如果指定的角色是一个组并且希望该组中的所有成员都被包括在内,在该角色名前面放一个+。可以提供多个角色名,用逗号分隔它们。在文件名前面放一个@,可以指定一个含有角色名的单独的文件。

主机IP

指定这一记录匹配的客户端机器的IP地址范围。它包含一个标准点分十进制表示的IP地址和一个CIDR掩码长度。IP地址只能用数字指定,不能写成域或者主机名。掩码长度指示客户端IP地址必须匹配的高位位数。给定IP地址中,在这些位的右边必须是零。IP地址、/和CIDR掩码长度之间不能有任何空格。
典型的CIDR地址例子是:192.0.2.89/32是一个单一主机,192.0.2.0/24是一个小网络,10.6.0.0/16是一个大网络。要指定一个单一主机,对IPv4使用一个CIDR掩码32,对IPv6使用128。在一个网络地址中,不要省略拖尾的零。

ip地址(ip-address)、子网掩码(ip-mask) 这两个字段包含可以看成是标准点分十进制表示的 IP地址/掩码值的一个替代。
例如,使用255.255.255.0 代表一个24位的子网掩码。它们俩放在一起,声明了这条记录匹配的客户机的 IP
地址或者一个IP地址范围。本选项只能在连接方式是host,hostssl或者hostnossl的时候指定。

认证方法

trust 无条件地允许联接,这个方法允许任何可以与PostgreSQL 数据库联接的用户以他们期望的任意 PostgreSQL 数据库用户身份进行联接,而不需要口令。

reject 联接无条件拒绝,常用于从一个组中"过滤"某些主机。

md5 要求客户端提供一个 MD5 加密的口令进行认证,这个方法是允许加密口令存储在pg_shadow里的唯一的一个方法。

sm4 国密算法支持(国产数据库如瀚高)

password 和"md5"一样,但是口令是以明文形式在网络上传递的,我们不应该在不安全的网络上使用这个方式。

2、配置~/.papass
~/ 目录下创建 .pgpass 文件,按照 host:port:dbname:username:password 格式输入对应信息。可以多行配置多个数据库连接信息,再执行:chmod 0600 .pgpass 更改文件权限。
(前提是该备份服务器ip已经按照1中的配置文件配置了允许访问数据库,认证方式为md5、sm4或者password)