分类 PostgreSQL 下的文章

Foreign Data Wrapper(FDW)创建

在PostgreSQL中,你可以通过多种方式创建外部数据封装器(Foreign Data Wrapper,FDW)。FDW 允许你访问存储在其他数据库管理系统中的数据,例如Oracle、MySQL、Microsoft SQL Server等。支持的外部文件有:csv、josn、pg_dump、xml等。下面是一些常用的方法来实现这一功能。

使用 PostgreSQL 自带的 FDW
PostgreSQL 自带一些 FDW,如 postgres_fdw、file_fdw 等。你可以使用这些内置的 FDW 来连接和查询其他数据库系统中的数据。

- 阅读剩余部分 -

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再次删除即可。