PostgreSQL教程FG122-libpq库实操:数据库连接与数据查询
本文档风哥主要介绍PostgreSQL数据库的libpq库实操,包括数据库连接、数据查询、批量操作等内容,风哥教程参考PostgreSQL官方文档libpq – C Library内容,适合开发人员在学习和测试中使用,如果要应用于生产环境则需要自行确认。
Part01-基础概念与理论知识
1.1 libpq库连接概念
libpq库的连接概念包括连接字符串、连接参数、连接状态等。连接是应用程序与PostgreSQL服务器之间的通信通道,所有的数据库操作都需要通过连接来进行。更多视频教程www.fgedu.net.cn
- 连接字符串:包含连接所需的所有信息,如主机、端口、数据库名、用户名、密码等
- 连接参数:控制连接行为的各种参数,如超时、SSL模式等
- 连接状态:表示连接的当前状态,如空闲、繁忙、关闭等
- 连接池:管理多个连接的容器,提高连接重用率
1.2 libpq库查询概念
libpq库的查询概念包括SQL执行、结果处理、预处理语句等。查询是应用程序向PostgreSQL服务器发送SQL语句并获取结果的过程。
1.3 libpq库错误处理
libpq库的错误处理包括错误消息、错误代码、错误状态等。妥善处理错误对于保证应用程序的稳定性和可靠性至关重要。
Part02-生产环境规划与建议
2.1 libpq库连接池设计
连接池是管理数据库连接的重要机制,可以提高应用程序性能和可靠性。
– 连接池大小:根据应用程序并发数和数据库服务器能力确定
– 连接生命周期:设置合理的连接超时和最大空闲时间
– 连接验证:定期验证连接是否有效
– 连接分配:采用公平的连接分配策略
– 错误处理:妥善处理连接失败的情况
– 监控:监控连接池状态和使用情况
# 连接池实现方式
– 自行实现:使用C语言实现简单的连接池
– 使用第三方库:如pgpool-II、PgBouncer等
– 应用服务器集成:如Java应用服务器的连接池
# 连接池大小计算公式
连接池大小 = (核心数 × 2)+ 有效磁盘数
2.2 libpq库查询优化
查询优化是提高应用程序性能的关键因素。
– 使用预处理语句:提高查询执行效率,防止SQL注入
– 批量操作:减少网络往返,提高吞吐量
– 结果集处理:合理处理大型结果集,避免内存溢出
– 索引使用:确保查询使用适当的索引
– 事务管理:合理使用事务,减少事务开销
– 连接复用:使用连接池,避免频繁建立和关闭连接
# 查询性能监控
– 使用EXPLAIN分析查询执行计划
– 监控查询执行时间
– 监控网络传输时间
– 监控服务器负载
2.3 libpq库安全考虑
安全是生产环境中的重要考虑因素。
- 密码管理:避免在代码中硬编码密码,使用环境变量或配置文件
- SQL注入:使用预处理语句,避免拼接SQL字符串
- SSL连接:使用SSL加密连接,保护数据传输安全
- 权限控制:使用最小权限原则,限制数据库用户权限
- 错误处理:避免向客户端暴露详细的错误信息
Part03-生产环境项目实施方案
3.1 libpq库连接实操
3.1.1 基本连接示例
#include
#include
#include
int main() {
PGconn *conn;
char *conninfo;
// 连接字符串
conninfo = “fgedu.net.cn=localfgedu.net.cn port=5432 fgedudb=fgedudb fgedu=pgsql password=postgres_password”;
// 建立连接
conn = PQconnectdb(conninfo);
// 检查连接状态
if (PQstatus(conn) != CONNECTION_OK) {
fprintf(stderr, “连接失败: %s\n”, PQerrorMessage(conn));
PQfinish(conn);
return 1;
}
printf(“连接成功!\n”);
printf(“服务器版本: %s\n”, PQserverVersion(conn) ? PQserverVersion(conn) : “未知”);
printf(“协议版本: %d\n”, PQprotocolVersion(conn));
// 关闭连接
PQfinish(conn);
return 0;
}
// 编译命令
$ gcc -o basic_connect basic_connect.c -lpq
// 运行结果
$ ./basic_connect
连接成功!
服务器版本: 150000
协议版本: 3
3.1.2 连接池实现
#include
#include
#include
#define MAX_CONNECTIONS 10
typedef struct {
PGconn *connections[MAX_CONNECTIONS];
int used[MAX_CONNECTIONS];
int count;
pthread_mutex_t lock;
} ConnectionPool;
ConnectionPool *create_pool(const char *conninfo) {
ConnectionPool *pool = (ConnectionPool *)malloc(sizeof(ConnectionPool));
pool->count = 0;
pthread_mutex_init(&pool->lock, NULL);
// 初始化连接
for (int i = 0; i < MAX_CONNECTIONS; i++) {
pool->connections[i] = PQconnectdb(conninfo);
if (PQstatus(pool->connections[i]) == CONNECTION_OK) {
pool->used[i] = 0;
pool->count++;
} else {
fprintf(stderr, “连接初始化失败: %s\n”, PQerrorMessage(pool->connections[i]));
PQfinish(pool->connections[i]);
pool->connections[i] = NULL;
pool->used[i] = 1;
}
}
printf(“连接池创建成功,可用连接数: %d\n”, pool->count);
return pool;
}
PGconn *get_connection(ConnectionPool *pool) {
pthread_mutex_lock(&pool->lock);
for (int i = 0; i < MAX_CONNECTIONS; i++) {
if (pool->connections[i] && !pool->used[i]) {
pool->used[i] = 1;
pthread_mutex_unlock(&pool->lock);
return pool->connections[i];
}
}
pthread_mutex_unlock(&pool->lock);
return NULL;
}
void release_connection(ConnectionPool *pool, PGconn *conn) {
pthread_mutex_lock(&pool->lock);
for (int i = 0; i < MAX_CONNECTIONS; i++) {
if (pool->connections[i] == conn) {
pool->used[i] = 0;
break;
}
}
pthread_mutex_unlock(&pool->lock);
}
void destroy_pool(ConnectionPool *pool) {
for (int i = 0; i < MAX_CONNECTIONS; i++) {
if (pool->connections[i]) {
PQfinish(pool->connections[i]);
}
}
pthread_mutex_destroy(&pool->lock);
free(pool);
printf(“连接池已销毁\n”);
}
int main() {
const char *conninfo = “fgedu.net.cn=localfgedu.net.cn port=5432 fgedudb=fgedudb fgedu=pgsql password=postgres_password”;
ConnectionPool *pool = create_pool(conninfo);
// 获取连接
PGconn *conn1 = get_connection(pool);
PGconn *conn2 = get_connection(pool);
if (conn1) {
printf(“成功获取连接1\n”);
}
if (conn2) {
printf(“成功获取连接2\n”);
}
// 释放连接
if (conn1) {
release_connection(pool, conn1);
printf(“连接1已释放\n”);
}
if (conn2) {
release_connection(pool, conn2);
printf(“连接2已释放\n”);
}
// 销毁连接池
destroy_pool(pool);
return 0;
}
// 编译命令
$ gcc -o connection_pool connection_pool.c -lpq -pthread
// 运行结果
$ ./connection_pool
连接池创建成功,可用连接数: 10
成功获取连接1
成功获取连接2
连接1已释放
连接2已释放
连接池已销毁
3.2 libpq库查询实操
3.2.1 执行查询并处理结果
#include
#include
#include
int main() {
PGconn *conn;
PGresult *res;
int nFields, nRows, i, j;
// 建立连接
conn = PQconnectdb(“fgedu.net.cn=localfgedu.net.cn port=5432 fgedudb=fgedudb fgedu=pgsql password=postgres_password”);
if (PQstatus(conn) != CONNECTION_OK) {
fprintf(stderr, “连接失败: %s\n”, PQerrorMessage(conn));
PQfinish(conn);
return 1;
}
// 执行查询
res = PQexec(conn, “SELECT id, name, age FROM fgedu_employees WHERE age > 30”);
// 检查查询结果
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr, “查询失败: %s\n”, PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
return 1;
}
// 获取结果集信息
nFields = PQnfields(res);
nRows = PQntuples(res);
// 打印列名
for (i = 0; i < nFields; i++) {
printf("%s\t", PQfname(res, i));
}
printf("\n");
// 打印数据
for (i = 0; i < nRows; i++) {
for (j = 0; j < nFields; j++) {
printf("%s\t", PQgetvalue(res, i, j));
}
printf("\n");
}
// 释放结果
PQclear(res);
// 关闭连接
PQfinish(conn);
return 0;
}
// 编译命令
$ gcc -o query_result query_result.c -lpq
// 运行结果
$ ./query_result
id name age
3 王五 35
6 吴十 33
3.2.2 使用预处理语句
#include
#include
#include
int main() {
PGconn *conn;
PGresult *res;
const char *paramValues[1];
int paramLengths[1];
int paramFormats[1];
// 建立连接
conn = PQconnectdb(“fgedu.net.cn=localfgedu.net.cn port=5432 fgedudb=fgedudb fgedu=pgsql password=postgres_password”);
if (PQstatus(conn) != CONNECTION_OK) {
fprintf(stderr, “连接失败: %s\n”, PQerrorMessage(conn));
PQfinish(conn);
return 1;
}
// 准备预处理语句
res = PQprepare(conn, “get_employees”,
“SELECT id, name, age FROM fgedu_employees WHERE age > $1”,
1, NULL);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, “准备语句失败: %s\n”, PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
return 1;
}
PQclear(res);
// 设置参数
paramValues[0] = “28”;
paramLengths[0] = -1; // 自动计算长度
paramFormats[0] = 0; // 文本格式
// 执行预处理语句
res = PQexecPrepared(conn, “get_employees”,
1, paramValues, paramLengths, paramFormats, 0);
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr, “执行语句失败: %s\n”, PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
return 1;
}
// 打印结果
int nFields = PQnfields(res);
int nRows = PQntuples(res);
for (int i = 0; i < nFields; i++) { printf("%s\t", PQfname(res, i)); } printf("\n"); for (int i = 0; i < nRows; i++) { for (int j = 0; j < nFields; j++) { printf("%s\t", PQgetvalue(res, i, j)); } printf("\n"); } // 释放结果 PQclear(res); // 关闭连接 PQfinish(conn); return 0; } // 编译命令 $ gcc -o prepared_statement prepared_statement.c -lpq // 运行结果 $ ./prepared_statement id name age 2 风哥2号 30 3 王五 35 6 吴十 33
3.3 libpq库批量操作
#include
#include
#include
int main() {
PGconn *conn;
PGresult *res;
// 建立连接
conn = PQconnectdb(“fgedu.net.cn=localfgedu.net.cn port=5432 fgedudb=fgedudb fgedu=pgsql password=postgres_password”);
if (PQstatus(conn) != CONNECTION_OK) {
fprintf(stderr, “连接失败: %s\n”, PQerrorMessage(conn));
PQfinish(conn);
return 1;
}
// 开始事务
res = PQexec(conn, “BEGIN”);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, “开始事务失败: %s\n”, PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
return 1;
}
PQclear(res);
// 批量插入数据
const char *insert_sql = “INSERT INTO fgedu_employees (name, age) VALUES “;
char batch_sql[1024] = “”;
strcat(batch_sql, insert_sql);
// 添加多条数据
strcat(batch_sql, “(‘郑十一’, 27),”);
strcat(batch_sql, “(‘王十二’, 31),”);
strcat(batch_sql, “(‘赵十三’, 29),”);
strcat(batch_sql, “(‘钱十四’, 34),”);
strcat(batch_sql, “(‘孙十五’, 26)”);
// 执行批量插入
res = PQexec(conn, batch_sql);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, “批量插入失败: %s\n”, PQerrorMessage(conn));
PQclear(res);
PQexec(conn, “ROLLBACK”);
PQfinish(conn);
return 1;
}
printf(“批量插入成功! 影响行数: %s\n”, PQcmdTuples(res));
PQclear(res);
// 提交事务
res = PQexec(conn, “COMMIT”);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, “提交事务失败: %s\n”, PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
return 1;
}
PQclear(res);
// 关闭连接
PQfinish(conn);
return 0;
}
// 编译命令
$ gcc -o bulk_insert bulk_insert.c -lpq
// 运行结果
$ ./bulk_insert
批量插入成功! 影响行数: 5
Part04-生产案例与实战讲解
4.1 libpq库连接常见问题
在使用libpq库连接数据库时,可能会遇到以下问题:
4.1.1 连接超时
# 分析步骤:
# 1. 检查网络连接
$ ping localfgedu.net.cn
$ telnet localfgedu.net.cn 5432
# 2. 检查PostgreSQL服务状态
$ sudo systemctl status postgresql
# 3. 检查连接字符串
conninfo = “fgedu.net.cn=localfgedu.net.cn port=5432 fgedudb=fgedudb fgedu=pgsql password=postgres_password connect_timeout=10”;
# 4. 解决方法
# 增加连接超时时间
conninfo = “fgedu.net.cn=localfgedu.net.cn port=5432 fgedudb=fgedudb fgedu=pgsql password=postgres_password connect_timeout=30”;
# 检查网络状态
$ netstat -tuln | grep 5432
# 检查防火墙设置
$ sudo firewall-cmd –list-ports
4.2 libpq库查询常见问题
在使用libpq库执行查询时,可能会遇到以下问题:
4.2.1 查询执行缓慢
# 分析步骤:
# 1. 分析查询执行计划
$ sudo -u pgsql psql -c “EXPLAIN ANALYZE SELECT * FROM fgedu_employees WHERE age > 30”;
# 2. 检查索引
$ sudo -u pgsql psql -c “\d fgedu_employees”;
# 3. 优化查询
// 使用索引
res = PQexec(conn, “CREATE INDEX idx_employees_age ON fgedu_employees(age)”);
// 优化查询语句
res = PQexec(conn, “SELECT id, name, age FROM fgedu_employees WHERE age > 30”);
# 4. 解决方法
# 创建适当的索引
# 优化查询语句
# 增加服务器资源
# 使用连接池
4.3 libpq库实战案例
# 1. 项目结构
$ mkdir -p employee_management/src
$ cd employee_management
# 2. 创建源文件
$ vi src/main.c
#include // 函数声明 int main() { // 连接数据库 // 创建表 // 插入员工 // 查询员工 // 更新员工 // 查询员工 // 删除员工 // 查询员工 // 清理资源 return 0; PGconn *connect_db() { conninfo = “fgedu.net.cn=localfgedu.net.cn port=5432 fgedudb=fgedudb fgedu=pgsql password=postgres_password”; if (PQstatus(conn) != CONNECTION_OK) { printf(“连接数据库成功!\n”); void create_table(PGconn *conn) { res = PQexec(conn, “CREATE TABLE fgedu_IF NOT EXISTS fgedu_employees (\n” if (PQresultStatus(res) != PGRES_COMMAND_OK) { printf(“创建表成功!\n”); void insert_employee(PGconn *conn, const char *name, int age, const char *department) { sprintf(query, “INSERT INTO fgedu_employees (name, age, department) VALUES (‘%s’, %d, ‘%s’)”, res = PQexec(conn, query); if (PQresultStatus(res) != PGRES_COMMAND_OK) { printf(“插入员工 %s 成功!\n”, name); void query_employees(PGconn *conn) { res = PQexec(conn, “SELECT id, name, age, department FROM fgedu_employees”); if (PQresultStatus(res) != PGRES_TUPLES_OK) { nFields = PQnfields(res); // 打印列名
#include
#include
#include
PGconn *connect_db();
void create_table(PGconn *conn);
void insert_employee(PGconn *conn, const char *name, int age, const char *department);
void query_employees(PGconn *conn);
void update_employee(PGconn *conn, int id, const char *name, int age, const char *department);
void delete_employee(PGconn *conn, int id);
void cleanup(PGconn *conn);
PGconn *conn;
conn = connect_db();
if (!conn) {
return 1;
}
create_table(conn);
insert_employee(conn, “风哥1号”, 25, “技术部”);
insert_employee(conn, “风哥2号”, 30, “市场部”);
insert_employee(conn, “王五”, 35, “财务部”);
printf(“\n员工列表:\n”);
query_employees(conn);
update_employee(conn, 1, “风哥1号”, 26, “技术部”);
printf(“\n更新后员工列表:\n”);
query_employees(conn);
delete_employee(conn, 3);
printf(“\n删除后员工列表:\n”);
query_employees(conn);
cleanup(conn);
}
PGconn *conn;
char *conninfo;
conn = PQconnectdb(conninfo);
fprintf(stderr, “连接失败: %s\n”, PQerrorMessage(conn));
return NULL;
}
return conn;
}
PGresult *res;
“id SERIAL PRIMARY KEY,\n”
“name VARCHAR(100) NOT NULL,\n”
“age INTEGER NOT NULL,\n”
“department VARCHAR(100) NOT NULL\n”
“)”);
fprintf(stderr, “创建表失败: %s\n”, PQerrorMessage(conn));
PQclear(res);
return;
}
PQclear(res);
}
PGresult *res;
char query[512];
name, age, department);
fprintf(stderr, “插入失败: %s\n”, PQerrorMessage(conn));
PQclear(res);
return;
}
PQclear(res);
}
PGresult *res;
int nFields, nRows, i, j;
fprintf(stderr, “查询失败: %s\n”, PQerrorMessage(conn));
PQclear(res);
return;
}
nRows = PQntuples(res);
for (i = 0; i < nFields; i++) {
printf("%s\t", PQfname(res, i));
}
printf("\n");
// 打印数据
for (i = 0; i < nRows; i++) {
for (j = 0; j < nFields; j++) {
printf("%s\t", PQgetvalue(res, i, j));
}
printf("\n");
}
PQclear(res);
}
void update_employee(PGconn *conn, int id, const char *name, int age, const char *department) {
PGresult *res;
char query[512];
sprintf(query, "UPDATE fgedu_employees SET name='%s', age=%d, department='%s' WHERE id=%d",
name, age, department, id);
res = PQexec(conn, query);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "更新失败: %s\n", PQerrorMessage(conn));
PQclear(res);
return;
}
printf("更新员工 ID=%d 成功!\n", id);
PQclear(res);
}
void delete_employee(PGconn *conn, int id) {
PGresult *res;
char query[256];
sprintf(query, "DELETE FROM fgedu_employees WHERE id=%d", id);
res = PQexec(conn, query);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "删除失败: %s\n", PQerrorMessage(conn));
PQclear(res);
return;
}
printf("删除员工 ID=%d 成功!\n", id);
PQclear(res);
}
void cleanup(PGconn *conn) {
if (conn) {
PQfinish(conn);
printf("连接已关闭!\n");
}
}
# 3. 创建Makefile
$ vi Makefile
CC = gcc
CFLAGS = -Wall -Wextra
LIBS = -lpq
all: employee_management
employee_management: src/main.c
$(CC) $(CFLAGS) -o employee_management src/main.c $(LIBS)
clean:
rm -f employee_management
# 4. 编译和运行
$ make
$ ./employee_management
# 运行结果
连接数据库成功!
创建表成功!
插入员工 风哥1号 成功!
插入员工 风哥2号 成功!
插入员工 王五 成功!
员工列表:
id name age department
1 风哥1号 25 技术部
2 风哥2号 30 市场部
3 王五 35 财务部
更新员工 ID=1 成功!
更新后员工列表:
id name age department
1 风哥1号 26 技术部
2 风哥2号 30 市场部
3 王五 35 财务部
删除员工 ID=3 成功!
删除后员工列表:
id name age department
1 风哥1号 26 技术部
2 风哥2号 30 市场部
连接已关闭!
Part05-风哥经验总结与分享
5.1 libpq库最佳实践
libpq库最佳实践:
- 连接管理:使用连接池,避免频繁建立和关闭连接
- 错误处理:检查所有libpq函数的返回值,妥善处理错误
- 资源管理:及时释放结果和连接,避免内存泄漏
- 参数化查询:使用预处理语句,避免SQL注入
- 批量操作:使用批量插入和更新,提高性能
- 事务管理:合理使用事务,确保数据一致性
- 性能优化:优化查询语句,使用适当的索引
- 安全性:使用SSL连接,避免硬编码密码
5.2 libpq库性能调优
# 1. 连接优化
– 使用连接池:减少连接建立和关闭的开销
– 适当设置连接超时:避免连接等待时间过长
– 使用连接复用:在多个请求之间复用连接
# 2. 查询优化
– 使用预处理语句:提高查询执行效率
– 批量操作:减少网络往返次数
– 结果集处理:使用游标处理大型结果集
– 索引使用:确保查询使用适当的索引
# 3. 网络优化
– 使用SSL连接:保护数据传输安全
– 适当设置缓冲区大小:提高数据传输效率
– 减少网络往返:使用批量操作和存储过程
# 4. 服务器优化
– 增加max_connections参数:支持更多并发连接
– 优化shared_buffers参数:提高缓存效率
– 调整work_mem参数:提高排序和哈希操作性能
– 优化maintenance_work_mem参数:提高维护操作性能
5.3 libpq库工具与资源
libpq库相关工具与资源:
from oracle:www.itpux.com
- pgAdmin:PostgreSQL图形化管理工具
- libpqxx:C++封装的libpq库
- psycopg2:Python的PostgreSQL接口(基于libpq)
- node-postgres:Node.js的PostgreSQL接口
- JDBC:Java的PostgreSQL接口
- ODBC:ODBC驱动(基于libpq)
- PostgreSQL官方文档:https://www.postgresql.org/docs/current/libpq.html
- PostgreSQL社区:https://www.postgresql.org/community/
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
