【C++八股文】数据库篇:从连接到优化全面解析
·
1. 数据库基础概念
1.1 数据库类型
数据库主要分为关系型数据库和非关系型数据库两大类。
-
关系型数据库:采用表格形式存储数据,使用SQL进行查询操作。常见的有:
- MySQL
- SQL Server
- Oracle
- PostgreSQL
- SQLite
-
非关系型数据库:不以表格形式存储数据,包括键值对、文档、列存储等形式。常见的有:
- Redis
- Memcache
- MongoDB
- HBase
1.2 关键操作区别
DELETE、TRUNCATE 和 DROP 的区别
| 操作 | 类型 | 可否回滚 | 是否记录日志 | 执行速度 | 影响范围 |
|---|---|---|---|---|---|
| DELETE | DML | 可以 | 记录 | 最慢 | 删除部分或全部行数据 |
| TRUNCATE | DDL | 不可以 | 不记录 | 中等 | 删除所有行数据,ID从0开始 |
| DROP | DDL | 不可以 | 不记录 | 最快 | 删除表数据及表结构 |
2. C++连接数据库
2.1 MySQL连接示例
C++连接MySQL数据库通常使用MySQL Connector/C++库
#include <iostream>
#include <cppconn/driver.h>
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>
int main() {
try {
// 创建数据库驱动
sql::mysql::MySQL_Driver *driver;
sql::Connection *con;
// 初始化驱动并连接数据库
driver = sql::mysql::get_mysql_driver_instance();
con = driver->connect("tcp://127.0.0.1:3306", "username", "password");
// 选择数据库
con->setSchema("test_db");
std::cout << "成功连接到数据库!" << std::endl;
// 执行查询
sql::Statement *stmt;
sql::ResultSet *res;
stmt = con->createStatement();
res = stmt->executeQuery("SELECT * FROM users");
// 处理结果
while (res->next()) {
std::cout << "ID: " << res->getInt("id");
std::cout << ", Name: " << res->getString("name");
std::cout << ", Age: " << res->getInt("age") << std::endl;
}
// 释放资源
delete res;
delete stmt;
delete con;
} catch (sql::SQLException &e) {
std::cerr << "SQL错误: " << e.what() << std::endl;
std::cerr << "错误代码: " << e.getErrorCode() << std::endl;
}
return 0;
}
2.2 SQLite连接示例
对于轻量级应用,SQLite是一个不错的选择:
#include <iostream>
#include <sqlite3.h>
int main() {
sqlite3 *db;
char *zErrMsg = 0;
int rc;
// 打开数据库
rc = sqlite3_open("test.db", &db);
if (rc) {
std::cerr << "无法打开数据库: " << sqlite3_errmsg(db) << std::endl;
return(1);
} else {
std::cout << "成功打开数据库!" << std::endl;
}
// 创建表
const char *sql = "CREATE TABLE IF NOT EXISTS users ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"name TEXT NOT NULL, "
"age INTEGER);";
rc = sqlite3_exec(db, sql, 0, 0, &zErrMsg);
if (rc != SQLITE_OK) {
std::cerr << "SQL错误: " << zErrMsg << std::endl;
sqlite3_free(zErrMsg);
} else {
std::cout << "表创建成功" << std::endl;
}
sqlite3_close(db);
return 0;
}
3. 数据库操作
3.1 CRUD操作
在C++中执行基本的增删改查操作:
// 插入数据
sql::PreparedStatement *pstmt = con->prepareStatement(
"INSERT INTO users(name, age) VALUES (?, ?)");
pstmt->setString(1, "John Doe");
pstmt->setInt(2, 30);
pstmt->executeUpdate();
// 查询数据
sql::ResultSet *res = stmt->executeQuery(
"SELECT * FROM users WHERE age > 25");
// 更新数据
sql::PreparedStatement *pstmt = con->prepareStatement(
"UPDATE users SET age = ? WHERE name = ?");
pstmt->setInt(1, 31);
pstmt->setString(2, "John Doe");
pstmt->executeUpdate();
// 删除数据
sql::PreparedStatement *pstmt = con->prepareStatement(
"DELETE FROM users WHERE id = ?");
pstmt->setInt(1, 5);
pstmt->executeUpdate();
3.2 使用预处理语句
预处理语句不仅能提高性能,还能有效防止SQL注入攻击:
// 使用预处理语句插入数据
sql::PreparedStatement *pstmt = con->prepareStatement(
"INSERT INTO users (name, email, age) VALUES (?, ?, ?)");
pstmt->setString(1, "Alice");
pstmt->setString(2, "alice@example.com");
pstmt->setInt(3, 30);
pstmt->executeUpdate();
// 使用预处理语句查询
sql::PreparedStatement *pstmt = con->prepareStatement(
"SELECT name, age FROM users WHERE age > ? AND name LIKE ?");
pstmt->setInt(1, 25);
pstmt->setString(2, "A%");
sql::ResultSet *res = pstmt->executeQuery();
4. 事务处理
4.1 ACID特性
数据库事务必须满足ACID特性:
- 原子性 (Atomicity):事务中的所有操作要么全部完成,要么全部不完成。通过undo log实现,记录SQL执行相关信息,回滚时执行相反操作。
- 一致性 (Consistency):事务将数据库从一种一致状态转变为另一种一致状态。
- 隔离性 (Isolation):多个并发事务相互隔离,通过锁机制和MVCC(多版本并发控制)实现。
- 持久性 (Durability):事务提交后结果永久保存,通过redo log实现。
4.2 C++中的事务管理
try {
// 禁用自动提交
con->setAutoCommit(false);
// 执行多个数据库操作
sql::PreparedStatement *pstmt = con->prepareStatement(
"UPDATE accounts SET balance = balance - ? WHERE id = ?");
pstmt->setDouble(1, 100.0);
pstmt->setInt(2, 1);
pstmt->executeUpdate();
pstmt = con->prepareStatement(
"UPDATE accounts SET balance = balance + ? WHERE id = ?");
pstmt->setDouble(1, 100.0);
pstmt->setInt(2, 2);
pstmt->executeUpdate();
// 提交事务
con->commit();
std::cout << "事务执行成功" << std::endl;
} catch (sql::SQLException &e) {
// 回滚事务
con->rollback();
std::cerr << "事务执行失败: " << e.what() << std::endl;
}
>>>C/C++入门及练手项目<<<
5. 数据库优化
5.1 索引优化
索引是提高查询性能的关键手段:
- 索引类型:B-tree索引、哈希索引、全文索引等
- 索引设计原则:
- 为经常查询的列创建索引
- 为连接操作的表连接列创建索引
- 考虑索引选择性,高选择性列更适合索引
- 索引失效情况:
- 使用LIKE时以通配符开头(如
%value) - 对索引列进行运算操作
- 使用OR关键字且OR前后条件不全有索引
- 类型转换会导致索引失效
- 使用LIKE时以通配符开头(如
5.2 查询优化
1.避免使用SELECT *
尽量指定需要的列,而不是使用SELECT *:
-- 不推荐
SELECT * FROM users WHERE age > 25;
-- 推荐
SELECT id, name, age FROM users WHERE age > 25;
2.合理使用LIMIT分页
对于大数据集,使用高效分页:
-- 低效分页(偏移量大时)
SELECT * FROM users ORDER BY id LIMIT 10000, 20;
-- 高效分页(使用索引和边界值)
SELECT * FROM users WHERE id > 10000 ORDER BY id LIMIT 20;
5.3 数据库结构优化
-
规范化与反规范化
- 规范化:减少数据冗余,提高数据一致性
- 反规范化:适当增加冗余,减少连接操作,提高查询性能
-
分区表
对大数据表进行分区,提高查询性能和管理效率。 -
垂直拆分
将字段多的表拆分成多个表,将常用字段和不常用字段分开。
6. 高级主题
6.1 连接池技术
在多线程环境下,使用连接池可以有效地复用连接,提高性能并减少连接创建的开销。
// 简化的连接池示例
class ConnectionPool {
private:
std::queue<sql::Connection*> connections;
std::mutex mtx;
std::string url, username, password;
public:
ConnectionPool(const std::string& url, const std::string& username,
const std::string& password, int poolSize) {
sql::mysql::MySQL_Driver *driver = sql::mysql::get_mysql_driver_instance();
for (int i = 0; i < poolSize; ++i) {
sql::Connection *con = driver->connect(url, username, password);
connections.push(con);
}
}
sql::Connection* getConnection() {
std::lock_guard<std::mutex> lock(mtx);
if (connections.empty()) {
return nullptr;
}
sql::Connection* con = connections.front();
connections.pop();
return con;
}
void releaseConnection(sql::Connection* con) {
std::lock_guard<std::mutex> lock(mtx);
connections.push(con);
}
};
6.2 ORM框架基本概念
虽然C++没有官方标准的ORM框架,但你可以理解ORM的基本概念:
- 对象-关系映射:将数据库表映射为类,行映射为对象,列映射为属性
- 优势:提高开发效率,减少重复SQL编写
- 劣势:可能带来性能损失,复杂查询不如原生SQL灵活
7. 完整示例:简易员工管理系统
#include <iostream>
#include <cppconn/driver.h>
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>
class EmployeeDB {
private:
sql::mysql::MySQL_Driver *driver;
sql::Connection *con;
public:
EmployeeDB() {
try {
driver = sql::mysql::get_mysql_driver_instance();
con = driver->connect("tcp://127.0.0.1:3306", "root", "password");
con->setSchema("company");
} catch (sql::SQLException &e) {
std::cerr << "数据库连接错误: " << e.what() << std::endl;
}
}
~EmployeeDB() {
delete con;
}
void addEmployee(const std::string &name, const std::string &position, double salary) {
try {
sql::PreparedStatement *pstmt = con->prepareStatement(
"INSERT INTO employees (name, position, salary) VALUES (?, ?, ?)");
pstmt->setString(1, name);
pstmt->setString(2, position);
pstmt->setDouble(3, salary);
pstmt->executeUpdate();
delete pstmt;
std::cout << "员工添加成功: " << name << std::endl;
} catch (sql::SQLException &e) {
std::cerr << "添加员工错误: " << e.what() << std::endl;
}
}
void listEmployees() {
try {
sql::Statement *stmt = con->createStatement();
sql::ResultSet *res = stmt->executeQuery(
"SELECT id, name, position, salary FROM employees ORDER BY id");
std::cout << "ID\t姓名\t职位\t薪资" << std::endl;
std::cout << "--------------------------------" << std::endl;
while (res->next()) {
std::cout << res->getInt("id") << "\t";
std::cout << res->getString("name") << "\t";
std::cout << res->getString("position") << "\t";
std::cout << res->getDouble("salary") << std::endl;
}
delete res;
delete stmt;
} catch (sql::SQLException &e) {
std::cerr << "查询员工错误: " << e.what() << std::endl;
}
}
void updateSalary(int id, double newSalary) {
try {
con->setAutoCommit(false); // 开始事务
sql::PreparedStatement *pstmt = con->prepareStatement(
"UPDATE employees SET salary = ? WHERE id = ?");
pstmt->setDouble(1, newSalary);
pstmt->setInt(2, id);
int affectedRows = pstmt->executeUpdate();
if (affectedRows > 0) {
con->commit(); // 提交事务
std::cout << "员工薪资更新成功" << std::endl;
} else {
con->rollback(); // 回滚事务
std::cout << "未找到指定员工" << std::endl;
}
delete pstmt;
} catch (sql::SQLException &e) {
con->rollback(); // 回滚事务
std::cerr << "更新薪资错误: " << e.what() << std::endl;
}
}
};
int main() {
EmployeeDB db;
// 添加员工
db.addEmployee("张三", "工程师", 15000.0);
db.addEmployee("李四", "经理", 25000.0);
// 列出所有员工
db.listEmployees();
// 更新薪资
db.updateSalary(1, 16000.0);
return 0;
}
8. 总结
本文涵盖了C++数据库编程的核心知识点,包括:
- 数据库基础:了解了不同类型数据库及其特点
- 连接管理:掌握了如何使用C++连接MySQL和SQLite数据库
- CRUD操作:学会了执行增删改查等基本数据库操作
- 事务处理:理解了ACID特性及如何在C++中实现事务管理
- 性能优化:掌握了索引优化、查询优化和结构优化的关键技术
- 高级主题:了解了连接池、ORM等高级概念
掌握这些知识将帮助你在C++面试中更好地应对数据库相关的问题,同时也能在实际开发中构建高效、可靠的数据库应用程序。
注意:本文中的代码示例需要安装相应的数据库驱动库,如MySQL Connector/C++,并在编译时链接这些库。
完整C++后端八股文:>> C++八股文(完整版)<<
更多推荐
所有评论(0)