C语言基于AVL树实现简单的文件数据库

news2024/11/18 3:46:55

目录

  • 前言
  • 一、设计思路
  • 二、文件存储格式
  • 三、数据库操作
    • 3.1. 数据库结构
    • 3.2. 数据库初始化
    • 3.3. 插入
    • 3.4. 删除
    • 3.5. 修改
    • 3.6. 查询
    • 3.7. 清空
  • 四、示例代码

前言

之前介绍了C语言实现AVL树, 本文是对AVL树的一个简单应用,在资源偏紧张的硬件设备中可以使用,如资源足够还是建议使用sqlite。
先把仓库地址贴一下:https://gitee.com/sauryniu/file-database

一、设计思路

简单的实现文件数据库需要考虑两个方面,一个是数据的持久存储,另一个是数据的快速操作(增删改查)。关于持久存储,我们可以把数据存储到文件中;快速操作的话则需要借助算法实现,这里选择在内存中建立AVL树,相对二叉树来说性能比较均衡,又不像其他树那样复杂。

二、文件存储格式

在这里插入图片描述

  • 文件头:文件数据库头部,用户自定义大小和数据结构,可用于保存版本信息之类的数据。
  • 数据块个数:类型为int,最大4字节,取决于操作系统位数。
  • 数据块:用户真正保存的数据内容,自定义大小和数据结构,但是需要统一。

三、数据库操作

3.1. 数据库结构

封装结构对象,方便操作。

struct _file_db
{
    file_db_t* _this;
    void* _private_;

/*
@func: 
    添加元素到文件数据库中

@para: 
    db : 文件数据库指针
    ele : 要被添加的元素指针

@return:
    int : < 0 : 失败, 0 : 成功

@note:
    ele 传入的建议是非指针变量的地址,如果使用的是指向动态内存的指针,则用完后需自行释放资源
*/
    int (*add)(file_db_t* db, void *ele);

/*
@func: 
    通过键值删除指定键值对应的元素

@para: 
    db : 文件数据库指针
    key : 元素对应的键值

@return:
    int : < 0 : 失败, 0 : 成功

@note:
    none.
*/
    int (*del)(file_db_t* db, int key);

/*
@func: 
    根据键值编辑指定的元素

@para: 
    db : 文件数据库指针
    key : 元素对应的键值
    ele : 目标元素,将替换给定键值所对应的元素的值

@return:
    int : < 0 : 失败, 0 : 成功

@note:
    ele 参数通过用户传入的获取键值的函数计算得到的键值需要和本函数传入键值 key 一致
*/   
    int (*edit)(file_db_t* db, int key, void *ele);

/*
@func: 
    根据键值查询文件数据库中的元素

@para: 
    db : 文件数据库指针
    key : 元素的键值

@return:
    void* : NULL 查询失败, other 查询到的元素的指针

@note:
    none.
*/
    void* (*query)(file_db_t* db, int key);

/*
@func: 
    写入文件数据库的文件头

@para: 
    db : 文件数据库指针
    head : 写入的文件头指针

@return:
    int : < 0 : 失败, 0 : 成功

@note:
    none.
*/
    int (*write_head)(file_db_t* db, void* head);

/*
@func: 
    读取文件数据库的文件头到指定内存中

@para: 
    db : 文件数据库指针
    head : 读出的文件头指针

@return:
    int : < 0 : 失败, 0 : 成功

@note:
    none.
*/
    int (*read_head)(file_db_t* db, void* head);

/*
@func: 
    返回文件数据库中的元素个数

@para: 
    db : 文件数据库指针

@return:
    int : < 0 : 失败, other : 元素个数

@note:
    none.
*/
    int (*size)(file_db_t* db);

/*
@func: 
    遍历文件数据库中的所有内容,然后使用传入的函数指针对每个元素执行操作

@para: 
    db : 文件数据库指针
    visit : 对元素操作的函数指针

@return:
    int : < 0 : 失败, 0 : 成功

@note:
    none.
*/
    int (*traverse)(file_db_t* db, void (*visit)(void*));

/*
@func: 
    清除文件数据库内容,但是保存文件头

@para: 
    db : 文件数据库指针

@return:
    int : -1 : 失败, 0 : 成功

@note:
    【重要】此函数不能与 file_db_destory 同时使用
*/
    int (*clear)(file_db_t* db);

/*
@func: 
    并释放文件数据库相关动态内存

@para: 
    db : 文件数据库指针

@return:
    int : -1 : 失败, 0 : 成功

@note:
    【重要】此函数不能与 file_db_destory 同时使用
*/
    int (*free)(file_db_t* db);

/*
@func: 
    销毁数据库文件并释放相关动态内存

@para: 
    db : 文件数据库指针

@return:
    int : -1 : 失败, 0 : 成功

@note:
    【重要】此函数不能与 file_db_free 同时使用
*/
    int (*destory)(file_db_t* db);
};

3.2. 数据库初始化

初始化数据库,传入数据库路径,以及其他相关信息,如果文件不存在会创建

/*
@func: 
    使用指定的文件初始化文件数据库

@para: 
    path : 存放数据的文件所在路径 
    head_size : 头大小
    data_size : 数据块大小
    pf_hash_func :从数据块中获取key的哈希函数
    head : 头数据

@return:
    file_db_t* : 文件数据库指针

@note:
    如果不再使用该数据库需要调用销毁函数释放内存
    【重要】保存的数据的数据类型大小必须是固定的
*/
file_db_t* file_db_init(const char* path, int head_size, int data_size, int (*pf_hash_func)(void *), void* head);

3.3. 插入

插入数据块时,会先查询数据库中key是否存在,不存在才插入。插入时,先把数据块写到文件尾部,然后更新数据块数量,最后把数据更新到AVL树中。

/*
@func: 
    添加元素到文件数据库中

@para: 
    db : 文件数据库指针
    ele : 要被添加的元素指针

@return:
    int : < 0 : 失败, 0 : 成功
    -4  :内部错误
    -5  :key已存在
    -6  :内存不足
    -7  :写入文件异常
@note:
    ele 传入变量如果使用的是指向动态内存的指针,则用完后需自行释放资源
*/
static int file_db_add(file_db_t* db, void* ele)
{
    file_db_private_t* _this = get_private_member(db);
    if(NULL == _this) 
        return -4;

    if(NULL != _this->m_tree->query_by_key(_this->m_tree->_this, _this->pf_get_ele_key(ele)))
        return -5;

    int res_code = 0;
    file_db_record_t* record_data = (file_db_record_t*)malloc(sizeof(file_db_record_t));
    if(NULL == record_data)
        return -6;

    void* ele_memory = malloc(_this->m_data_size);
    if(NULL == ele_memory)
    {
        free(record_data);
        return -6;
    }

    memcpy(ele_memory, ele, _this->m_data_size);

    pthread_mutex_lock(&_this->m_file_db_mutex);
    FILE *db_fp = fopen(_this->m_path, "rb+");
    if(NULL == db_fp) 
    {
        FILE_DB_LOG_DEBUG("open db file error");
        res_code = -7;
        goto RUNTIME_ERROR;
    }
    fseek(db_fp, 0, SEEK_END);
    record_data->offset = ftell(db_fp);
    record_data->db = db;
    int write_len = fwrite(ele_memory, 1, _this->m_data_size, db_fp);
    if(write_len != _this->m_data_size) 
    {
        FILE_DB_LOG_DEBUG("write ele error, write[%d] but [%d]", _this->m_data_size, write_len);
        fclose(db_fp);
        res_code = -7;
        goto RUNTIME_ERROR;
    }
    _this->m_data_cnt++;
    FILE_DB_LOG_DEBUG("write cnt %d, key %d", _this->m_data_cnt, _this->pf_get_ele_key(ele));
    fseek(db_fp, _this->m_head_size, SEEK_SET);
    write_len = fwrite(&_this->m_data_cnt, 1, sizeof(int), db_fp);
    if(write_len != sizeof(int))
    {
        FILE_DB_LOG_DEBUG("write cnt error");
        _this->m_data_cnt--;
        fclose(db_fp);
        truncate(_this->m_path, record_data->offset);
        res_code = -9;
        goto RUNTIME_ERROR;
    }
    fflush(db_fp);
    fclose(db_fp);
    record_data->ele = ele_memory;
    pthread_mutex_unlock(&_this->m_file_db_mutex);
    return _this->m_tree->add(_this->m_tree->_this, (void *)record_data);

RUNTIME_ERROR:
    free(ele_memory);
    free(record_data);
    pthread_mutex_unlock(&_this->m_file_db_mutex);
    return res_code;
}

3.4. 删除

删除数据库块时,会先找到该数据块在文件中的位置,把文件尾部的第一个数据块复制到待删除的数据块所在位置,然后把数据块数量减1,最后在AVL树中把该数据块删除。

/*
@func: 
    通过键值删除指定键值对应的元素

@para: 
    db : 文件数据库指针
    key : 元素对应的键值

@return:
    int : < 0 : 失败, 0 : 成功

@note:
    none.
*/
static int file_db_del(file_db_t* db, int key)
{
    file_db_private_t* _this = get_private_member(db);

    if(NULL == _this) 
    {
        FILE_DB_LOG_DEBUG("Filedatabase error!");
        return -2;
    }
    file_db_record_t* record_data = _this->m_tree->query_by_key(_this->m_tree->_this, key);

    if(NULL == record_data)
    {
        FILE_DB_LOG_DEBUG("No such element. Del fail!");
        return -3;
    }

    pthread_mutex_lock(&_this->m_file_db_mutex);
    FILE* db_fp = fopen(_this->m_path, "rb+");
    if(NULL == db_fp)
    {
        pthread_mutex_unlock(&_this->m_file_db_mutex);
        return -4;
    }

    if(record_data->offset < _this->m_head_size + sizeof(int) + _this->m_data_size * (_this->m_data_cnt - 1))
    {
        fseek(db_fp, 0, SEEK_END);
        
        fseek(db_fp, -_this->m_data_size, SEEK_END);
       
        void* buff = malloc(_this->m_data_size);
        
        if(1 != fread(buff, _this->m_data_size, 1, db_fp))
        {
            FILE_DB_LOG_DEBUG("Read tail element error! file position %ld", ftell(db_fp));
            free(buff);
            fclose(db_fp);
            pthread_mutex_unlock(&_this->m_file_db_mutex);
            return -5;
        }

        fseek(db_fp, record_data->offset, SEEK_SET);

        if(1 != fwrite(buff, _this->m_data_size, 1, db_fp))
        {
            FILE_DB_LOG_DEBUG("Write tail element error!");

            free(buff);
            fclose(db_fp);
            pthread_mutex_unlock(&_this->m_file_db_mutex);
            return -6;
        }

        free(buff);
    }
    _this->m_data_cnt--;
    fseek(db_fp, _this->m_head_size, SEEK_SET);
    int write_len = fwrite(&_this->m_data_cnt, sizeof(int), 1, db_fp);
    if(write_len != 1)
    {
        FILE_DB_LOG_DEBUG("write cnt error");
        _this->m_data_cnt++;
        fclose(db_fp);
        pthread_mutex_unlock(&_this->m_file_db_mutex);
        return -7;
    }

    fclose(db_fp);
    truncate(_this->m_path, _this->m_head_size + sizeof(int) + _this->m_data_cnt * _this->m_data_size );
    pthread_mutex_unlock(&_this->m_file_db_mutex);

    return _this->m_tree->del_node_by_key(_this->m_tree->_this, key);
}

3.5. 修改

修改数据块,需要传入数据块对应key值和数据块内容,定位到文件中数据块位置后更新,最后把AVL树中的数据更新一下就可以。

/*
@func: 
    根据键值编辑指定的元素

@para: 
    db : 文件数据库指针
    key : 元素对应的键值
    ele : 目标元素,将替换给定键值所对应的元素的值

@return:
    int : < 0 : 失败, 0 : 成功

@note:
    ele 参数通过用户传入的获取键值的函数计算得到的键值需要和本函数传入键值 key 一致
*/
static int file_db_edit(file_db_t* db, int key, void *ele)
{
    file_db_private_t* _this = get_private_member(db);

    if(NULL == _this || NULL == ele) return -1;

    if(key != _this->pf_get_ele_key(ele))
    {
        FILE_DB_LOG_DEBUG("Edit error, Key value and element do not match");
        return -1;
    }
    pthread_mutex_lock(&_this->m_file_db_mutex);
    file_db_record_t* record_data = (file_db_record_t*)(_this->m_tree->query_by_key(_this->m_tree->_this, key));
    if(NULL == record_data)
    {
        FILE_DB_LOG_DEBUG("Edit error, Query data error");
        pthread_mutex_unlock(&_this->m_file_db_mutex);
        return -2;
    }
    
    memset(record_data->ele, 0, _this->m_data_size);
    memcpy(record_data->ele, ele, _this->m_data_size);
    FILE_DB_LOG_DEBUG("query key[%d], ele key[%d], get key[%d]", key, _this->pf_get_ele_key(ele), _this->pf_get_ele_key(record_data->ele));
    FILE* db_fp = fopen(_this->m_path, "rb+");

    if(NULL == db_fp) 
    {
        FILE_DB_LOG_DEBUG("Edit error, File error");
        pthread_mutex_unlock(&_this->m_file_db_mutex);
        return -3;
    }
    fseek(db_fp, record_data->offset, SEEK_SET);

    if(fwrite(record_data->ele, _this->m_data_size, 1, db_fp) != 1)
    {
        fclose(db_fp);
        pthread_mutex_unlock(&_this->m_file_db_mutex);
        FILE_DB_LOG_DEBUG("Edit element, write new error!");
        return -4;
    }
    fclose(db_fp);
    pthread_mutex_unlock(&_this->m_file_db_mutex);
    return 0;
}

3.6. 查询

查询比较简单,直接传入key,在AVL树中查询即可。

/*
@func: 
    根据键值查询文件数据库中的元素

@para: 
    db : 文件数据库指针
    key : 元素的键值

@return:
    void* : NULL 查询失败, other 查询到的元素的指针

@note:
    none.
*/
static void* file_db_query(file_db_t* db, int key)
{
    file_db_private_t* _this = get_private_member(db);
    if(NULL == _this) 
    {
        FILE_DB_LOG_DEBUG("_this is NULL");
        return NULL;
    }
    file_db_record_t* record_data = (file_db_record_t*)(_this->m_tree->query_by_key(_this->m_tree->_this, key));
    if(NULL == record_data) 
    {
        FILE_DB_LOG_DEBUG("record_data is NULL");
        return NULL;
    }
    return record_data->ele;
}

3.7. 清空

清空时,直接把文件截断,然后把数据块数量写入0,最后清空所有AVL树节点即可。

/*
@func: 
    清除文件数据库内容,但是保存文件头

@para: 
    db : 文件数据库指针

@return:
    int : -1 : 失败, 0 : 成功

@note:
    【重要】此函数不能与 file_db_destory 同时使用
*/
static int file_db_clear(file_db_t* db)
{
    file_db_private_t* _this = get_private_member(db);

    if(NULL == _this) return -1;
    int cnt = 0;

    pthread_mutex_lock(&_this->m_file_db_mutex);
    FILE* db_fp = fopen(_this->m_path, "rb+");
    if(NULL == db_fp)
    {
        pthread_mutex_unlock(&_this->m_file_db_mutex);
        return -1;
    }
    fseek(db_fp, _this->m_head_size, SEEK_SET);
    if(1 != fwrite(&cnt, sizeof(int), 1, db_fp))
    {
        fclose(db_fp);
        pthread_mutex_unlock(&_this->m_file_db_mutex);
        FILE_DB_LOG_DEBUG("[file_db_clear] : write cnt error");
        return -2;
    } 
    fclose(db_fp);
    truncate(_this->m_path, _this->m_head_size + sizeof(int));
    pthread_mutex_unlock(&_this->m_file_db_mutex);
    
    _this->m_data_cnt = 0;

    _this->m_tree->clear_node(_this->m_tree->_this);

    return 0;
}

四、示例代码

示例代码使用名称为 example.db 的文件作为数据库文件,采用随机数的方式最大写入20个元素,通常会小于20个,因为会有一部分重复的,然后进行删、改、查的操作。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "FileDatabase.h"

#define EXAMPLE_FILE_DB "example.db"
#define FILE_DB_SIZE 20
static file_db_t* file_db;

typedef struct _db_head
{
    int version;
    char reserve[252];
}db_head_t;

typedef struct _db_data
{
    int key;
    char value[60];
}db_data_t;

static int get_key(void *ele)
{
    if(NULL == ele)
    {
        return -1;
    }
    db_data_t* record = (db_data_t*)ele;
    return record->key;
}

static void visit(void* ele)
{
    if(NULL == ele) 
    {
        printf("ele null!!!");
    }
    db_data_t* data = (db_data_t*)ele;
    printf("[visit] key[%d]->value[%s]\r\n", data->key, data->value);
}

int main(void)
{
    db_head_t head;
    head.version = 1;
    file_db = file_db_init(EXAMPLE_FILE_DB, sizeof(db_head_t), sizeof(db_data_t), get_key, &head);
    
    if(NULL == file_db)
    {
        printf("create db error\r\n");
        return -1;
    }
    db_data_t element;
   
    int cnt = 0;
    srand(time(NULL));

    int key[FILE_DB_SIZE];
    int index = 0;
    for(int i = 0; i < FILE_DB_SIZE; ++i)
    {
        int num = rand()%FILE_DB_SIZE;
        key[index++] = num;
        //printf("index :%d -> key %d\r\n", index, key[index]);
        element.key = num;
        memset(element.value, 0, 60);
        sprintf(element.value, "element%d", num);
        
        if(0 == file_db->add(file_db->_this, &element))
        {
            cnt++;
        }
    }

    printf("Add cnt %d, total %d\r\n", cnt, file_db->size(file_db->_this));

    int random_key = rand()%FILE_DB_SIZE;
    printf("Del key[%d], res[%d]\r\n", key[random_key], file_db->del(file_db->_this, key[random_key]));
    
    random_key = rand()%FILE_DB_SIZE;
    memset(element.value, 0, 60);
    sprintf(element.value, "edit%d", key[random_key]);
    element.key = key[random_key];
    printf("Edit key[%d], res[%d]\r\n", key[random_key], file_db->edit(file_db->_this, key[random_key], &element));
    
    random_key = rand()%FILE_DB_SIZE;
    db_data_t* ele_query = (db_data_t*) file_db->query(file_db->_this, key[random_key]);
    if(NULL == ele_query)
    {
        printf("ele_query null\r\n");
    }
    else
    {
        printf("Query key[%d], value[%s]\r\n", ele_query->key, ele_query->value);
    }

    file_db->traverse(file_db->_this, visit);

    file_db->clear(file_db->_this);
    file_db->free(file_db->_this);
    //file_db->destory(file_db->_this);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1110081.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++当中的IO流介绍 - operator 类型()的特殊用法

C语言当中的IO流 C语言中我们用到的最频繁的输入输出方式就是 scanf () 与 printf()。 scanf(): 从标准输入设备(键盘)读取数据&#xff0c;并将值存放在变量中。printf(): 将指定的文字/字符串输出到标准输出设备(屏幕)。 注意&#xff1a;宽度输出和精度输出控制。C语言借助…

数字图像处理实验记录二(直方图和直方图均衡化)

文章目录 一、基础知识1&#xff0c;什么是直方图2&#xff0c;直方图有什么用3&#xff0c;直方图均衡化4、原理代码实现 二、实验要求任务1&#xff1a;任务2&#xff1a; 三、实验记录任务1&#xff1a;任务2&#xff1a; 四、结果展示任务1&#xff1a;任务2&#xff1a; 五…

利用Cpolar永久免费内网穿透软件实现IStoreOS安装与远程访问

文章目录 前言1. ssh局域网登陆iStoreOS系统2. 安装Cpolar内 网穿透软件3. 测试公网远程链接4. 公网使用固定http地址远程访问iStoreOS webui界面 前言 iStoreOS系统是基于OpenWrt定制的软路由系统&#xff0c;提供了如轻nas&#xff0c;云盘&#xff0c;文件共享等众多网络服务…

【EI会议征稿】2024年智能电网与人工智能国际学术会议(SGAI 2024)

2024年智能电网与人工智能国际学术会议&#xff08;SGAI 2024) 2024 International Conference on Smart Grid and Artificial Intelligence 随着时代的发展&#xff0c;基础的电网技术已经比较成熟&#xff0c;但与日俱增的用电需求以及不断转变的用电模式促使我们需要不断地…

Go 存储系列:LSM存储引擎 LevelDB

概念介绍 LSM-Tree 被是一种面向写多读少应用场景的数据结构 &#xff0c;被 Hbase、RocksDB 等强力 NoSQL 数据库采用作为底层文件组织方式。 简单的LSM-Tree 包含 2 层树状数据结构&#xff1a; Memtable 并完全驻留在内存中&#xff08;假设 T0&#xff09; SStables 存储…

可视化(Visual) SQL初探

一、背景 在当今数字化时代&#xff0c;数据信息作为企业和组织的宝贵资源之一&#xff0c;如何挖掘其中的价值并帮助企业和组织个体决策&#xff0c;已然成为炙手可热的话题。数据分析作为其具体载体&#xff0c;是从数据中提取信息、洞察机遇、制定战略、做出决策的关键过程…

在云计算环境中,如何利用 AI 改进云计算系统和数据库系统性能

文章目录 前言一、关于唐明洁教授二、AI for System2.1 面向分布式作业的人工智能2.1.1 现阶段企业云计算系统环境所遇到的普遍痛点2.1.2 云计算系统环境所遇到的普遍痛点的解决方案&#xff08;一&#xff09;Google Autopilot Eurosys 2021方案&#xff08;Pod级别&#xff0…

熟练使用 Redis 的五大数据结构:Java 实战教程

入门 入门阶段主要记住 Redis 的命令&#xff0c;熟练使用 Redis 的 5 大数据结构就可以了。 如果没有 Redis 环境&#xff0c;可以直接通过这个网址https://try.redis.io/&#xff0c;很赞&#xff0c;它会给你模拟一个在线的环境可供你尽情使用&#xff01; 熟练使用Redis的…

C++11lambda表达式--你了解C++的lambda表达式吗?他的底层是怎样的呢?

文章目录 1.lambda表达式的出现1.1C98对内置类型排序1.2C98对自定义类型排序1.3C11中lambda表达式对数据进行排序 2.lambda表达式的语法2.1lambda表达式语法的介绍2.2lambda表达式语法的讲解1.交换函数的lambda表达式写法2.捕捉列表捕捉3.捕捉列表混合捕捉4.不能相互赋值 可以拷…

excel怎么固定前几行前几列不滚动?

在Excel中&#xff0c;如果你想固定前几行或前几列不滚动&#xff0c;可以通过以下几种方法来实现。详细的介绍如下&#xff1a; **固定前几行不滚动&#xff1a;** 1. 选择需要固定的行数。例如&#xff0c;如果你想要固定前3行&#xff0c;应该选中第4行的单元格。 2. 在E…

2023年全网最新 Windows10 搭建 Node.js 环境教程

目录 一、Node.js安装包下载1.1 官网下载node.js安装包1.2 百度网盘获取node.js安装包 二、Windows上安装Node.js三、配置npm在安装全局模块时的路径和缓存cache的路径二、Pycharm 调用 NodeJS 运行 js 代码的环境配置 本文将向大家介绍在 Windows10 上安装 Node.js 的方法。本…

短视频剪辑矩阵系统开发源码----源头搭建

短视频剪辑矩阵系统开发源码----源头搭建 目录 一、源码技术构建源码部署搭建交付之---- 二、短视频矩阵系统功能模型搭建 三、短视频矩阵系统开发的路径呢规则 一、源码技术构建源码部署搭建交付之---- 1.需要协助系统完成部署、接口全部正常接入、系统正常运行多久&#…

canvas操作像素的处理,图片操作例子,黑白蒙版等

经常拍照的同学会使用图片处理软件&#xff0c;给自己的照片加上各种效果。图片处理软件也是软件&#xff0c;同样也是由代码写的&#xff0c;那么如何实现图片处理呢&#xff0c;这章我们就探讨一下这个问题。 canvas中像素处理涉及到3个方法&#xff0c;我们先来看一下API吧…

Jmeter项目实战

一&#xff0c;性能测试流程 性能需求分析 性能方案设计 业务建模 脚本优化 执行测试 收集性能数据 结果分析 性能测试报告 二&#xff0c;性能需求分析 项目管理系统业务&#xff1a;登录 注册 搜索&#xff08;一般最核心的就是登陆&#xff0c;大多只对登录做压测&a…

Spring framework day 01:spring 整合数据源(连接池)

前言 在现代的企业应用开发中&#xff0c;数据库是不可或缺的一部分。而对于大部分的应用程序来说&#xff0c;与数据库的交互涉及到频繁的连接、查询和事务操作。为了提高应用程序的性能和可扩展性&#xff0c;使用连接池来管理数据库连接是一个不错的选择。而 Spring 框架提…

基于多线程+队列实现生产者和消费者

基于多线程队列实现生产者和消费者 需求分析设计思路代码展示 需求分析 需要设计一个系统&#xff0c;能够实时接收视频传来的车牌数据并注入到对应的车辆实体类中。这可能涉及到多线程编程&#xff0c;以处理并监督车牌数据的流入和处理。下面是一种可能的设计思想&#xff1…

如何一起修改多张图片大小?一键修改多张图片尺寸的技巧

如果我们需要在社交平台上分享图片信息&#xff0c;就要把调整图片大小到平台要求的尺寸&#xff0c;单张图片尺寸修改比较简单&#xff0c;要是多张的话&#xff0c;为了提高处理效率&#xff0c;我们就要用到专业工具了&#xff1b;今天分享一个在线图片编辑器&#xff0c;利…

ES6 Class和Class继承

1.class的基本语法 class可以理解为是一个语法糖&#xff0c;将js只能通过构造函数创建实例的方法进行了补充 构造函数&#xff1a; function Person ({ name, age18 }) {this.name namethis.age age } new Person({name: 张三}) Class类&#xff1a; class Person {con…

TCP 传输、重传及工作原理

IP和MAC层的内存受限&#xff0c;用于发送数据包。因此&#xff0c;它们都会限制消息的长度。 这一限制要求TCP在提供给IP层之前&#xff0c;将可变长度的字节打包成多个段。每个段的长度应该是合适的。 下面是一个简单的图示&#xff0c;展示了段是如何通过互联网发送的。 1*I…

淘宝店铺所有商品数据接口及店铺商品数据分析

获取淘宝店铺所有商品数据的接口是淘宝开放平台提供的接口&#xff0c;通过该接口可以获取店铺所有商品数据。 通过淘宝开放平台接口获取店铺所有商品数据的方法如下&#xff1a; 在开放平台注册成为开发者并创建一个应用&#xff0c;获取到所需的 App Key 和 App Secret 等信…