PostgreSQL 远程管理越来越简单,6个自动化脚本开胃菜

文摘   2024-10-16 06:00   天津  

开头还是介绍一下群,如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, OceanBase, Sql Server等有问题,有需求都可以加群群内有各大数据库行业大咖,可以解决你的问题。加群请联系 liuaustin3 ,(共2500人左右 1 + 2 + 3 + 4 +5 + 6 + 7)(1 2 3 4 5 群均已爆满,6群超过450人关闭自由申请,新人进7群,准备开8群)


Node.js 今天开始搞这个,原因很简单,因为要写程序对数据库进行管理,但我不想使用麻烦的Python, 超高难度的Go, Node.js正是一个写脚本的好工具,且部署简单,今天我们就开始部署Node.js且用他来管理一些PostgreSQL.

如果下载太慢,是那就是力访问网站太慢,需要修改nodejs源,具体方法下面。


curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
nvm install 22
node -v # should print `v20.18.0`
npm -v # should print `10.8.2`

上面在主机上安装node.js十分方便,安装完毕后,我们通过node -v npm -v 都可以获得对应的版本信息。

在安装中需要安装依赖包,如果依赖包在官方的网站无法进行下载,可以转换镜像源

[root@nodejs ~]# nrm ls
  npm ---------- https://registry.npmjs.org/
  yarn --------- https://registry.yarnpkg.com/
* tencent ------ https://mirrors.cloud.tencent.com/npm/
  cnpm --------- https://r.cnpmjs.org/
  taobao ------- https://registry.npmmirror.com/
  npmMirror ---- https://skimdb.npmjs.com/registry/
  huawei ------- https://repo.huaweicloud.com/repository/npm/

比如我们将镜像源改为腾讯的

[root@nodejs ~]# npm config set registry https://mirrors.cloud.tencent.com/npm/
[root@nodejs ~]# npm install pg

added 1 package in 2s
[root@nodejs ~]# npm list
root@ /root
├── mongo@0.1.0
├── mysql@2.18.1
├── postgres@3.4.4
└── redis@4.7.0


可以通过npm list 来对当前已经安装的模块进行查看。

下面我们简单的写一个访问PostgreSQL数据库的脚本,来远程访问数据库且进行一些信息的查询。

vacuum 有关的脚本

1 查看PostgreSQL 数据库中所有逻辑库中3天没有进行 vacuum,autovacuum的表的列表

const { Client } = require('pg');


const config = {
    host: '192.168.198.130',
    port: 5432,
    user: 'test',
    password: 'test'
};

async function checkTablesNotVacuumed() {
    const dbClient = new Client(config);
    
    try {
      
        await dbClient.connect();

   
        const dbsRes = await dbClient.query('SELECT datname FROM pg_database WHERE datistemplate = false;');
        const databases = dbsRes.rows.map(row => row.datname);

        for (const dbName of databases) {
            const client = new Client({ ...config, database: dbName });
            await client.connect();

            try {
                // 查询没有进行过 VACUUM 或 AUTOVACUUM 操作的表
                const query = `
                    SELECT
                        schemaname,
                        relname,
                        last_vacuum,
                        last_autovacuum
                    FROM
                        pg_stat_all_tables
                    WHERE
                        (last_vacuum IS NULL OR last_vacuum < NOW() - INTERVAL '3 days') AND
                        (last_autovacuum IS NULL OR last_autovacuum < NOW() - INTERVAL '3 days');
                `;

                const res = await client.query(query);
                
       
                if (res.rows.length > 0) {
                    console.log(`数据库: ${dbName} - 以下表在过去3天没有进行过 VACUUM 或 AUTOVACUUM:`);
                    res.rows.forEach(row => {
                        console.log(`表: ${row.schemaname}.${row.relname} | 上次 VACUUM: ${row.last_vacuum} | 上次 AUTOVACUUM: ${row.last_autovacuum}`);
                    });
                } else {
                    console.log(`数据库: ${dbName} - 所有表在过去3天内都有进行 VACUUM 或 AUTOVACUUM 操作。`);
                }
            } catch (err) {
                console.error(`查询数据库 ${dbName} 时出错:`, err);
            } finally {
                await client.end();
            }
        }
    } catch (err) {
        console.error('查询数据库列表时出错:', err);
    } finally {
        await dbClient.end();
    }
}


checkTablesNotVacuumed();

其中用户名和密码需要自己改,链接地址也需要改数据库会自行进行遍历

执行后的结果

2 查找PG数据库中,那个库的那个表执行 vacuum 超过5分钟

const { Client } = require('pg');


const config = {
    host: '192.168.198.130',
    port: 5432,
    user: 'test',
    password: 'test'
};

async function checkLongRunningAutovacuum() {
    const dbClient = new Client(config);
    
    try {
     
        await dbClient.connect();

      
        const dbsRes = await dbClient.query('SELECT datname FROM pg_database WHERE datistemplate = false;');
        const databases = dbsRes.rows.map(row => row.datname);

        for (const dbName of databases) {
            const client = new Client({ ...config, database: dbName });
            await client.connect();

            try {
               
                const query = `
                    SELECT
                        p.relid::regclass AS table_name,
                        a.pid,
                        a.query_start,
                        now() - a.query_start AS duration
                    FROM
                        pg_stat_progress_vacuum p
                    JOIN
                        pg_stat_activity a ON p.pid = a.pid
                    WHERE
                        now() - a.query_start > interval '5 minutes';
                `;

                const res = await client.query(query);
                
               
                if (res.rows.length > 0) {
                    console.log(`数据库: ${dbName} - 以下表的 AUTOVACUUM 操作时间超过 5 分钟:`);
                    res.rows.forEach(row => {
                        console.log(`表: ${row.table_name} | 进程 ID: ${row.pid} | 开始时间: ${row.query_start} | 持续时间: ${row.duration}`);
                    });
                } else {
                    console.log(`数据库: ${dbName} - 没有表的 AUTOVACUUM 操作时间超过 5 分钟。`);
                }
            } catch (err) {
                console.error(`查询数据库 ${dbName} 时出错:`, err);
            } finally {
                await client.end();
            }
        }
    } catch (err) {
        console.error('查询数据库列表时出错:', err);
    } finally {
        await dbClient.end();
    }
}


checkLongRunningAutovacuum();

执行结果

与PostgreSQL权限有关的脚本

3  查询数据库中所有表的权限

const { Client } = require('pg');  

const config = {
    host: '192.168.198.130',
    port: 5432,
    user: 'test',
    password: 'test'
};

async function checkTablePrivileges() {
    const dbClient = new Client(config);
    
    try {
       
        await dbClient.connect();

      
        const dbsRes = await dbClient.query('SELECT datname FROM pg_database WHERE datistemplate = false;');
        const databases = dbsRes.rows.map(row => row.datname);

        for (const dbName of databases) {
            const client = new Client({ ...config, database: dbName });
            await client.connect();

            try {
             
                const query = `
                    SELECT 
                        table_schema, 
                        table_name, 
                        grantee, 
                        privilege_type 
                    FROM 
                        information_schema.table_privileges 
                    ORDER BY 
                        table_schema, 
                        table_name, 
                        grantee;
                `;

                const res = await client.query(query);
                
              
                if (res.rows.length > 0) {
                    console.log(`数据库: ${dbName} - 表权限信息:`);
                    res.rows.forEach(row => {
                        console.log(`模式: ${row.table_schema} | 表: ${row.table_name} | 用户: ${row.grantee} | 权限: ${row.privilege_type}`);
                    });
                } else {
                    console.log(`数据库: ${dbName} - 没有权限信息。`);
                }
            } catch (err) {
                console.error(`查询数据库 ${dbName} 时出错:`, err);
            } finally {
                await client.end();
            }
        }
    } catch (err) {
        console.error('查询数据库列表时出错:', err);
    } finally {
        await dbClient.end();
    }
}


checkTablePrivileges();

结果如下

4 查询赋值账号对表的权限占有的情况,这个脚本会比较复杂,且也是很多DBA搞不定的一个问题,比如你有A用户建立A 库,后续grant A to B 用户,B 用户,那么我咱们查出来与A用户有关联的所有数据库用户且这些用户对A库的那些表有权限,是从A用户赋予的权限上来的。

const { Client } = require('pg');


const roleName = process.argv[2]; 


if (!roleName) {
    console.error('请提供角色名称作为命令行参数,例如: node your_script.js 赋值权限账号');
    process.exit(1);  // 退出程序
}


const config = {
    host: '192.168.198.130',
    port: 5432,
    user: 'test',
    password: 'test',
    database: 'testdb'  // 指定你要连接的数据库
};


async function checkRolePrivileges() {
    const client = new Client(config);

    try {
       
        await client.connect();
        console.log(`已成功连接到 PostgreSQL 数据库,正在查询角色 ${roleName} 的权限信息`);

      
        const query = `
            WITH inherited_roles AS (
                SELECT r1.rolname AS master_role,
                       r2.rolname AS member_role
                FROM pg_roles AS r1
                JOIN pg_auth_members m ON r1.oid = m.roleid
                JOIN pg_roles r2 ON m.member = r2.oid
                WHERE r1.rolname = $1
            )
            SELECT t_grant_table.grantee AS user_name,
                   ir.member_role AS member,
                   t_grant_table.table_catalog AS database_name,
                   t_grant_table.table_schema,
                   t_grant_table.table_name,
                   t_grant_table.privilege_type
            FROM information_schema.role_table_grants t_grant_table
            JOIN inherited_roles ir ON t_grant_table.grantee = ir.master_role;
        `;

        const res = await client.query(query, [roleName]);
        
      
        if (res.rows.length > 0) {
            console.log('以下是角色权限信息:');
            res.rows.forEach(row => {
                console.log(`用户: ${row.user_name} | 成员: ${row.member} | 数据库: ${row.database_name} | 模式: ${row.table_schema} | 表: ${row.table_name} | 权限: ${row.privilege_type}`);
            });
        } else {
            console.log('没有找到相关权限信息。');
        }
    } catch (err) {
        console.error('查询角色权限时出错:', err);
    } finally {
       
        await client.end();
        console.log('数据库连接已关闭');
    }
}


checkRolePrivileges();

执行的结果,这里注意参数给的是system,这里在PG数据库中建立了system账号,且建立了一个数据库叫system,system对system数据库具有owner的权限,同时我们建立一个数据库账号叫nomarl,我们使用了grant system to nomarl,然后我们想查询当前与system 账号有关的连的账号是那些,且他们都对那些表有权限,什么权限,就可以通过下面的脚本进行查询了。

与连接有关的

5 清理超过你指定时间的数据库连接


const { Client } = require('pg');


const database = process.argv[2]; // 数据库名
const idleTime = process.argv[3]; // 时间限制,如 '5 minutes'
const user = process.argv[4];     // 可选的用户名


if (!database || !idleTime) {
    console.error('请提供数据库名称和时间限制,例如:node clean_connections.js <database> <time> [<user>]');
    process.exit(1);
}


const config = {
    host: '192.168.198.130',
    port: 5432,
    user: 'test',  
    password: 'test',  
    database: 'postgres' 
};


async function cleanIdleConnections() {
    const client = new Client(config);

    try {
    
        await client.connect();


        let query = `
            SELECT pid, usename, state, backend_start, query_start, state_change
            FROM pg_stat_activity
            WHERE datname = $1
            AND (now() - query_start) > $2::interval
        `;

        const params = [database, idleTime];
        if (user) {
            query += ` AND usename = $3`;
            params.push(user);
        }

     
        const res = await client.query(query, params);

      
        if (res.rows.length > 0) {
            console.log(`以下连接超过 ${idleTime} 被清理:`);
            for (const row of res.rows) {
                console.log(`终止连接:PID: ${row.pid}, 用户: ${row.usename}, 开始时间: ${row.backend_start}`);

          
                await client.query(`SELECT pg_terminate_backend($1)`, [row.pid]);
            }
        } else {
            console.log(`没有发现连接超过 ${idleTime} 的用户。`);
        }

    } catch (err) {
        console.error('执行查询时出错:', err);
    } finally {
       
        await client.end();
    }
}


cleanIdleConnections();

这个脚本的功能是通过执行带有参数的命令,直接将指定的数据库的超过预定时间的连接,直接termial,通过这样的方法满足管理者的需求。

具体执行的效果图

命令方式,node kill_conncion.js 数据库名 ‘时间’ 用户名(可选)

6 查询当前数据库指定数据库长时间运行语句的连接

const { Client } = require('pg');


const databaseName = process.argv[2]; 
const timeLimit = process.argv[3];   


const config = {
    host: '192.168.198.130',  
    port: 5432,              
    user: 'test',       
    password: 'test' 
};


const client = new Client({
    ...config,
    database: databaseName     
});


async function checkLongRunningQueries() {
    try {
        await client.connect();

        const query = `
            SELECT
                pid,
                usename AS username,
                query,
                state,
                now() - query_start AS duration,
                query_start
            FROM
                pg_stat_activity
            WHERE
                state = 'active' 
                AND now() - query_start > $1::interval
                AND query != '<IDLE>' 
            ORDER BY
                duration DESC;
        `;

     
        const res = await client.query(query, [timeLimit]);

        if (res.rows.length > 0) {
            console.log(`查询到执行时间超过 ${timeLimit} 的SQL语句:`);
            res.rows.forEach(row => {
                console.log(`PID: ${row.pid} | 用户: ${row.username} | 执行时间: ${row.duration} | 开始时间: ${row.query_start} | SQL: ${row.query}`);
            });
        } else {
            console.log(`没有查询到执行时间超过 ${timeLimit} 的 SQL 语句。`);
        }
    } catch (err) {
        console.error('查询出错:', err);
    } finally {
        await client.end();
    }
}


checkLongRunningQueries();

脚本运行的结果,直接打印那个长时间进行查询的语句超过了命中给定的时间

node monitor_long_query.js system '1 seconds'

node monitor_long_query.js 数据库名 ‘指定时间’

注:以上的脚本,直接可以从文章中截取,代码不是截图,脚本的名字是人为进行命名不存在强制性,拷贝下代码可以自行命名脚本。

注意:node.js 版本为 22 最新版 测试的PostgreSQL 为PG14.7  (低版本的PG部分脚本可能无法使用) 脚本中的用户名密码权限为 superuser



置顶文章

PolarDB 杀疯了,Everywhere Everytime Everydatabase on Serverless

OceanBase  学习记录 --  开始入门

开发问MySQL 大事务证据,一个脚本堵上他的嘴

瞬间成为MongoDB专家,8个脚本都写好了,一用一个不吱声

MySQL还用学吗?这谁问的 “好问题” !

MySQL 8.0x 到 9.0均可能崩溃--云厂商开发指责 MYSQL不测试就推新版本?

MySQL 让你还用5.7 出事了吧,用着用着5.7崩了

MySQL 8.0 小版本更新要点,那个小版本更稳定(8.0.24-8.0.37)

MySQL 8.0 版本更新 要点 列表 (8.0-8.0.23)

DBA 失职导致 PostgreSQL 日志疯涨

微软 “爱” 上PostgreSQL, PG  “嫁给” 微软!

撕逼!PostgreSQL 和 MongoDB 开撕,MySQL却躺枪

阿里云 安全扫描 ,说我PostgreSQL 自建主机极度不安全, 谁的问题?

PostgreSQL 13.0-13.15 功能更新和bug fixed列表

撕逼!PostgreSQL 和 MongoDB 开撕,MySQL却躺枪




往期热门文章:


PostgreSQL  哪些版本尽量避免使用,版本更新重点明晰(PG12)

PostgreSQL  15 16 小版本更新信息小结 版本更新是不是挤牙膏

PostgreSQL 14 小版本分析,有那个版本不建议使用

Windows 是MySQL和PostgreSQL高性能数据库的坟墓

PostgreSQL 具有createdb的用户无法创建数据库的原因(之一)

道歉贴,为最近写的一篇“垃圾贴”

PostgreSQL 同样的语句 一会快 一会慢到底怎么回事,
MongoDB  系统IOPS 告警系统处于崩溃,优化语句从1秒优化到1毫秒解决问题
云原生数据库是青出于蓝胜于蓝,还是数据库产品的倒退?
专访唐建法-从MongoDB中国第一人到TapData掌门人的故事
MySQL 8.0x 到 9.0均可能崩溃--云厂商开发指责 MYSQL不测试就推新版本?
DISS 阿里云 DAS数据库服务,阿里云数据库服务的毒瘤

临时工说:DBA 7*24H 给2万的工作,到底去不去?

PolarDB 最近遇到加字段加不上的问题 与 使用PolarDB 三年感受与恳谈

PostgreSQL 稳定性平台 PG中文社区大会--杭州来去匆匆

MySQL 让你还用5.7 出事了吧,用着用着5.7崩了

临时工访谈:问金融软件开发总监  哪些业务不用传统数据库
PolarDB  Serverless POC测试中有没有坑与发现的疑问
临时工访谈:PolarDB Serverless  发现“大”问题了  之 灭妖记 续集
临时工访谈:庙小妖风大-PolarDB 组团镇妖 之 他们是第一
PolarDB for PostgreSQL  有意思吗?有意思呀
PolarDB  Serverless POC测试中有没有坑与发现的疑问

MySQL 的SQL引擎很差吗?由一个同学提出问题引出的实验

临时工访谈:从国产数据库 到 普罗大众的产品 !与在美国创业软件公司老板对话

PostgreSQL 如何通过工具来分析PG 内存泄露

MySQL 的SQL引擎很差吗?由一个同学提出问题引出的实验
临时工访谈:我很普通,但我也有生存的权利,大龄程序员 求职贴
临时工说: 快速识别 “海洋贝壳类” 数据库方法速递
临时工说:国产 数据库 销售人员  图鉴
临时工说:DBA 是不是阻碍国产数据库发展的毒瘤 ,是不是?从国产
PostgreSQL   玩PG我们是认真的,vacuum 稳定性平台我们有了
临时工说:裁员裁到 DBA 咋办  临时工教你 套路1 2 3
PolarDB  搞那么多复杂磁盘计费的东西,抽筋了吗?
临时工说:OceanBase 到访,果然数据库的世界很卷,没边
MONGODB  ---- Austindatabases  历年文章合集
MYSQL  --Austindatabases 历年文章合集
POSTGRESQL --Austindatabaes 历年文章整理
POLARDB  -- Ausitndatabases 历年的文章集合
PostgreSQL  查询语句开发写不好是必然,不是PG的锅
SQL SERVER 如何实现UNDO REDO  和PostgreSQL 有近亲关系吗
MongoDB 2023纽约 MongoDB 大会 -- 我们怎么做的新一代引擎 SBE Mongodb 7.0双擎力量(译)
MongoDB 2023年度纽约 MongoDB 年度大会话题 -- MongoDB 数据模式与建模
MongoDB  双机热备那篇文章是  “毒”
MongoDB   会丢数据吗?在次补刀MongoDB  双机热备
临时工说:从人性的角度来分析为什么公司内MySQL 成为少数派,PolarDB 占领高处
POLARDB  到底打倒了谁  PPT 分享 (文字版)
PostgreSQL  字符集乌龙导致数据查询排序的问题,与 MySQL 稳定 "PG不稳定"
PostgreSQL  Patroni 3.0 新功能规划 2023年 纽约PG 大会 (音译)

Austindatabases 公众号,主要围绕数据库技术(PostgreSQL, MySQL, Mongodb, Redis, SqlServer,PolarDB, Oceanbase 等)和职业发展,国外数据库大会音译,国外大型IT信息类网站文章翻译,等,希望能和您共同发展。



AustinDatabases
关于数据库相关的知识分享
 最新文章