数据库与多路IO基础
一、数据库基础操作(增删改查)
1. 创建表
CREATE TABLE student (
id INT PRIMARY KEY, -- 学号,主键
name VARCHAR(50), -- 姓名
age INT, -- 年龄
score FLOAT -- 成绩
);
说明:
-
CREATE TABLE:创建数据表的命令 -
INT:整数类型 -
VARCHAR(50):可变长字符串,最大50字符 -
FLOAT:浮点数类型 -
PRIMARY KEY:主键约束,值唯一且非空
2. 增加数据(增)
-- 插入一条完整数据
INSERT INTO student VALUES (1, '张三', 18, 90.5);
-- 插入指定字段数据
INSERT INTO student (id, name, score) VALUES (2, '李四', 85.5);
关键字 :INSERT INTO - 向表中添加新数据
3. 查询数据(查)
-- 查询所有字段
SELECT * FROM student;
-- 查询指定字段
SELECT name, score FROM student;
-- 带条件查询
SELECT * FROM student WHERE age > 18;
-- 排序查询
SELECT * FROM student ORDER BY score DESC; -- DESC:降序,ASC:升序
关键字 :SELECT - 从表中查询数据
4. 修改数据(改)
-- 修改满足条件的数据
UPDATE student SET score = 95 WHERE id = 1;
-- 修改多个字段
UPDATE student SET age = 19, score = 88 WHERE name = '张三';
关键字 :UPDATE - 修改表中的数据
5. 删除数据(删)
-- 删除满足条件的数据
DELETE FROM student WHERE id = 2;
-- 删除所有数据
DELETE FROM student;
关键字 :DELETE - 从表中删除数据
二、数据库效率问题
1. 数据量增大带来的问题
单表数据量过大 → 查询变慢 → 索引效率下降 → 整体性能降低
2. 存储策略:分表存
垂直分表:
原表:id, name, age, score, address, phone, email
↓
主表:id, name, age, score
扩展表:id, address, phone, email
特点:按列拆分,将不常用字段分离
水平分表:
原表:1-100万条数据
↓
表1:1-25万条
表2:25-50万条
表3:50-75万条
表4:75-100万条
特点:按行拆分,将数据分散到多个结构相同的表
分表目的:
-
减少单表数据量
-
提高查询速度
-
降低锁竞争
-
便于维护
三、多路IO技术
1. 什么是多路IO
定义:一个进程可以同时监听多个文件描述符,当某个文件描述符就绪(可读/可写)时,通知应用程序处理。
类比:
单路IO:一个服务员只服务一桌客人
多路IO:一个服务员同时服务多桌客人,哪桌有需要就服务哪桌
2. 三种多路IO模型对比
| 特性 | select | poll | epoll |
|---|---|---|---|
| 文件描述符限制 | 最多1024个 | 无限制 | 无限制 |
| 效率 | 低 | 中等 | 高 |
| 实现机制 | 轮询 | 轮询 | 回调 |
| 内存拷贝 | 每次都要从内核拷贝 | 每次都要从内核拷贝 | 共享内存 |
四、select详解
1. select特点
-
最大限制:最多监听1024个文件描述符
-
效率低:每次调用都需要遍历所有文件描述符
-
工作原理:轮询方式,线性扫描
2. select工作流程
1. 应用程序设置要监听的文件描述符集合
2. 调用select,进程阻塞
3. 内核遍历文件描述符集合
4. 有就绪事件返回
5. 应用程序再次遍历找到就绪的描述符
6. 处理数据
3. select代码示例
fd_set readfds;
struct timeval tv;
FD_ZERO(&readfds); // 清空集合
FD_SET(sockfd, &readfds); // 添加socket到集合
tv.tv_sec = 5; // 超时时间5秒
tv.tv_usec = 0;
int ret = select(maxfd + 1, &readfds, NULL, NULL, &tv);
if (ret > 0) {
if (FD_ISSET(sockfd, &readfds)) { // 检查socket是否就绪
// 处理数据
}
}
select缺点:
-
监听上限1024个文件描述符
-
每次调用都需要从用户空间拷贝集合到内核空间
-
返回后需要遍历所有描述符才能找到就绪的
五、poll详解
1. poll特点
-
无限制:监听文件描述符数量没有上限
-
效率中等:仍是轮询方式,但不需要拷贝整个集合
-
数据结构:使用pollfd结构体数组
2. pollfd结构体
struct pollfd {
int fd; // 文件描述符
short events; // 监听的事件(读/写/异常)
short revents; // 返回的就绪事件
};
3. poll工作流程
1. 应用程序填充pollfd数组
2. 调用poll,进程阻塞
3. 内核遍历pollfd数组
4. 有就绪事件返回,设置revents
5. 应用程序遍历数组检查revents
6. 处理数据
4. poll代码示例
struct pollfd fds[10];
fds[0].fd = sockfd; // 设置文件描述符
fds[0].events = POLLIN; // 监听读事件
int ret = poll(fds, 1, 5000); // 超时5秒
if (ret > 0) {
if (fds[0].revents & POLLIN) { // 检查读事件是否就绪
// 处理数据
}
}
poll优点:
-
无1024上限
-
不需要重新初始化事件集合
poll缺点:
-
仍需要遍历所有描述符
-
用户空间到内核空间的数组拷贝
六、epoll详解
1. epoll特点
-
效率高:回调机制,只返回就绪的描述符
-
无限制:监听数量没有上限
-
内存共享:使用mmap共享内存,减少拷贝
2. epoll工作流程
1. epoll_create() - 创建epoll实例
2. epoll_ctl() - 添加/修改/删除监听的文件描述符
3. epoll_wait() - 等待事件发生,返回就绪的描述符
4. 直接处理就绪的描述符(无需遍历所有)
3. epoll代码示例
// 1. 创建epoll实例
int epfd = epoll_create(1);
// 2. 设置监听事件
struct epoll_event ev;
ev.events = EPOLLIN; // 监听读事件
ev.data.fd = sockfd; // 关联的文件描述符
epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev); // 添加监听
// 3. 等待事件发生
struct epoll_event events[10];
int nfds = epoll_wait(epfd, events, 10, 5000);
// 4. 处理就绪事件
for (int i = 0; i < nfds; i++) {
// events[i].data.fd 就是就绪的文件描述符
// 直接处理,无需遍历
}
4. epoll两种工作模式
LT模式(水平触发):
特点:只要缓冲区有数据,就会一直通知
默认模式,编程简单
ET模式(边缘触发):
特点:只有状态发生变化时通知一次
效率更高,需要循环读取数据
七、三种多路IO对比总结
1. 效率对比
select < poll < epoll
(最低) (最高)
2. 适用场景
| 技术 | 适用场景 | 原因 |
|---|---|---|
| select | 监听少量连接(<1024) | 实现简单,跨平台好 |
| poll | 监听较多连接 | 无数量限制,但仍有性能问题 |
| epoll | 高并发、海量连接 | 效率最高,只返回就绪事件 |
3. 核心区别
select/poll:
"有多少个连接?我全部查一遍"
↓
遍历1000个,找到2个就绪
↓
处理这2个
epoll:
"哪个连接好了?告诉我"
↓
直接返回2个就绪的连接
↓
处理这2个
八、综合示例:数据库+多路IO
1. 典型应用场景
客户端连接 → epoll监听 → 接收请求 → 查询数据库 → 返回结果
2. 流程示例
// 1. 创建epoll监听客户端连接
epoll_wait(epfd, events, 10, -1);
// 2. 收到查询请求
recv(client_fd, query, sizeof(query), 0);
// 例如:SELECT * FROM student WHERE id = 1
// 3. 查询数据库
// 执行SQL语句,获取结果
// 4. 返回结果给客户端
send(client_fd, result, len, 0);
总结:
-
数据库基础:增删改查是核心操作,通过SQL语句实现
-
分表存储:数据量大时的优化策略,提高查询效率
-
多路IO:单进程监听多个文件描述符的技术
-
select/poll/epoll:三种实现方式,效率和机制各不相同
-
epoll优势:在高并发场景下性能最好,是现代网络编程的首选