Linux嵌入式系统SQlite3数据库学习笔记

前言

SQlite3是一个轻量级、嵌入式的关系型数据库管理系统,其中具有的核心特点:

1:嵌入式数据库:无需独立服务器进程,数据库直接嵌入到应用程序中。

2:单文件存储:整个数据库存储为单个文件(.db或.sqlite后缀),便于移植和备份。

3:跨平台:支持 Windows、Linux、macOS、iOS、Android 等系统。

相比于MySQl/PostgreSQL等数据库具有轻量级操作简单的优点。

一、数据库安装

可以直接使用命令安装

复制代码
# 更新软件源
sudo apt update

# 安装 SQLite3 命令行工具和库
sudo apt install sqlite3

# 可选:安装开发头文件(用于编程开发)
sudo apt install libsqlite3-dev

也可以直接去下载对应的安装包本地编译

复制代码
wget https://www.sqlite.org/2024/sqlite-autoconf-3440200.tar.gz
tar xvfz sqlite-autoconf-*.tar.gz
cd sqlite-autoconf-*/

# 编译安装
./configure
make
sudo make install

安装的话是比较简单的,毕竟是轻量级的数据库。

二、数据库使用方法

1:创建数据库

复制代码
sqlite3 xxxx.db //创建数据库

创建完数据库后进入数据库后开头就是sqlite>这样的

然后对数据库的操作的相关指令

复制代码
.open filename -- 打开文件
.show -- 显示SQKite命令提示符的默认设置
.q -- 退出
.databases -- 显示数据库
.help -- 帮助
.dump --导入导出数据库
.tables -- 查看表

2.数据类型

Sqlite3的主要数据类型

除了这些主要的数据类型之外还有别的数据类型

3.创建表

前面已经创建了数据库和知道了有那些数据类型,那么接下来就该进行数据库的核心操作"增删改查"。

创建表的话有两种方式一种是使用标准的SQL语句另外一种是使用对应的API接口函数,我看了很多文章很多博主是分开进行介绍的其本质还是一样的。

标准SQL语句

复制代码
CREATE TABLE Filename (列名 数据类型 ...)

API接口函数

对于SQlite3数据库的基本使用的话就三个核心API接口函数

1:打开数据库

函数原型:

cpp 复制代码
SQLITE_API int sqlite3_open(
  const char *filename,   /* Database filename (UTF-8) */
  sqlite3 **ppDb          /* OUT: SQLite db handle */
);

参数说明:

const char *filename:指向要打开的数据库文件名的字符串,可以带路径。

sqlite3 **ppDb:是一个指向SQlite的结构体的二级指针

函数返回值:

  1. 成功开发返回SQLITE_OK(0)。
  2. 失败返回错误码

补充说明:

cpp 复制代码
SQLITE_API int sqlite3_open16(
  const void *filename,   /* Database filename (UTF-16) */
  sqlite3 **ppDb          /* OUT: SQLite db handle */
);
SQLITE_API int sqlite3_open_v2(
  const char *filename,   /* Database filename (UTF-8) */
  sqlite3 **ppDb,         /* OUT: SQLite db handle */
  int flags,              /* Flags */
  const char *zVfs        /* Name of VFS module to use */
);

2:SQL语句执行函数

函数原型:

cpp 复制代码
SQLITE_API int sqlite3_exec(
  sqlite3*,                                  /* An open database */
  const char *sql,                           /* SQL to be evaluated */
  int (*callback)(void*,int,char**,char**),  /* Callback function */
  void *,                                    /* 1st argument to callback */
  char **errmsg                              /* Error msg written here */
);

函数参数:

sqlite3*:一个打开的数据库连接指针。

const char *sql:要执行的 SQL 语句字符串。可以是单个 SQL 语句或多个 SQL 语句的序列,以分号分隔。

int (*callback)(void*,int,char**,char**):回调函数,每当执行一个查询语句并返回结果时,这个回调函数会被调用。如果不需要处理查询结果,可以设置为 NULL。

void *:用户提供的指针,可以在回调函数中使用,通常用于传递上下文信息。

char **errmsg:如果发生错误,这个指针将被设置为指向一个包含错误信息的字符串。

函数返回值:

  1. 成功返回SQLITE_OK(0)。
  2. 失败返回错误码

3:关闭数据库

cpp 复制代码
SQLITE_API int sqlite3_close(sqlite3*);

函数参数:

数据库链接指针。

这三个函数就是SQLite3的基本API接口函数。可以直接使用SQL语句来进行相关的表操作,其本质与直接使用SQl语句操作数据库是一样的。除此之外还有一些高级用法。

创建表格的基础用法程序示例:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>

//打开数据库函数示例
int main(int argc,char * argv[])
{
    sqlite3 *db;
    char *mang = 0;
    int rc;
    char *err_msg = 0;
    //1:连接数据库
    rc = sqlite3_open("./test.db",&db);//打开当前目录下的数据库
    if(rc)
    {
        //非0表示打开失败
        fprintf(stderr,"Can't open database: %s\n",sqlite3_errmsg(db));
    }else
    {
        //打开成功
        fprintf(stderr,"Opened database successfully\n");
    }
    //2:创建表
    /*
        构建SQLstatement
        创建一个名为files的表
        一共三列分别为:FILENAME DATE SIZE
        数据类型分别为:文本字符串 文本字符串 无符号整形
    */
    // 创建表的 SQL 语句
    const char *sql = 
        "CREATE TABLE IF NOT EXISTS files ("
        "FILENAME TEXT PRIMARY KEY,"
        "DATE TEXT NOT NULL,"
        "SIZE INTEGER NOT NULL);";

    //执行SQL命令创建表
    rc = sqlite3_exec(db,sql,0,0,&err_msg);
    if( rc != SQLITE_OK ){
    fprintf(stderr, "SQL error: %s\n", err_msg);
        sqlite3_free(err_msg);
    }else{
        fprintf(stdout, "Table created successfully\n");
    }

    sqlite3_close(db);//关闭数据库
    return 0;
}

查看上面的SQL语句做一下解释说明:

观察上面的写法可以看到其中处理规范中的内容多了一些:PRIMARY KEY、NOT NULL。

这些在sqlite3数据库中叫做约束,表中的每一列都有一些限制属性,这些限制属性就叫做约束。

这些约束在表中根据具体情况来使用。

4:插入数据

上面介绍的示例使用的SQL语句使用法,在表中插入数据当然也可以使用这个方法,

语法一:insert into 表名 (字段名称) values (值名称)。

例如:insert into user (id,age) values (1,10)。

语法二:insert into 表名 values(值 1,值 2,...)。

例如:insert into user values(3,"wang",11)。

经过我的学习中发现了另外的一种的用法为占位符使用

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>

//使用预处理语句添加元素
int main(int argc,char * argv[])
{
    sqlite3 *db;
    char *mang = 0;
    int rc;
    char *err_msg = 0;
    //1:连接数据库
    rc = sqlite3_open("./test.db",&db);//打开当前目录下的数据库
    if(rc)
    {
        //非0表示打开失败
        fprintf(stderr,"Can't open database: %s\n",sqlite3_errmsg(db));
    }else
    {
        //打开成功
        fprintf(stderr,"Opened database successfully\n");
    }

    // 插入数据的SQL语句(使用占为符?)
    const char *sql = 
        "INSERT INTO files (FILENAME,DATE,SIZE)VALUES(?,?,?);";

    sqlite3_stmt *stmt;

    rc = sqlite3_prepare_v2(db,sql,-1,&stmt,NULL);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "准备语句失败: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return 1;
    }

    //绑定参数(索引从1开始)
    sqlite3_bind_text(stmt,1,"flie4.mp4",-1,SQLITE_STATIC);// FILENAME
    sqlite3_bind_text(stmt,2,"2025-04-21",-1,SQLITE_STATIC);//DATE
    sqlite3_bind_int(stmt,3,4096);//SIZE

    //执行插入
    rc = sqlite3_step(stmt);
    if(rc != SQLITE_DONE)
    {
        fprintf(stderr, "插入失败: %s\n", sqlite3_errmsg(db));
        sqlite3_finalize(stmt);
        sqlite3_close(db);
        return 1;
    }
    // 释放预处理语句资源
    sqlite3_finalize(stmt);
    printf("数据插入成功!\n");
    
    sqlite3_close(db);//关闭数据库
    return 0;
}
cpp 复制代码
SQLITE_API int sqlite3_prepare_v2(
  sqlite3 *db,            /* Database handle */
  const char *zSql,       /* SQL statement, UTF-8 encoded */
  int nByte,              /* Maximum length of zSql in bytes. */
  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
  const char **pzTail     /* OUT: Pointer to unused portion of zSql */
);

参数绑定函数后很多种,区别就在于对应的数据类型

|-----------------------|--------|
| 函数 | 数据类型 |
| sqlite3_bind_int() | 整数 |
| sqlite3_bind_double() | 浮点数 |
| sqlite3_bind_text() | 字符串 |
| sqlite3_bind_blob() | 二进制数据 |
| sqlite3_bind_null() | NULL 值 |

cpp 复制代码
SQLITE_API int sqlite3_step(sqlite3_stmt*);//插入函数

插入一条语句的话这样使用是没有问题的,那既然插入一条语句是这样的那么插入多条语句应该怎么实现。那就引出来一个全新的概念那就是事务。

对于事务这个概念就要引出ACID特性

|-------------------------|--------------------------------|
| 特性 | 描述 |
| ​原子性​​ (Atomicity) | 事务内的操作要么全部成功,要么全部回滚(不可分割的最小单元) |
| 一致性​​ (Consistency) | 事务执行后数据库必须保持有效状态(满足约束、触发器等规则) |
| 隔离性​​ (Isolation) | 并发事务之间互不干扰(通过锁机制实现) |
| 持久性​​ (Durability) | 事务提交后,修改永久保存(即使系统崩溃) |

在SQLite3中的事务用法

命令 作用
BEGIN TRANSACTION 开启事务(默认进入 DEFERRED 模式,首次读操作时获取锁)
COMMIT 提交事务(持久化更改)
ROLLBACK 回滚事务(撤销所有未提交的操作)
SAVEPOINT name 创建保存点(支持嵌套事务)
RELEASE SAVEPOINT name 释放保存点(提交到该点的操作)
ROLLBACK TO name 回滚到指定保存点

使用示例:

cpp 复制代码
//插入多条语句
int main() {
    sqlite3 *db;
    char *err_msg = NULL;
    int rc;

    // 打开数据库
    rc = sqlite3_open("test.db", &db);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(db));
        return 1;
    }

    // 开启事务(大幅提升批量插入速度)
    rc = sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "事务开启失败: %s\n", err_msg);
        sqlite3_free(err_msg);
        sqlite3_close(db);
        return 1;
    }

    // 准备插入语句
    const char *sql = "INSERT INTO files (FILENAME, DATE, SIZE) VALUES (?, ?, ?);";
    sqlite3_stmt *stmt;
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "预处理失败: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return 1;
    }

    // 示例数据(假设从数组或文件读取)
    const char *filenames[] = {"file1.txt", "file2.pdf", "file3.jpg","flie4.mp4"};
    const char *dates[] = {"2023-10-05", "2023-10-06", "2023-10-07","2023-10-07"};
    const int sizes[] = {1024, 2048, 3072,4096};

    // 插入多条数据
    for (int i = 0; i < 3; i++) {
        // 绑定参数
        sqlite3_bind_text(stmt, 1, filenames[i], -1, SQLITE_STATIC); // FILENAME
        sqlite3_bind_text(stmt, 2, dates[i], -1, SQLITE_STATIC);     // DATE
        sqlite3_bind_int(stmt, 3, sizes[i]);                         // SIZE

        // 执行插入
        rc = sqlite3_step(stmt);
        if (rc != SQLITE_DONE) {
            fprintf(stderr, "插入失败: %s\n", sqlite3_errmsg(db));
            sqlite3_exec(db, "ROLLBACK;", NULL, NULL, NULL); // 回滚事务
            sqlite3_finalize(stmt);
            sqlite3_close(db);
            return 1;
        }

        // 重置语句(不清除绑定)
        sqlite3_reset(stmt);
    }

    // 提交事务
    rc = sqlite3_exec(db, "COMMIT;", NULL, NULL, &err_msg);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "提交失败: %s\n", err_msg);
        sqlite3_free(err_msg);
    }

    // 释放资源
    sqlite3_finalize(stmt);
    sqlite3_close(db);
    printf("成功插入 %d 条数据!\n", 3);
    return 0;
}

5:删除表数据

删除表中数据:

语法:delete from 表名 [满足条件]。

例如:

delete from user(删除表中所有数据)。

delete from user where id = 1(删除id=1的数据)。

删除表中的内容除了可以一次删除一条语句之外还可以一下删除具有相同的名字的所有数据。

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <string.h>
//删除表内容
/**
 * 删除表中数据(按文件名或日期)
 * @param db        数据库句柄
 * @param condition 条件类型:"filename" 或 "date"
 * @param value     条件值(如文件名或日期字符串)
 * @return          1: 成功, 0: 失败
 */
int delete_records(sqlite3 *db, const char *condition, const char *value) {
    sqlite3_stmt *stmt = NULL;
    char *err_msg = NULL;
    int rc;

    // 根据条件类型生成 SQL 语句
    const char *sql;
    if (strcmp(condition, "filename") == 0) {
        sql = "DELETE FROM files WHERE FILENAME = ?;";
    } else if (strcmp(condition, "date") == 0) {
        sql = "DELETE FROM files WHERE DATE = ?;";
    } else {
        fprintf(stderr, "无效的条件类型: 仅支持 'filename' 或 'date'\n");
        return 0;
    }

    // 准备预处理语句
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "预处理失败: %s\n", sqlite3_errmsg(db));
        return 0;
    }

    // 绑定参数(索引从 1 开始)
    sqlite3_bind_text(stmt, 1, value, -1, SQLITE_STATIC);

    // 执行删除操作
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE) {
        fprintf(stderr, "删除失败: %s\n", sqlite3_errmsg(db));
        sqlite3_finalize(stmt);
        return 0;
    }

    // 获取删除的行数
    int rows_affected = sqlite3_changes(db);
    printf("成功删除 %d 条数据\n", rows_affected);

    // 释放资源
    sqlite3_finalize(stmt);
    return 1;
}
int main(int argc,char * argv[])
{
    sqlite3 *db;
    int rc;

    //1:连接数据库
    rc = sqlite3_open("./test.db",&db);//打开当前目录下的数据库
    if(rc)
    {
        //非0表示打开失败
        fprintf(stderr,"Can't open database: %s\n",sqlite3_errmsg(db));
    }else
    {
        //打开成功
        fprintf(stderr,"Opened database successfully\n");
    }
    
    // // 示例 1:按文件名删除(删除 file1.txt)
    // if (!delete_records(db, "filename", "file1.txt")) {
    //     sqlite3_close(db);
    //     return 1;
    // }

    //示例 2:按日期删除(删除所有日期为 2025-04-21 的记录)
    if (!delete_records(db, "date", "2025-04-21")) {
        sqlite3_close(db);
        return 1;
    }

    // 关闭数据库
    sqlite3_close(db);
    return 0;
}

6:修改表数据

修改表中数据:

语法:update 表名 set 表字段 = 值 [满足条件]。

例如:

update user set id = 1 where name = 'li'。

update user set id = 1 where name = "li" and passwd = "123"。

update user set id = 2 where name = "li" or name = "zhao"。

使用示例:

cpp 复制代码
#include <stdio.h>
#include <sqlite3.h>

/**
 * 安全更新数据项(原子化操作)
 * @param db_name    数据库文件名
 * @param table_name 表名
 * @param old_value  旧数据值(需存在)
 * @param new_value  新数据值(需唯一)
 * @return           1: 成功, 0: 失败
 */
int update_data_item(const char* db_name,const char* table_name,const char* old_value,const char* new_value) 
{
    sqlite3 *db = NULL;
    sqlite3_stmt *stmt = NULL;
    char *sql = NULL;
    int rc, ret = 0;

    // 打开数据库
    if (sqlite3_open(db_name, &db) != SQLITE_OK) {
        fprintf(stderr, "数据库连接失败: %s\n", sqlite3_errmsg(db));
        return 0;
    }

    // 开启事务
    if (sqlite3_exec(db, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK) {
        fprintf(stderr, "事务开启失败: %s\n", sqlite3_errmsg(db));
        goto cleanup;
    }

    // ------------------- 检查新值是否唯一 -------------------
    sql = sqlite3_mprintf("SELECT COUNT(*) FROM %w WHERE FILENAME=?;", table_name);
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    sqlite3_free(sql);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "唯一性检查预处理失败: %s\n", sqlite3_errmsg(db));
        goto rollback;
    }

    sqlite3_bind_text(stmt, 1, new_value, -1, SQLITE_STATIC);
    if (sqlite3_step(stmt) != SQLITE_ROW) {
        fprintf(stderr, "唯一性检查执行失败\n");
        goto rollback;
    }

    if (sqlite3_column_int(stmt, 0) > 0) {
        fprintf(stderr, "错误: 新值 '%s' 已存在\n", new_value);
        goto rollback;
    }
    sqlite3_finalize(stmt);
    stmt = NULL;

    // ------------------- 执行更新操作 -------------------
    sql = sqlite3_mprintf("UPDATE %w SET FILENAME=? WHERE FILENAME=?;", table_name);
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    sqlite3_free(sql);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "更新预处理失败: %s\n", sqlite3_errmsg(db));
        goto rollback;
    }

    sqlite3_bind_text(stmt, 1, new_value, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, old_value, -1, SQLITE_STATIC);
    
    if (sqlite3_step(stmt) != SQLITE_DONE) {
        fprintf(stderr, "更新执行失败: %s\n", sqlite3_errmsg(db));
        goto rollback;
    }

    // 验证实际更新行数
    if (sqlite3_changes(db) == 0) {
        fprintf(stderr, "错误: 旧值 '%s' 不存在\n", old_value);
        goto rollback;
    }

    // 提交事务
    if (sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK) {
        fprintf(stderr, "提交失败: %s\n", sqlite3_errmsg(db));
        goto rollback;
    }

    ret = 1; // 成功标志
    printf("[%s] %s → %s\n", table_name, old_value, new_value);

rollback:
    if (!ret) sqlite3_exec(db, "ROLLBACK;", NULL, NULL, NULL);
cleanup:
    if (stmt) sqlite3_finalize(stmt);
    sqlite3_close(db);
    return ret;
}

// 示例用法
int main() {
    // 将 test.db 数据库 files 表中 "file3.jpg" 改为 "video.mp4"
    int success = update_data_item("test.db", "files", "video.mp4", "会议室1上午关键记录.mp4");
    printf("操作结果: %s\n", success ? "成功" : "失败");
    return 0;
}

7:查找数据

cpp 复制代码
//查表
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
//SQL语句用法
//使用查表操作主要是sqlite3_exec()的回调函数
typedef int (*sqlite3_callback)(
    void*,    /* Data provided in the 4th argument of sqlite3_exec() */
    int,      /* The number of columns in row */
    char**,   /* An array of strings representing fields in the row */
    char**    /* An array of strings representing column names */
    );
// 第一个参数:即第四个参数传入的数据
// 第二个参数:行中的列数
// 第三个参数:表示行中字段的字符串数组,即各行中的数据
// 第四个参数:表示列名的字符串数组,创建链表时设置的
// 执行流程:查表,是否还有符合条件数据。有,执行sqlite3_callback()函数;没有,退出
// 回调函数
int callback(void *data, int argc, char **argv, char **azColName)
{
    int i;
    //fprintf(stderr, "%s ", (const char*)data);
    for(i=0; i<argc; i++){
       printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
}
int main(int argc,char* argv[])
{
    sqlite3 *db;
    char *mang = 0;
    int rc;
    char *err_msg = 0;
    const char* data = "Table";
    //1:连接数据库
    rc = sqlite3_open("./test.db",&db);//打开当前目录下的数据库
    if(rc)
    {
        //非0表示打开失败
        fprintf(stderr,"Can't open database: %s\n",sqlite3_errmsg(db));
    }else
    {
        //打开成功
        fprintf(stderr,"Opened database successfully\n");
    }
    // 创建表查询表的SQL语句
    const char *sql = 
        "SELECT * FROM files";

    //执行SQL命令创建表
    rc = sqlite3_exec(db,sql,callback,(void *)data,&err_msg);
    if( rc != SQLITE_OK ){
        fprintf(stderr, "SQL error: %s\n", err_msg);
        sqlite3_free(err_msg);
    }else{
        fprintf(stdout, "Operation done successfully\n");
    }

    sqlite3_close(db);//关闭数据库
    return 0;
}
//占位符用法
int main() {
    sqlite3 *db;
    sqlite3_stmt *stmt = NULL;
    char *err_msg = NULL;
    int rc;

    // 打开数据库
    rc = sqlite3_open("test.db", &db);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(db));
        return 1;
    }

    // 准备查询语句(示例:查询所有数据)
    const char *sql = "SELECT FILENAME, DATE, SIZE FROM files;";
    rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "预处理失败: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return 1;
    }

    // 遍历结果集
    printf("文件列表:\n");
    printf("----------------------------------\n");
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        // 提取各列数据(列索引从 0 开始)
        const char *filename = sqlite3_column_text(stmt, 0);  // 第0列:FILENAME
        const char *date = sqlite3_column_text(stmt, 1);      // 第1列:DATE
        int size = sqlite3_column_int(stmt, 2);               // 第2列:SIZE

        printf("文件名: %-12s  日期: %-10s  大小: %d字节\n", filename, date, size);
    }
    printf("----------------------------------\n");

    // 检查是否因错误退出循环
    if (sqlite3_errcode(db) != SQLITE_DONE) {
        fprintf(stderr, "查询未完整执行: %s\n", sqlite3_errmsg(db));
    }

    // 释放资源
    sqlite3_finalize(stmt);
    sqlite3_close(db);
    return 0;
}

对于数据库的使用我觉得其核心功能是对数据的处理也就是经典四字真言"增删改查";当掌握其中的使用方法后就基本会使用这个数据库的方法了。

相关推荐
yh云想22 分钟前
《深入解析缓存三大难题:穿透、雪崩、击穿及应对之道》
数据库·redis
ptc学习者25 分钟前
oracle 11G安装大概率遇到问题
数据库
SelectDB1 小时前
天翼云与飞轮科技达成战略合作,共筑云数融合新生态
大数据·数据库·数据分析
Virgil1391 小时前
【YOLO学习笔记】YOLOv8详解解读
笔记·学习·yolo
学习网安的doro1 小时前
3a服务器的基本功能1之身份认证
服务器·网络·学习·安全·身份认证·ac
望获linux2 小时前
【实时Linux实战系列】实时数据流处理框架分析
linux·运维·前端·数据库·chrome·操作系统·wpf
Forever Nore3 小时前
Nginx 学习
运维·学习·nginx
野犬寒鸦4 小时前
Pipeline功能实现Redis批处理(项目批量查询点赞情况的应用)
java·服务器·数据库·redis·后端·缓存
꧁༺摩༒西༻꧂4 小时前
Spring Boot Actuator 监控功能的简介及禁用
java·数据库·spring boot
程序员JerrySUN4 小时前
当前主流GPU全景讲解:架构、功能与应用方向
数据库·人工智能·驱动开发·redis·缓存·架构