【 OpenGauss源码学习 —— 列存储(autoanalyze)(二)】
列存储(autoanalyze)(二)
- 概述
- PgStat_StatTabEntry 结构体
- pgstat_count_heap_insert 与 pgstat_count_cu_insert 函数
- CStoreInsert::BatchInsertCommon 函数
- pgstat_count_cu_update 函数
- pgstat_count_cu_delete 函数
- pgstat_count_truncate 函数
- pgstat_update_heap_dead_tuples 函数
- 总结
声明:本文的部分内容参考了他人的文章。在编写过程中,我们尊重他人的知识产权和学术成果,力求遵循合理使用原则,并在适用的情况下注明引用来源。
本文主要参考了 OpenGauss1.1.0 的开源代码和《OpenGauss数据库源码解析》一书以及OpenGauss社区学习文档和一些学习资料
概述
列存储(autoanalyze)是一种数据库自动维护统计信息的功能,它定期分析表中的数据分布和变化情况,以优化查询性能。自动分析通过检查表的内容,确定最佳查询计划,从而帮助数据库优化查询操作,提高查询性能,特别是在大型数据库中。这个过程会定期运行,通常由数据库自动调度,以确保统计信息的准确性和最新性,以便数据库查询优化器可以更好地选择执行计划。这有助于提高数据库的性能和响应速度,减少了手动维护统计信息的工作负担。
在上一章中我们学习了relation_needs_vacanalyze 函数,其作用是确定一个表是否需要执行自动化的 VACUUM 或 ANALYZE 操作,并返回相应的决策。在relation_needs_vacanalyze 函数中,以下代码的主要目的是根据一些阈值和条件来确定是否需要对表进行自动的 VACUUM 和 ANALYZE 操作。
if (false == *dovacuum && allowVacuum)*dovacuum = ((float4)vactuples > vacthresh);// 确定表是否需要分析
if (allowAnalyze)*doanalyze = ((float4)anltuples > anlthresh);
以下表格中列举了相关参数及含义:
参 数 | 含 义 |
---|---|
vac_base_thresh | 表的自动 VACUUM 操作的基本阈值,通常是一个整数,表示表中元组数量的下限,低于此下限将触发 VACUUM。 |
anl_base_thresh | 表的自动 ANALYZE 操作的基本阈值,通常是一个整数,表示表中元组数量的下限,低于此下限将触发 ANALYZE。 |
vac_scale_factor | VACUUM 操作的缩放因子,通常是一个浮点数,用于根据表的大小和性能需求调整自动 VACUUM 的触发条件。 |
anl_scale_factor | ANALYZE 操作的缩放因子,通常是一个浮点数,用于根据表的大小和性能需求调整自动 ANALYZE 的触发条件。 |
vacthresh | 实际的自动 VACUUM 操作阈值,根据基本阈值和缩放因子计算得出。 |
anlthresh | 实际的自动 ANALYZE 操作阈值,根据基本阈值和缩放因子计算得出。 |
vactuples | 跟踪表的元组数量,用于自动 VACUUM 操作中。 |
anltuples | 跟踪表的元组数量,用于自动 ANALYZE 操作中。 |
那么问题来了,vactuples和 anltuples 是如何获取到的呢?。
观察如下代码:
if (tabentry && (tabentry->changes_since_analyze || tabentry->n_dead_tuples)) {anltuples = tabentry->changes_since_analyze;vactuples = tabentry->n_dead_tuples;AUTOVAC_LOG(DEBUG2, "fetch local stat info: vac \"%s\" changes_since_analyze = %ld n_dead_tuples = %ld ",NameStr(classForm->relname), tabentry->changes_since_analyze, tabentry->n_dead_tuples);
}
if (avwentry && (avwentry->changes_since_analyze || avwentry->n_dead_tuples)) {anltuples = avwentry->changes_since_analyze;vactuples = avwentry->n_dead_tuples;AUTOVAC_LOG(DEBUG2, "fetch global stat info: vac \"%s\" changes_since_analyze = %ld n_dead_tuples = %ld ",NameStr(classForm->relname), avwentry->changes_since_analyze, avwentry->n_dead_tuples);}
这段代码用于从统计信息中获取表的相关统计数据(changes_since_analyze 和 n_dead_tuples),并将这些数据分别赋值给 anltuples 和 vactuples,以便后续决定是否执行自动 ANALYZE 和自动 VACUUM 操作。那么 tabentry 又是什么呢?
PgStat_StatTabEntry 结构体
PgStat_StatTabEntry 结构体,用于存储数据库中表的统计信息。源码如下:(路径:src/include/pgstat.h
)
typedef struct PgStat_StatTabEntry {PgStat_StatTabKey tablekey; /* 表的统计信息的唯一标识符 */PgStat_Counter numscans; /* 表的扫描次数 */PgStat_Counter tuples_returned; /* 从表中返回的元组数 */PgStat_Counter tuples_fetched; /* 从表中提取的元组数 */PgStat_Counter tuples_inserted; /* 插入到表中的元组数 */PgStat_Counter tuples_updated; /* 更新表中的元组数 */PgStat_Counter tuples_deleted; /* 从表中删除的元组数 */PgStat_Counter tuples_hot_updated; /* 热更新的元组数(在没有引起索引更新的情况下更新的元组数) */PgStat_Counter n_live_tuples; /* 存活的元组数(未被标记为死亡的元组数) */PgStat_Counter n_dead_tuples; /* 死亡的元组数(标记为死亡但尚未被清除的元组数) */PgStat_Counter changes_since_analyze; /* 从上次分析以来发生的变化次数 */PgStat_Counter blocks_fetched; /* 从磁盘读取的块数 */PgStat_Counter blocks_hit; /* 从缓存中读取的块数 */PgStat_Counter cu_mem_hit; /* 连续内存中的命中次数 */PgStat_Counter cu_hdd_sync; /* 硬盘同步的次数 */PgStat_Counter cu_hdd_asyn; /* 硬盘异步的次数 */TimestampTz vacuum_timestamp; /* 用户发起的 VACUUM 的时间戳 */PgStat_Counter vacuum_count; /* 用户发起的 VACUUM 的计数 */TimestampTz autovac_vacuum_timestamp; /* 自动 VACUUM 的时间戳 */PgStat_Counter autovac_vacuum_count; /* 自动 VACUUM 的计数 */TimestampTz analyze_timestamp; /* 用户发起的分析的时间戳 */PgStat_Counter analyze_count; /* 用户发起的分析的计数 */TimestampTz autovac_analyze_timestamp; /* 自动分析的时间戳 */PgStat_Counter autovac_analyze_count; /* 自动分析的计数 */TimestampTz data_changed_timestamp; /* 数据变更的时间戳(例如,插入、删除、更新操作的时间戳) */uint64 autovac_status; /* 自动 VACUUM 的状态信息 */
} PgStat_StatTabEntry;
PgStat_StatTabEntry 结构体用于存储数据库中每个表的统计信息,包括表的扫描次数、元组的增删改查数量、块的读取次数、硬盘访问情况、时间戳等信息,用于监控和分析表的使用情况和性能表现,特别是在自动 VACUUM 和分析过程中提供了关键的统计数据。
pgstat_count_heap_insert 与 pgstat_count_cu_insert 函数
在了解了以上逻辑后,我们知道每次操作表后会更改统计信息,那么是怎么修改的呢?我们首先来从 pgstat_count_heap_insert 函数开始看起吧。
pgstat_count_heap_insert 函数用于在数据库中统计表中插入新元组的操作。它追踪并记录了在当前事务嵌套级别下插入的元组数量,以便进行性能监控和统计分析。
pgstat_count_heap_insert 函数源码如下所示:(路径:src/gausskernel/process/postmaster/pgstat.cpp
)
/** pgstat_count_heap_insert - count a tuple insertion of n tuples* 统计插入 n 条元组的操作次数*/
void pgstat_count_heap_insert(Relation rel, int n)
{PgStat_TableStatus* pgstat_info = rel->pgstat_info;// 检查是否需要进行统计if (pgstat_info != NULL) {/* We have to log the effect at the proper transactional level */// 确定当前嵌套事务级别int nest_level = GetCurrentTransactionNestLevel();// 如果当前事务级别和记录的事务级别不一致,需要添加新的事务级别信息if (pgstat_info->trans == NULL || pgstat_info->trans->nest_level != nest_level)add_tabstat_xact_level(pgstat_info, nest_level);// 更新当前事务级别下插入的元组数量pgstat_info->trans->tuples_inserted += n;}
}
此外,pgstat_count_cu_insert 函数是一个宏定义,用于将 pgstat_count_cu_insert 映射为 pgstat_count_heap_insert 函数的调用,其作用是用于统计某个表中的元组插入操作的次数。
pgstat_count_cu_insert 宏定义如下所示:(路径:src/include/pgstat.h
)
#define pgstat_count_cu_insert(rel, n) \do { \pgstat_count_heap_insert(rel, n); \} while (0)
CStoreInsert::BatchInsertCommon 函数
了解了 pgstat_count_heap_insert 函数和 pgstat_count_cu_insert 函数的作用,那这两个函数是在哪里调用的呢?答案是在 CStoreInsert::BatchInsertCommon 函数中进行调用的,该函数用于执行批量插入操作到列存储表,其功能包括创建列更新单元(CU)、统计插入或更新操作次数以及批量插入索引表中的数据。函数源码如下所示:(路径:src/gausskernel/storage/cstore/cstore_insert.cpp
)
void CStoreInsert::BatchInsertCommon(bulkload_rows* batchRowPtr, int options)
{// 检查参数有效性,若为空或行数为0则直接返回if (unlikely(batchRowPtr == NULL || batchRowPtr->m_rows_curnum == 0))return;int attno = m_relation->rd_rel->relnatts;int col = 0;Assert(attno == batchRowPtr->m_attr_num); // 断言表的列数和批处理行的列数相等CHECK_FOR_INTERRUPTS(); // 检查是否收到中断信号/* 步骤 1: 创建列更新单元(CU)和 CU 描述(CUDesc) */for (col = 0; col < attno; ++col) {// 如果列没有被删除,则创建相应的 CU 和 CUDescif (!m_relation->rd_att->attrs[col]->attisdropped) {m_cuPPtr[col] = FormCU(col, batchRowPtr, m_cuDescPPtr[col]);m_cuCmprsOptions[col].m_sampling_fihished = true;}}// 根据是否为更新操作,统计 CU 插入或更新的次数if (m_isUpdate)pgstat_count_cu_update(m_relation, batchRowPtr->m_rows_curnum);elsepgstat_count_cu_insert(m_relation, batchRowPtr->m_rows_curnum);/** 步骤 2:* a) 分配 CUID(列更新单元ID)和 CUPointer(列更新单元指针)* b) 写入 CU 和 CUDesc*/SaveAll(options);/* 步骤 3: 批量插入索引表 */if (m_relation->rd_att->attrs[0]->attisdropped) {int fstColIdx = CStoreGetfstColIdx(m_relation);InsertIdxTableIfNeed(batchRowPtr, m_cuDescPPtr[fstColIdx]->cu_id);} elseInsertIdxTableIfNeed(batchRowPtr, m_cuDescPPtr[0]->cu_id);
}
其中,参数 m_isUpdate 用于指示是否执行更新操作。如果 m_isUpdate 为真(true),则表示执行更新操作;如果 m_isUpdate 为假(false),则表示执行插入操作。在函数的后续部分,根据 m_isUpdate 的值来统计插入或更新的次数。
pgstat_count_cu_update 函数
在 CStoreInsert::BatchInsertCommon 函数中还调用了 pgstat_count_cu_update 函数,其主要功能是在统计信息中记录列存(CStore)或 DFS 表的更新操作的数量。它首先获取与给定关系(Relation)关联的统计信息,然后检查是否需要在适当的事务级别记录操作效果。最后,它将更新的数量添加到统计信息中,以反映列存/DFS表的更新操作情况。函数源码如下所示:(路径:src/gausskernel/process/postmaster/pgstat.cpp
)
/** pgstat_count_cu/dfs_update - count a cstore/dfs update* pgstat_count_cu/dfs_update - 记录列存/DFS更新操作*/
void pgstat_count_cu_update(Relation rel, int n)
{// 获取与关系关联的统计信息PgStat_TableStatus* pgstat_info = rel->pgstat_info;// 如果存在统计信息if (pgstat_info != NULL) {// 需要在适当的事务级别记录操作效果int nest_level = GetCurrentTransactionNestLevel();// 如果没有事务信息或者事务嵌套级别与当前不匹配,需要添加新的事务级别统计信息if (pgstat_info->trans == NULL || pgstat_info->trans->nest_level != nest_level)add_tabstat_xact_level(pgstat_info, nest_level);// 更新统计信息中的"更新的元组数"字段,表示列存/DFS更新操作的数量pgstat_info->trans->tuples_updated += n;}
}
pgstat_count_cu_delete 函数
pgstat_count_cu_delete 函数的主要作用是,根据传入的关系(表)和删除的行数(n),统计列存或 DFS 的删除操作的数量,并记录在 PgStat_TableStatus 结构中,以便后续的性能统计和监视。函数首先获取表的 PgStat_TableStatus 信息,然后检查是否在当前事务层次中,如果不是,则添加一个新的事务层次。最后,增加已删除元组的数量。函数源码如下所示:(路径:src/gausskernel/process/postmaster/pgstat.cpp
)
以下代码定义了两个函数 pgstat_count_cu_delete 和 pgstat_count_dfs_delete,用于统计列存(CStore)和分布式文件系统(DFS)的删除操作。
/** pgstat_count_cu/dfs_delete - 统计列存/DFS删除操作的数量*/
void pgstat_count_cu_delete(Relation rel, int n)
{// 获取表的 PgStat_TableStatus 信息PgStat_TableStatus* pgstat_info = rel->pgstat_info;if (pgstat_info != NULL) {// 需要在正确的事务层次记录操作效果int nest_level = GetCurrentTransactionNestLevel();// 如果当前事务层次与记录的事务层次不同,添加新的事务层次if (pgstat_info->trans == NULL || pgstat_info->trans->nest_level != nest_level)add_tabstat_xact_level(pgstat_info, nest_level);// 增加已删除元组的数量pgstat_info->trans->tuples_deleted += n;}
}
pgstat_count_truncate 函数
pgstat_count_truncate 函数用于在执行表的截断操作后更新元组计数器。其主要作用是:根据传入的关系(表),在执行表的截断操作后,重置元组计数器,以便反映截断操作的影响。函数首先获取表的 PgStat_TableStatus 信息,然后检查是否在当前事务层次中,如果不是,则添加一个新的事务层次。接着,保存在截断前的计数器值,然后将插入、更新和删除的计数器重置为0,以便后续的性能统计和监视。
pgstat_count_truncate 函数源码如下所示:(路径:src/gausskernel/process/postmaster/pgstat.cpp
)
/** pgstat_count_truncate - 由于截断而更新元组计数器*/
void pgstat_count_truncate(Relation rel)
{// 获取表的 PgStat_TableStatus 信息PgStat_TableStatus* pgstat_info = rel->pgstat_info;if (pgstat_info != NULL) {// 需要在正确的事务层次记录操作效果int nest_level = GetCurrentTransactionNestLevel();// 如果当前事务层次与记录的事务层次不同,添加新的事务层次if (pgstat_info->trans == NULL || pgstat_info->trans->nest_level != nest_level)add_tabstat_xact_level(pgstat_info, nest_level);// 保存截断前的计数器值pgstat_truncate_save_counters(pgstat_info->trans);// 将插入、更新和删除的计数器重置为0pgstat_info->trans->tuples_inserted = 0;pgstat_info->trans->tuples_updated = 0;pgstat_info->trans->tuples_deleted = 0;}
}
pgstat_update_heap_dead_tuples 函数
pgstat_update_heap_dead_tuples 函数的主要作用是:根据传入的关系(表)和增量值,更新死元组的计数。函数首先获取表的 PgStat_TableStatus 信息,然后将增量值减去 t_delta_dead_tuples,以表示死元组的恢复。这个函数用于非事务性的 “delta” 死元组的计数,不会影响事务性状态,而是直接更新表的计数器。
/** pgstat_update_heap_dead_tuples - 更新死元组的计数** 这个函数的语义是我们正在报告非事务性的 "delta" 死元组的恢复;* 因此,t_delta_dead_tuples 减少而不是增加,并且更改直接进入每个表的计数器,* 而不是进入事务性状态。*/
void pgstat_update_heap_dead_tuples(Relation rel, int delta)
{// 获取表的 PgStat_TableStatus 信息PgStat_TableStatus* pgstat_info = rel->pgstat_info;if (pgstat_info != NULL)// 减少 delta 值,表示死元组的恢复pgstat_info->t_counts.t_delta_dead_tuples -= delta;
}
总结
以上这些函数用于数据库性能统计和监控,可以跟踪指定关系(表)的插入、更新、删除、截断、CU(Chunk Update)等操作,以记录和报告这些操作的频率和数量,帮助数据库管理员识别性能问题并监视数据库的活动。下表对以上函数进行了总结:
函 数 | 作 用 |
---|---|
pgstat_count_heap_insert | 用于统计在指定关系(表)中插入了多少行数据。 |
pgstat_count_heap_update | 用于统计在指定关系(表)中进行了多少次更新操作。 |
pgstat_count_heap_delete | 用于统计在指定关系(表)中进行了多少次删除操作。 |
pgstat_count_truncate | 用于统计在指定关系(表)上执行了多少次截断操作。 |
pgstat_update_heap_dead_tuples | 用于更新关系(表)中的死元组数量统计信息。 |
pgstat_count_cu_update | 用于统计在指定关系(表)的 CU(Chunk Update)上进行了多少次更新操作。 |
pgstat_count_cu_delete | 用于统计在指定关系(表)的 CU 上进行了多少次删除操作。 |
相关文章:

【 OpenGauss源码学习 —— 列存储(autoanalyze)(二)】
列存储(autoanalyze)(二) 概述PgStat_StatTabEntry 结构体pgstat_count_heap_insert 与 pgstat_count_cu_insert 函数CStoreInsert::BatchInsertCommon 函数pgstat_count_cu_update 函数pgstat_count_cu_delete 函数pgstat_count_…...

使用postman 调用 Webservice 接口
1. 先在浏览器地址栏 访问你的webService地址 地址格式: http://127.0.0.1:8092/xxxx/ws(这个自己的决定)/xxxxXccv?wsdl 2. post man POST 访问wwebService接口 地址格式: http://127.0.0.1:8092/xxxx/ws(这个自己的决定)/xxxxXccv <soapenv:Envelope xmlns:soapenv…...

程序员Google插件推荐
文章目录 AdBlock (广告拦截插件)SuperCopy 超级复制Octotree (github增强工具)GitZip for github (github增强工具)JSON-handleSimpleExtManager(管理谷歌插件)OneTab (标签页合并)PostWoman(接口调试)篡改猴 (Tampermonkey)FeHelper(前端助手) AdBlock (广告拦截插件) ☆ 拦截…...

机器学习中常见的监督学习方法和非监督学习方法有哪些。
问题描述:最近面试某些公司算法岗,看到一道简答题,让你举例熟悉的监督学习方法和非监督学习方法。 问题解答: 监督学习方法常见的比较多: 线性回归(Linear Regression): 用于回归问…...

UEFI基础——测试用例Hello Word
Hello 测试用例 硬件环境:龙芯ls3a6000平台 软件环境:龙芯uefi固件 GUID获取网址:https://guidgen.com 一、创建工程 mkdir TextPkg/三个文件 Hello.c 、 Hello.inf 、HelloPkg.dsc 1.1 Hello.c /** fileThe application to print hello …...

【tomcat、java】
java:maven配置 1.安装插件 <build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.1</version><configuration><port&…...

京东获取推荐商品列表 API
item_recommend-获取推荐商品列表 请求参数 请求参数:type 参数说明:type:推荐类型 进入API测试页 响应参数 Version: Date: 名称类型必须示例值描述 items items[]0获取推荐商品列表 num_iid Bigint010021415166448宝贝ID detail_url String0http…...

rust cfg的使用
前提是一个crate倒入另一个crate。 先看结构 test_lib目录结构 这与另一个crate处于同一个目录,所以另一crate倒入的时候在Cargo.toml中使用如下语句。 test_lib = {path = "../test_lib" }先在test_lib/src/abc/abc.rs中添加没有cfg的两个函数做测试。 pub fn…...

电脑屏幕怎么录制?5 个最佳免费录屏软件
您是否想使用网络摄像头录制优酷视频、抖音直播或在线课程等项目,但完全不知道如何开始? 不用担心。有很多软件选项可以帮助您。虽然每一款都有不同的功能,但它们都能够录制网络摄像头并输出精美的高质量视频。 以下是我们精选的最佳作品。…...

vscode 调试使用 make 编译的项目
1、首先点击运行 --> 启动调试: 2、选择g或gcc生成和调试活动文件: 3、出现下面提示是正常的,点击仍要调试: 点击打开“launch.json”: 4、此时会在项目工作目录下生成tsak.josn和launch.json文件: 如…...

Docker修改阿里源
在一次安装rtmp推流服务时,总是无法下载源,估计是国外资源下载超时照成的,于是想到修改为国内源。 docker pull alfg/nginx-rtmp Using default tag: latest latest: Pulling from alfg/nginx-rtmp 530afca65e2e: Retrying in 7 seconds c20…...

有必要买一台内衣裤专洗机吗?家用小洗衣机推荐
随着内衣洗衣机的流行,很多小伙伴在纠结该不该入手一款内衣洗衣机,专门来洗一些贴身衣物,答案是非常有必要的,因为我们现在市面上的大型洗衣机只能做清洁,无法对我们的贴身衣物进行一个高强度的清洁,而小小…...

高精度与高精度的乘法---基础算法
看到一个博主写得不错,我也照猫画虎:) 原因 在计算两个非负整数时,如果位数很大,连 long long 类型都存储不了,就要使用到高精度的乘法 原理 原理依旧是模拟人计算两个数的积,早在小学我们已…...

护眼灯有效果吗?科普护眼灯的作用与推荐
现在我们很多家长对自己孩子的视力十分关心,生怕自己的孩子是近视、远视、弱视等等。对于父母而言,在孩子读书压力大课业重的关键时期,为孩子选择合适的桌椅,保护灯具从而保护孩子的眼睛是非常重要的事情!那么买给孩子读书做功课的…...

【办公自动化】在Excel中按条件筛选数据并存入新的表2.0(文末送书)
🤵♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞Ǵ…...

HDLbits: Lfsr5
我的错误写法,半成品,完全错误: module top_module(input clk,input reset, // Active-high synchronous reset to 5h1output [4:0] q ); dff dff_1(clk, 0 ^ q[0],q[4]);dff dff_2(clk, q[4] ,q[3]);dff dff_3(clk, q[3] ^ q[0] ,q[2]);…...

Visual Studio 错误CS0006:未能找到元数据文件踩坑记录
前言 在写项目的时候,添加了个新的Nuget包,突然就不行,然后就是报错,找不到文件、 出现的原因是因为项目之间互相引用出现了问题,比如如下情况 先版本回退 如果有Git仓库 第一时间去看Git 文件比较,找到…...

tcpdump(三)命令行参数讲解(二)
一 tcpdump实战详解 骏马金龙tcpdump详解 强调: 注意区分选项参数和过滤条件 本文继上篇 网卡没有开启混杂模式 tcpdump默认开启混杂模式 --no-promiscuous-mode --> 可以指定在非混杂模式抓包 ① -vv 控制详细内容的输出 ② -s -s 长度: 可以只…...

面试算法25:链表中的数字相加
题目 给定两个表示非负整数的单向链表,请问如何实现这两个整数的相加并且把它们的和仍然用单向链表表示?链表中的每个节点表示整数十进制的一位,并且头节点对应整数的最高位数而尾节点对应整数的个位数。例如,两个分别表示整数98…...

APP如何设计应用的屏幕截图以提高下载量
APP高质量的应用程序商店屏幕截图,对于建立初始信任以及向潜在用户推销应用程序的优势至关重要。创建应用程序商店屏幕截图,以最好的方式展示我们的应用程序,从而优化应用形象。 1、使用大标题。 确保重点突出品牌的独特性,在屏幕…...

qt 关于自定义控件,然后其他页面提升后背景样式表不生效问题
一、自定义控件如果是widget ,需要再widget 里放一个QFrame ,在QFrame设置样式表背景才行 二、重写paintEvent void Form::paintEvent(QPaintEvent *e) {QStyleOption opt;opt.init(this);QPainter p(this);style()->drawPrimitive(QStyle::PE_Widg…...

对比纯软开与嵌入式硬件开发谁更好呢?
对比纯软开与嵌入式硬件开发谁更好呢? 你的纠结和犹豫是理解的,职业选择确实是一个重要的决策。我明白你在嵌入式和软件开发之间犹豫不决的原因。让我给你提供一些建议,帮助你做出更明智的决定。最近很多小伙伴找我,说想要一些嵌入…...

软考 系统架构设计师系列知识点之软件质量属性(5)
接前一篇文章:软考 系统架构设计师系列知识点之软件质量属性(4) 所属章节: 第8章. 系统质量属性与架构评估 第2节. 面向架构评估的质量属性 相关试题 5. 某公司欲开发一个网上商城系统。在架构设计阶段,公司的架构师…...

修改ubuntu服务器fs文件最大打开数
起因 在对项目进行压测的时候,请求异常 java.net.SocketException: socket closed,查看nginx代理服务器的日志。tail -f -n500 /var/log/nginx/error.log 显示 文件打开数太多socket() failed (24: Too many open files) while connecting to upstream …...

linux下Qt的pro文件
生成生成文件后缀名的说明。这只是泛泛而谈,实际发现跟编译器有关。比如在windows系统上用MinGW,可能静态库还是a后缀。 文件静态库动态库目标文件LINUXasooWINDOWSlibdllobj 在.pro文件中,INCLUDEPATH用于引入外部库的头文件,L…...

git常用命令和开发常用场景
git命令 git init 创建一个空的git仓库或者重新初始化已有仓库 git clone [url] 将存储库克隆到新目录 git add 添加内容到索引 git status 显示工作树状态 git commit -m "" 记录仓库的修改 git reset 重置当前HEAD到指定的状态 git reset –-soft:…...

02 认识Verilog HDL
02 认识Verilog HDL 对于Verilog的语言的学习,我认为没必要一开始就从头到尾认真的学习这个语言,把这个语言所有细节都搞清楚也不现实,我们能够看懂当前FPGA的代码的程度就可以了,随着学习FPGA深度的增加,再不断的…...

解决VUE安装依赖时报错:npm ERR! code ERESOLVE
前言 在使用 npm 安装项目依赖时,有时会遇到错误信息 “npm ERR! code ERESOLVE”,该错误通常发生在依赖版本冲突或者依赖解析问题时。本文将详细介绍出现这个错误的原因,并提供解决方法,确保正确安装项目依赖并避免该错误的发生。…...

软件公司的项目管理软件选择指南
我们经常在项目推进中经常遇到各种各样的问题,最常见的是因团队工作效率低而无法在截止日期之前按时完成工作。但是如果能合理使用项目管理软件,可以有效监控项目进程,提高工作效率,从而保证按时完成任务。那么软件公司适合什么项…...

2、服务器安装docker
# 1.卸载旧的版本 yum remove -y docker \ docker-client\ docker-client-latest\ docker-common docker-latest\ docker-latest-logrotate\ docker-logrotate docker-s…...