本篇为当日学习整理笔记,系统梳理IO 多路复用中 select 与 epoll 的核心差异 ,以及SQLite 轻量数据库的基础语法、常用命令与 C 语言接口。内容从基础概念到实操语法,再到编程接口,适合嵌入式 / 网络编程入门学习者复习参考。
文章目录
- [一、IO 多路复用:select 与 epoll 核心区别](#一、IO 多路复用:select 与 epoll 核心区别)
- [1.1 核心区别总览表](#1.1 核心区别总览表)
- [1.2 select 核心特点](#1.2 select 核心特点)
- [1.3 epoll 核心特点](#1.3 epoll 核心特点)
- [二、SQLite 数据库基础](#二、SQLite 数据库基础)
- [2.1 数据库的概念与分类](#2.1 数据库的概念与分类)
- [2.2 SQLite 安装](#2.2 SQLite 安装)
- [2.3 SQLite 常用元命令](#2.3 SQLite 常用元命令)
- [三、SQL 基础语法全梳理](#三、SQL 基础语法全梳理)
- [3.1 SQLite 支持的数据类型](#3.1 SQLite 支持的数据类型)
- [3.2 表的创建与删除](#3.2 表的创建与删除)
- [3.3 数据操作:增删改查](#3.3 数据操作:增删改查)
- [3.4 条件查询与排序](#3.4 条件查询与排序)
- [3.5 多表联合查询](#3.5 多表联合查询)
- [四、SQLite C 语言接口详解](#四、SQLite C 语言接口详解)
- [4.1 sqlite3_open:打开数据库](#4.1 sqlite3_open:打开数据库)
- [4.2 sqlite3_exec:执行 SQL 语句](#4.2 sqlite3_exec:执行 SQL 语句)
- [4.3 sqlite3_close:关闭数据库](#4.3 sqlite3_close:关闭数据库)
- 五、学习总结
一、IO 多路复用:select 与 epoll 核心区别
IO 多路复用是网络编程中处理高并发连接的核心技术,select 和 epoll 是 Linux 下最经典的两种实现,二者在设计思路、性能、触发模式上有本质区别。
1.1 核心区别总览表
| 对比维度 | select | epoll |
|---|---|---|
| 监听集合本质 | 数组结构,有数量上限(默认 1024) | 内核事件表,无数量上限 |
| 数据拷贝开销 | 每次调用都需在用户态与内核态间全量拷贝集合,开销大 | 事件表常驻内核,仅返回就绪事件,无需全量拷贝,开销小 |
| 触发模式 | 仅支持水平触发(低速模式) | 支持水平触发(默认)和边沿触发(高速模式) |
| 事件定位方式 | 需手动遍历整个集合查找就绪文件描述符 | 自动返回就绪 fd 列表,无需遍历 |
1.2 select 核心特点
- 文件描述符集合有上限 select 本质上通过
fd_set数组管理待监听的文件描述符,受系统常量FD_SETSIZE限制,Linux 默认最大监听 1024 个文件描述符,不适合超大并发场景。 - 用户态与内核态拷贝开销大 select 的监听集合维护在应用层(用户态),每次调用都需要把整个集合从用户态拷贝到内核态,内核检测完事件后再拷贝回用户态。连接数越多,拷贝开销呈线性增长。
- 仅支持水平触发模式 水平触发也叫低速模式:只要文件描述符上还有未处理的数据,每次调用 select 都会重复通知该事件。优点是编程简单、不易丢数据,缺点是触发效率低。
- 需要手动遍历查找就绪 fd select 返回后只会告知 "有事件就绪",不会指明具体是哪个 fd,应用程序需要遍历整个监听集合逐个检查,连接数越多效率越低。
1.3 epoll 核心特点
- 监听事件无数量上限 epoll 在内核中通过红黑树管理待监听的文件描述符,没有数量上限,理论上只受限于系统内存,适合高并发大连接场景。
- 内核级管理,拷贝开销极低 epoll 的事件表直接常驻内核空间,应用程序仅在添加 / 删除 fd 时做一次拷贝,监听过程中无需重复拷贝整个集合。内核检测到就绪事件后,通过就绪链表直接返回事件,极大降低了资源开销。
- 双触发模式可选
- 水平触发(默认):和 select 逻辑一致,有未处理数据就持续通知,编程简单。
- 边沿触发:仅在数据到来的瞬间通知一次,是高速模式。处理效率更高,但要求程序一次性读完缓冲区数据,编程复杂度更高,处理不当容易丢数据。
- 自动返回就绪 fd epoll 返回时直接将就绪的文件描述符列表返回给应用层,程序不需要遍历整个集合,直接处理就绪事件即可,连接数越多效率优势越明显。
二、SQLite 数据库基础
2.1 数据库的概念与分类
数据库是能够对大批量数据进行高效增、删、改、查管理的软件,按照数据存储方式主要分为两大类:
(1)关系型数据库
- 数据以表格形式存储在磁盘文件中,支持标准 SQL 语言操作
- 代表产品:Oracle、MySQL、SQLite
- 特点:数据结构严谨、支持事务、适合持久化存储
(2)非关系型数据库
- 多为内存数据库,数据存储在内存中,读写速度极快
- 代表产品:Redis、各类 NoSQL 数据库
- 特点:读写速度快、结构灵活、适合高并发缓存场景
2.2 SQLite 安装
SQLite 是一款轻量级嵌入式数据库,无需独立服务进程,以单个文件形式存在,Linux 下安装命令:
sudo apt-get install sqlite3
2.3 SQLite 常用元命令
注意:这类.开头的命令是 sqlite3 终端工具的内置命令,不是标准 SQL 语句,不需要加分号。
| 命令 | 功能 |
|---|---|
.tables |
查看当前数据库中所有表名 |
.databases |
列出当前附加的数据库名称与文件路径 |
.headers on/off |
开启 / 关闭查询结果的列名显示 |
.mode 模式 |
设置数据显示格式(如 column、list 等) |
.schema 表名 |
查看指定表的建表语句 |
.width 数值 |
设置每列的显示宽度 |
三、SQL 基础语法全梳理
SQL 是操作关系型数据库的标准语言,SQLite 支持标准 SQL 的核心子集。
3.1 SQLite 支持的数据类型
| 类型 | 说明 |
|---|---|
| INTEGER | 整数类型 |
| REAL | 浮点数类型 |
| TEXT | 文本字符串类型 |
| BLOB | 二进制数据类型 |
3.2 表的创建与删除
(1)创建表
语法:
create table 表名 (
列名称1 数据类型1,
列名称2 数据类型2,
...
);
示例:
create table student (
id integer,
name text,
sex text,
age integer,
score integer
);
(2)删除表
语法:
drop table 表名称;
示例:
drop table student;
3.3 数据操作:增删改查
(1)插入数据 insert
语法:
-- 全列插入
insert into 表名 values (值1, 值2, ...);
-- 指定列插入
insert into 表名 (列1, 列2) values (值1, 值2);
示例:
-- 全列插入
insert into student values (1, "张三", "男", 18, 90);
-- 允许部分字段为NULL
insert into student values (3, "王二", NULL, NULL, NULL);
-- 指定列插入
insert into student (id, name, sex) values (4, "赵五", "男");
(2)查询数据 select
语法:
select 列名称1, 列名称2 from 表名;
-- 查询所有列
select * from 表名;
示例:
select * from student;
select name,score from student;
(3)更新数据 update
语法:
update 表名称 set 列名称 = 新值 where 匹配条件;
示例:
update student set score=91 where name="赵五";
(4)删除数据 delete
语法:
delete from 表名称 where 列名称 = 值;
示例:
delete from student where name = "王二";
3.4 条件查询与排序
(1)where 条件子句
用于筛选符合条件的数据,常用运算符:
| 运算符 | 含义 |
|---|---|
| = | 等于 |
| <> | 不等于 |
| > | 大于 |
| < | 小于 |
| >= | 大于等于 |
| <= | 小于等于 |
| BETWEEN ... AND ... | 在某个范围内 |
| LIKE | 模糊匹配,%代表任意字符 |
示例:
-- 模糊查询姓赵的学生
select name,score from student where name like "赵%";
-- 查询成绩大于等于90分的学生
select name from student where score >= 90;
-- 多条件组合
select name,score from student where name like "赵%" and score > 95;
(2)排序 order by
语法:
asc:升序(默认)desc:降序
示例:
-- 男生按成绩降序排列
select name,score from student where sex="男" order by score desc;
3.5 多表联合查询
(1)cross join 交叉连接
返回两张表的笛卡尔积,即所有行的组合。 示例:
select 学生信息.姓名 as 姓名, 课程信息.名称 as 课程, 课程信息.学分 as 学分
from 学生信息 cross join 课程信息;
(2)inner join 内连接
只返回两张表中匹配条件的行,是最常用的连接方式。 示例:
-- 两表连接
select 学生信息.姓名 as 姓名, 成绩信息.成绩 as 成绩
from 学生信息 inner join 成绩信息
on 学生信息.ID = 成绩信息.学生ID;
-- 三表连接
select 学生信息.姓名 as 姓名, 课程信息.名称 as 课程, 成绩信息.成绩 as 成绩
from 学生信息 inner join 成绩信息 on 学生信息.ID = 成绩信息.学生ID
inner join 课程信息 on 成绩信息.课程ID = 课程信息.ID;
(3)left outer join 左外连接
以左表为准,右表没有匹配数据时返回 NULL。 示例:
select 学生信息.姓名 as 姓名,成绩信息.成绩 as 成绩
from 学生信息 left outer join 成绩信息
on 学生信息.ID = 成绩信息.学生ID;
四、SQLite C 语言接口详解
SQLite 提供了一套 C 语言函数接口,可在代码中直接操作数据库,核心有 3 个函数。
4.1 sqlite3_open:打开数据库
函数原型:
int sqlite3_open(
const char *filename, /* 数据库文件名(UTF-8编码) */
sqlite3 **ppDb /* 输出参数:数据库句柄 */
);
- 功能:打开一个数据库文件,如果文件不存在则自动创建。
- 参数 :
filename:数据库文件路径ppDb:二级指针,用来返回数据库句柄,后续所有操作都通过该句柄完成
- 返回值 :成功返回
SQLITE_OK,失败返回对应错误码。
4.2 sqlite3_exec:执行 SQL 语句
函数原型:
int sqlite3_exec(
sqlite3*, /* 已打开的数据库句柄 */
const char *sql, /* 要执行的SQL语句 */
int (*callback)(void*,int,char**,char**), /* 回调函数 */
void*, /* 传给回调函数的参数 */
char **errmsg /* 输出参数:错误信息 */
);
- 功能:执行一条标准 SQL 语句,是最核心的执行接口。
- 参数说明 :
- 已打开的数据库句柄
sql:要执行的 SQL 字符串callback:回调函数,仅执行 select 查询时使用 。每查到一行数据就会调用一次该函数,用于处理查询结果;增、删、改操作不需要回调,传NULL即可。- 传给回调函数的第一个参数,可用来在主函数和回调之间传递数据
errmsg:二级指针,执行失败时会指向错误描述字符串,使用完需要调用sqlite3_free()释放
- 返回值 :成功返回
SQLITE_OK,失败返回错误码。
回调函数说明: 原型:
int callback(void *arg, int colnum, char **pcontent, char **ptitle)
arg:主函数传入的自定义参数colnum:查询结果的列数pcontent:字符串数组,存储每一列的内容ptitle:字符串数组,存储每一列的列名 返回值必须为 0,否则会中断查询。
4.3 sqlite3_close:关闭数据库
函数原型:
int sqlite3_close(sqlite3*);
- 功能:关闭数据库连接,释放相关资源。
- 注意:程序结束前必须调用该函数,否则可能造成资源泄漏和数据损坏。
五、学习总结
- select 与 epoll 的核心差异在于事件管理的位置、拷贝开销、触发模式和事件定位方式,epoll 在高并发场景下性能远优于 select。
- SQLite 是轻量嵌入式关系型数据库,支持标准 SQL 语法,通过单个文件存储数据,适合嵌入式和小型项目。
- SQL 语法核心围绕增删改查,条件筛选、排序、多表连接是查询的核心技能。
- SQLite 的 C 接口简洁易用,
sqlite3_open/sqlite3_exec/sqlite3_close三个函数即可完成绝大多数数据库操作,查询操作通过回调函数获取结果。