CMU15445(2023fall) Project #4 - Concurrency Control踩坑历程
把树木磨成月亮最亮时的样子,
就能让它更快地滚下山坡,
有时会比骑马还快。
完整代码见:
SnowLegend-star/CMU15445-2023fall: Having Conquered the Loftiest Peak, We Stand But a Step Away from Victory in This Stage. With unwavering determination, we press onward, for destiny favors those brave enough to forge ahead, cutting through the thorns and overcoming every obstacle that dares to stand in their way.
目录
Task #1 - Timestamps
1.1 Timestamp Allocation
1.2 Watermark
Task #2 - Storage Format and Sequential Scan
2.1 Tuple Reconstruction
2.2 Sequential Scan / Tuple Retrieval
Task #3 - MVCC Executors
3.1 Insert Executor
3.2 Commit
3.3 Update and Delete Executor
3.4 Stop-the-world Garbage Collection
Task #1 - Timestamps
1.1 Timestamp Allocation
建议上来把UndoLog、Transaction、Transaction_Manager这几个类粗略分析一遍,对lab04的基础框架有个大致的了解。第一个小task实现起来一共也没几行代码,正所谓授人以鱼不如授人以渔,关键是理解read_ts和write_ts到底是怎么来的。
重点看下面这两段话:
- 当一个事务开始时,它会被分配一个读时间戳,即最新已提交事务的提交时间戳。事务提交时,它会被分配一个单调递增的提交时间戳。其实这里说“提交时间戳”我个人认为是带有歧义的,用“时间戳”更为准确。但是由于“时间戳”只能是由“commit”带来增长,普通的read操作并不会导致时间戳的移动,所以这里才用“提交时间戳”。
- 提交时间戳是事务提交的时间。它是一个逻辑计数器,每当事务提交时,提交时间戳都会增加 1。
而read_ts是从1开始计数,那commit_ts就是从2开始计数了。一次只能有一个txn成功提交。在UndoLog的ts_字段中,指的就是这个undolog被提交的时间。我们在访问某个tuple的版本链时,就是用当前txn的read_ts和undolog的ts进行比较,当然这是后话。
1.2 Watermark
核心就是一句话“Watermark is the lowest read timestamp among all in-progress transactions”,至于为啥需要watermark呢?答案在task 3.4的垃圾回收部分——对于那些ts_小于watermark的undolog,我们可以直接删除来节省空间,因为没有txn可以再访问这些过于老旧的undolog。
Watermark的执行流程如下:
- 当出现一个新的txn时,自动调用AddTxn来记录当前所有运行中的txn的read_ts。显然,AddTxn这个操作并不会改变watermark_的值,因为无论是read还是write都是在一条线性增加的时间线上顺序运行的。
- 某个txn执行结束后,自动调用RemoveTxn来删除这个txn。但是,这个操作有可能改变watermark_的值!试想如果当前txn的read_ts是现存最小的,其他存在的read_ts都比它大,那删除这个txn后就需要寻找新的最小read_ts(即更新watermark_)了。
实验说明还提到我们需要在O(log N)的时间复杂度上完成watermark的计算操作,这就间接提醒我们用某种堆来实现了。我这里采用的事优先队列,具体实现细节可以自己想想,给人一种刷LeetCode的感觉嘻嘻。
Task #2 - Storage Format and Sequential Scan
到task2部分就嘻嘻不了了,首先摆在我们面前的难题是理清Undolog、UndoLink、VersionUndoLink和PageVersionInfo之间的区别。老规矩,是时候展示我的匠心图例了。
PageVersionInfo是最为宏观的存在,每个page都会对应一份PageVersionInfo。version_info_我觉得是十分精妙的,利用了每个page_id的唯一性(由Project 02的bufferpool分配),越过table_heap直接管理每个page的版本链信息。
要访问某个tuple,首先就要定位这个tuple所在的page,之后我们就可以对tuple进行需要的操作了。VersionUndoLink应运而生,它作为版本链的头结点,直接指向真正的表头元素。UndoLink就是版本链的每个节点了,更准确地说就像链表节点的next部分,这里我感觉用UndoLinkNode会更加恰如其分,这个可恶的“UndoLink”最开始很容易看作为一条链表而不是一个节点。
那UndoLog又是什么呢?同理,我认为这里叫UndoLogNode会更为恰当。每个对tuple进行操作的txn都会有一份undologs。而UndoLog其实也是有学问的,task3就是和这个问题作斗争。比如图中的TxnY,它的undologs就有两个元素,因为它更改了tuple A和tuple C两个元素。
UndoLink和UndoLog在我看来更像是共生关系。因为UndoLink自己是没有实际的内容的,它只能指向真正有内容的UndoLog,这么看来UndoLog就相当于链表节点的value部分。每个UndoLog内部会包含指向下一个版本链节点的UndoLink,以此类推。
至于版本链的尾节点也就是最后一个有效UndoLog包含的UndoLink,这个“next”指向一个无效的内容。用那张图上的内容来说,A1、B1等元素只是版本链最后一个有效的UndoLog,它们包含的UndoLink会指向一个空的内容,如下图。
顺便一提,tuple的版本链顺序是从新版本指向旧的版本,就像这种图例一样。
2.1 Tuple Reconstruction
从这里可以看出,bustub的Version Storage选择的是“Delta Storage”,我们需要根据update操作使用的partial tuple来构造出一个完整的tuple返回。实验说明给出的这张图是重中之重。
我们需要根据表模式和已修改字段构造元组的部分模式,然后逐渐将这个partial tuple凑成完整的tuple。在# Vn+1版本的UndoLog中未进行修改的字段由# Vn版本UndoLog中修改的字段进行补足,若还是无法形成一个完整的tuple,继续遍历版本链。如果遍历完版本链还是无法形成tuple,就用base_tuple来补全剩余字段。一言蔽之,就是某个tuple[A*, B*, C*]的每个value都应该是最新的值。
总结一下可以分为三步走
- 获得undoLog的schema,也就是undoLog中partial tuple的schema类型
- 用base schema和partial schema进行比对,从而用undoLog中的partial tuple来修改原始的base tuple。
- 一步步形成完整的tuple(可能需要借助版本链和base tuple)
2.2 Sequential Scan / Tuple Retrieval
这个task是让我们修改Project 03里面的SeqScan来支持MVCC,还是先回顾如何遍历table:
- 利用table_heap提供的iter遍历表中的tuple
- 用tuple的rid来获得它的VersionUndoLink,也就是版本链的头结点
- 利用版本链和base tuple拼凑出完整的tuple,即调用reconstruction()
- 利用filter_expr来判断tuple是否符合过滤条件
OK,把最重要的框架搭建出来了以后,我们可以着手其中细节。需要注意的一点是区分tuple的ts状态——是正在被某个txn修改,还是已经被提交了呢?在执行MVCC版的SeqScan过程中,有三种情况需要我们特别注意:
- Table heap中的元组是最新的数据,并且这个数据已经提交了,当前txn的read_ts>tuple.ts可以直接读取
- 表堆中的元组包含当前事务的修改,即当前tuple的ts是一个很大的数字,而且恰好等于当前访问txn的id号,也可以直接访问
- 表堆中的元组 (1) 被其他未提交事务修改,或 (2) 时间戳比事务的读时间戳更新。在这种情况下,我们需要遍历版本链
如果还是没有完全理解SeqScan的具体工作,可以从分析ScanTest入手,我当时就是这么干的。这里我放几张当时分析的图:
再贴上对应的debug过程
Tuple1的首链,它的prev_txn_id是5,对应txn_store3,正好是prev_log_2这个。
这里可以看到log的操作为Del。
可以发现这个txn_store_2确实是有两个操作
Task #3 - MVCC Executors
3.1 Insert Executor
Insert部分的逻辑还是比较简单的,对我们在Project 03中实现的代码稍加改进就可以通过测试了。老规矩,先总结出Insert的大体框架:
- 创建新的tuple并设置meta,注意这里的ts应该设置为临时事务时间戳
- 调用InsertTuple()插入到table heap中
- 设置txn对应的write_set,在commit阶段一起提交所有的修改元组rid
其他细枝末节和以前的是一样的,还有一点需要注意:到底要不要在Insert阶段生成一条UndoLog呢?我个人认为是需要的,别的不说,单从UndoLogs的可读性来讲为Insert操作生成UndoLog就是必要的,但是根据lab的测试点来说并不推荐这种做法。But,我认为咱们都是抱着学习而不是功利地通过测试点的态度来完成lab的,所以这里我坚持自己的做法——为每个完成的Insert操作生成一条UndoLog,作为版本链的起始节点。当然要是想修改也不是很麻烦,这就仁者见仁智者见智了。
3.2 Commit
Commit部分实在是过于友好,实验说明部分直接把大致框架帮我们罗列出来了:
- 获取提交锁(commit mutex)。
- 获取提交时间戳(但不要增加 last-committed timestamp,因为直到提交完成,提交时间戳的内容才会稳定)。
- 遍历所有由此事务修改的元组(使用写集),将base tuple的时间戳设置为提交时间戳。你需要在所有修改执行器(插入、更新、删除)中维护写集。
- 将事务设置为已提交状态,并更新事务的提交时间戳。
- 更新 last_committed_ts。
在这里值得我们思考的问题是:为什么每个txn的write_set_需要table_oid呢?换个问法可能会更直观,我们怎么通过rid找到对应的tuple呢?
std::unordered_map<table_oid_t, std::unordered_set<RID>> write_set_;
我的想法是:Catalog利用table_id找到对应的table info,接下来利用table info得到这个表的table heap。得到了table heap我们就可以根据写集中的rid找到对应的tuple了。
如果要为Insert生成UndoLog,在commit阶段生成比较好。在Insert executor中生成也行,不过最后还是需要在commit阶段修改这个UndoLog。
3.3 Update and Delete Executor
Update executor需要讨论的情况就比较多了,看实验说明那一大堆基本都是在说update的各种情况。先来过一遍实验说明的重点部分:
在检查完写写冲突后,你可以继续实现更新/删除逻辑。
- 为修改生成 undo 日志。对于删除操作,生成包含原始元组完整数据的 undo 日志。对于更新操作,生成仅包含修改列的 undo 日志。将 undo 日志存储在当前事务中。如果当前事务已经修改过该元组并且有 undo 日志,它应当更新现有的 undo 日志,而不是创建新的。#没有就创建新的,有就直接append。
- 更新元组的下一个版本链接,指向新的 undo 日志。
- 更新表堆中的基础元组及其元数据。
实验说明有一句话是核心:“检测写写冲突的唯一条件是检查基础元组元数据的时间戳”。从这句话可以看出每个txn是可以看到正在被处理(包括update和delete)的元组的ts的,只是它们此时就不具备修改这个正在更新的tuple的权限。
这个task最核心的工作是总结修改操作会发生write-write conflict几种情况,大致可以分为以下几种:
- 保证修改操作的原子性。Tuple1正在被txn1修改(包括update和delete),即tuple1的ts_=txn1。此时如果其他txn想要修改tuple1就会出现写冲突,但是如果别的txn拥有的read_ts足够大是可以看到这个正在被修改的tuple1的。
- 不可重复修改。Tuple1已经被txn1删除并且提交了,假设此时tuple1的ts_=5。Txn2的read_ts=4,则txn2只能读到旧版本的tuple1。假设txn2对这个旧版本的tuple1进行修改,那就会出现重复修改的写写冲突。
除了写冲突外,还有些特殊情况需要进行讨论。
- 假如txn1进行insert操作(insert tuple1 with [1, 1, 1]),在这个insert操作还没提交的情况下,所有基于insert操作的后续update/delete都不应该生成新的undolog。
- 假如tuple1的内容为[1, 1, 1],txn1进行update tuple1 with [1, _, _]到底算不算update呢?从我个人的角度来看是不算的,但是测试点上却把这算作了一条update,这点我是不认可的。因为比较是否更新是用Value::CompareExactlyEquals判断新插入的value和旧value是否相等,这里显然不符合update的条件。
- 假如tuple1的内容为[1, 1, 1],txn1进行update tuple1 with [2, _, _],但是没有提交。过了一会儿txn1又进行update tuple1 with [_, 3, _],此时tuple1临时变为[2, 3, 1],相应的修改位也就是[T, T, F]。而不仅仅是基于第二次update的[F, T, F]。
理清上面几种情况之后,就可以着手完成代码了,顺带一提Delete Executor实现起来要比Update Executor简单得多。最后还有两个问题需要理清楚
Q:update的步骤
A:可以像以前一样删除旧的tuple再插入新的tuple吗?不可以,用UpdateTupleInPlace
就地更新。
Q:将删除的tuple的ts置为0有问题吗
A:没什么问题。因因为如果txn是在正常提交的话,那commit必然会大于0.
接下来分享我在实现时遇到的问题
①在修改tuple的meta时只修改了临时变量,没有真正修改tuple。属于是掩耳盗铃了。
②实验说明里面提到
搞得我还真以为是在某处代码中将时间戳设置为0,结果检查半天发现是我自己设置的
③注意:commit时一定要分清楚修改base tuple和修改对应版本链的区别,两者都需要修改。
④不能重复修改已经删除的tuple,就像下面这样
⑤在这种情况中,如果一个insert操作还没有提交,那基于这个insert操作的所有操作(update和delete)都不应该生成undolog。
⑥UpdateTest2中,如果一个update没有提交,那这个txn的后续update或者del都不用生成新的undolog。要注意后续的更改都是要基于base tuple,而不是基于前一个修改的tuple。
⑦UpdateConflict。这里出现的错误是txn3修改的时间小于tuple2提交的时间。
更具体一点,txn3的read_ts=1,所以它只能看到tuple0的版本0,实际上tuple0的版本1也完成了提交。此时txn3试图更改tuple0在版本0的内容,这就会造成另一种写冲突。因为txn3如果成功更改了版本0,那它就会生成tuple0新的版本1,这样自然就和tuple0已有的版本1发生冲突,违背了可重复读的原则。也就是我之前提到的。
3.4 Stop-the-world Garbage Collection
这个task还是比较简单的,简单过一遍实验说明就可以了。
你需要遍历表堆和版本链,识别仍然可以被任何正在进行的事务访问的 undo 日志。如果一个事务已经提交或中止,并且其不包含任何可以被正在进行的事务访问的 undo 日志,你可以简单地将其从事务映射中移除。
需要注意的是, 不需要更新前一个 undo 日志来修改悬挂指针并使其成为无效指针,将其保留在那里是可以的。也就是说如果应该txn的所有undologs都过于老旧,对其他的txb都不可见,那就删除这个txn,不用手动释放它的undolog。
相关文章:
CMU15445(2023fall) Project #4 - Concurrency Control踩坑历程
把树木磨成月亮最亮时的样子, 就能让它更快地滚下山坡, 有时会比骑马还快。 完整代码见: SnowLegend-star/CMU15445-2023fall: Having Conquered the Loftiest Peak, We Stand But a Step Away from Victory in This Stage. With unwavering…...
腿足机器人之十三-强化学习PPO算法
腿足机器人之十三-强化学习PPO算法 腿足机器人位姿常用强化学习算法PPO算法核心原理PPO算法的创新设计PPO算法典型流程优势函数 对于复杂地形适应性(如楼梯、碎石路),传统的腿足机器人采用基于模型的控制器,该方法依赖精确动力学建…...
ubuntu下r8125网卡重启丢失修复案例一则
刚装的一台服务器,ubuntu24.04,主板网卡是r8125,安装服务后会莫名其妙丢失驱动 按照官网的方法下载最新8125驱动包: Realtek 然后卸载驱动 rmmod r8125 然后在驱动包里安装(幸好我之前装了build-essential&#x…...
设计一个“车速计算”SWC,通过Sender-Receiver端口输出车速信号。
1. 需求分析 功能目标:根据车轮脉冲信号(轮速传感器输入)计算当前车速,并将结果通过Sender端口发送给其他SWC。 输入:轮速脉冲数(如WheelPulse,类型uint32)。 输出:车速(如VehicleSpeed,类型float32,单位km/h)。 触发方式:周期性计算(例如每10ms执行一次)。 2.…...
DeepSeek 使用窍门与提示词写法指南
一、通用提示词技巧 窍门分类技巧说明示例提示词明确需求用“角色任务要求”明确目标作为健身教练,为30岁上班族设计一周减脂计划,需包含饮食和15分钟居家训练结构化提问分步骤、分模块提问第一步:列出Python爬虫必备的5个库;第二…...
MySQL零基础教程12—聚合查询(聚合函数)
背景 有时候我们需要汇总一些数据,比如查询一个班级的平均分数,这个时候我们需要的是把分数汇总,然后计算出一个平均值进行返回,并不需要返回某一列的值,针对这种场景,mysql中提供了一些聚合函数帮助快速完…...
JMeter 引入 JAR 包的几种方法
JMeter 支持加载外部 JAR 文件,用于: 扩展 JMeter 功能使用 Java 代码(BeanShell / JSR223)连接数据库 / 解析 Excel / 读取 CSV 📌 1. JMeter 引入 JAR 包的方式 ✅ 方式 1:将 JAR 放入 lib/ 或 lib/ext…...
一周一个Unity小游戏2D反弹球游戏 - 球板的发球
前言 本文将实现当游戏开始时球在球板上,且不具备物理性,在Windows平台上通过点击屏幕来球发射,安卓平台上当手指触摸到屏幕上时进行发球,并此时开始具备物理性。 发球逻辑 首先在球板上创建一个球的发射点,新建一个空的游戏物体,并命名为BallPoint,并将其作为SpringBoa…...
C++Primer学习(4.8位运算符)
4.8位运算符 位运算符作用于整数类型的运算对象,并把运算对象看成是二进制位的集合。位运算符提供检查和设置二进制位的功能,如17.2节(第640页)将要介绍的,一种名为bitset的标准库类型也可以表示任意大小的二进制位集合,所以位运算符同样能用…...
Linux | Ubuntu 与 Windows 双系统安装 / 高频故障 / UEFI 安全引导禁用
注:本文为 “buntu 与 Windows 双系统及高频故障解决” 相关文章合辑。 英文引文,机翻未校。 How to install Ubuntu 20.04 and dual boot alongside Windows 10 如何将 Ubuntu 20.04 和双启动与 Windows 10 一起安装 Dave’s RoboShack Published in…...
SpringSecurity 实现token 认证
配置类 Configuration EnableWebSecurity EnableGlobalMethodSecurity(prePostEnabledtrue) public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { Bean Override public AuthenticationManager authenticationManagerBean() throws Exception {return s…...
C语言基础要素(007):使用变量
定义变量的同时可以给定一个值,这叫初始化变量;未初始化的变量,其值在程序运行时是不确定的。变量在定义之后可以多次设置值,这称为赋值。定义使得变量从无到有,而赋值则改变已有变量的状态。变量只能被初始化一次&…...
6. Nginx 动静分离配置案例(附有详细说明+配图)
6. Nginx 动静分离配置案例(附有详细说明配图) 文章目录 6. Nginx 动静分离配置案例(附有详细说明配图)1. 动静分离概述说明2. 先使用传统方式实现,不使用 Nginx3. 使用上 Nginx 实现动静分离优化步骤4. 最后: 1. 动静分离概述说明 什么是动静分离&…...
Deepseek对ChatGPT的冲击?
从测试工程师的视角来看,DeepSeek对ChatGPT的冲击主要体现在**测试场景的垂直化需求与通用模型局限性之间的博弈**。以下从技术适配性、效率优化、风险控制及未来趋势四个维度展开分析: --- ### **一、技术适配性:垂直领域能力决定工具选择…...
在已安装二进制movit2的情况下使用自编译moveit2
在已安装二进制movit2的情况下,想使用自编译moveit2,只要引入一下自编译moveit2库的环境变量即可。主要是想搞明白这个过程发生了什么,也就是引入环境后有什么变化,以及如何对编译过程产生影响 一、setup.bash流程 所有资料上都…...
React 常见面试题及答案
记录面试过程 常见问题,如有错误,欢迎批评指正 1. 什么是虚拟DOM?为什么它提高了性能? 虚拟DOM是React创建的一个轻量级JavaScript对象,表示真实DOM的结构。当状态变化时,React会生成新的虚拟DOM…...
2025-03-01 学习记录--C/C++-C语言 整数类型对比
C语言 整数类型对比 类型位数范围(有符号)范围(无符号)格式化符号char8-128 到 1270 到 255%c 或 %hhdshort16-32,768 到 32,7670 到 65,535%hdint32-2,147,483,648 到 2,147,483,6470 到 4,294,967,295%dlong32 或 64-2,147,483…...
金融赋能绍兴纺织 民生银行助力外贸中小微企业“走出去”
在浙江绍兴,纺织业作为一张熠熠生辉的产业名片,承载着深厚的历史底蕴与蓬勃的发展活力。这里依傍长三角经济圈,交通网络纵横交错,将原材料产地与广阔市场紧密相连;产业集群高度成熟,上下游产业链完备&#…...
TCP的三握四挥
TCP协议 TCP( Transmission control protocol )即传输控制协议,是一种面向连接、可靠的数据传输协议,它是为了在不可靠的互联网上提供可靠的端到端字节流而专门设计的一个传输协议。 TCP的基本特点 面向连接:通信双方在进行数据传输之前&…...
Phpstudy中的MySQL无法正常启动或启动后自动暂停,以及sqlilab环境搭建出现的问题解决方法
【解决方法】 无法启动的原因是Phpstudy中的MySQL与本地的mysql重名,导致无法正常启动;所以这时我们就需要将本地的MySQL进行修改名称; 或者修改phpstudy中数据库的端口号,但是我觉得还是不是很好解决这种问题 最后一个方法&#…...
用C语言实现一个链表(四)
用C语言实现一个链表(四) 在上期内容中,我们探讨了实现一个双向循环链表的准备工作以及一些功能——创建新结点,初始化头结点,尾插数据,尾删数据,遍历的代码,上期内容留下了一个判断…...
【我的 PWN 学习手札】House of Kiwi
House of Kiwi 之前我们利用IO_FILE一般是通过劫持vtable来实现的, House of Kiwi虽然不是通过劫持vtable来实现,但实质上是劫持vtable指向的全局的_IO_file_jumps_表来实现的。注意:对于某些版本的glibc,_IO_file_jumps_并不可写…...
象棋笔记-实战记录
文章目录 实战没发现杀招2024-06-16 实战又漏杀了,尴尬,炮震五子,3路炮有下底的机会,本来是绝杀,没算明白,以为窝心马和象都能看住这个点。。。2024-07-06 实战有进炮串打的机会,又错过了&#…...
RabbitMQ系列(六)基本概念之Routing Key
在 RabbitMQ 中,Routing Key(路由键) 是用于将消息从交换机(Exchange)路由到指定队列(Queue)的关键参数。其核心作用是通过特定规则匹配绑定关系,确保消息被正确分发。以下是其核心机…...
企业微信里可以使用的企业内刊制作工具,FLBOOK
如何让员工及时了解公司动态、行业资讯、学习专业知识,并有效沉淀企业文化?一份高质量的企业内刊是不可或缺的。现在让我来教你该怎么制作企业内刊吧 1.登录与上传 访问FLBOOK官网,注册账号后上传排版好的文档 2.选择模板 FLBOOK提供了丰富的…...
JAVA笔记【一】
现实 (抽象) 类 (创建) 对象 特点: 1.面向对象 2.跨平台 3.安全性 4.多线程 java程序基本结构 1. java源代码文件实际是普通的文本文件,源代码文件必须是.java扩展名,且必须小写 2. …...
Mybatis做批量操作
前面我们将动态标签foreach的时候,做过批量操作,但是foreach只能处理记录数不多的批量操作,数据量大了后,先不说效率,能不能成功操作都是问题,所以这里讲一讲Mybatis正确的批量操作方法: 在获取…...
C/C++动静态库的制作与原理 -- 静态库,动态库,目标文件,ELF文件,动态链接,静态链接
目录 1. 什么是库 2. 静态库 2.1 静态库的制作 2.2 静态库的使用 3. 动态库 3.1 动态库的制作 3.2 动态库的使用 4. 目标文件 5. ELF文件 6. ELF从形成到加载轮廓 6.1 ELF形成可执行 7.2 ELF可执行文件加载 7. 理解链接和加载 7.1 静态链接 7.2 ELF加载与进程地…...
Java 并发编程之synchronized
一、前言 在并发编程中,多个线程访问同一个共享资源时,我们必须考虑如何维护数据的原子性。在JDK1.5之前,Java是依靠Synchronized关键字实现锁功能来做到这点的。Synchronized是JVM实现的一种内置锁,锁的获取和释放是由JVM隐式实…...
Windows 11【1001问】查看Windows 11 版本的18种方法
随着技术的飞速发展,操作系统作为连接硬件与软件的核心桥梁,其版本管理和更新变得尤为重要。对于用户而言,了解自己设备上运行的具体Windows 11版本不仅有助于优化系统性能,还能确保安全性和兼容性。然而,不同场景和需…...
python 元组tuple
元组:有序不可变列表 (相当于只读的list) 注意:元组里的普通元素不可以修改,但是元组里的list可以修改 index(元素) 查找某个元素,有的话返回下标,没有的话报错 count(元素) 统计某元素在元组中出现的次数 len(元组) 统计元组内的元素个数 #定义元组,元组支持嵌套 t1("…...
485 多路信号采集,校验干扰问题
在RS-485总线中同时采集多路信号时,若某一路出现CRC校验失败,通常由总线冲突、信号干扰或硬件设计缺陷引起。以下是具体影响分析和解决方案: 一、多路信号同时采集的影响 1. 总线冲突风险 现象:多路信号同时发送时,485总线(半双工)无法区分信号,导致数据叠加损坏。 后…...
【Eureka 缓存机制】
今天简单介绍一下Eureka server 的缓存机制吧✌️✌️✌️ 一、先来个小剧场:服务发现的"拖延症" 想象你是个外卖小哥(客户端),每次接单都要打电话问调度中心(Eureka Server):“现在…...
MySQL并发知识(面试高频)
mysql并发事务解决 不同隔离级别下,mysql解决并发事务的方式不同。主要由锁机制和MVCC(多版本并发控制)机制来解决并发事务问题。 1. mysql中的锁有哪些? 表级锁: 场景:表级锁适用于需要对整个表进行操作的情况,例如…...
Git GitHub基础
git是什么? Git是一个分布式版本控制系统,用于管理源代码的变更。它允许多个开发者在同一个项目上协作,同时跟踪每个修改的历史记录。 关键词: 分布式版本控制软件 软件 安装到我们电脑上的一个工具 版本控制 例如论文&…...
Rabbit MQ 高频面试题【刷题系列】
文章目录 一、公司生产环境用的什么消息中间件?二、Kafka、ActiveMQ、RabbitMQ、RocketMQ有什么优缺点?三、解耦、异步、削峰是什么?四、消息队列有什么缺点?五、RabbitMQ一般用在什么场景?六、简单说RabbitMQ有哪些角…...
Ubantu22.04系统docker部署Open WebUI+Ollama【教程】
Open WebUI 是一个可扩展、功能丰富且用户友好的自托管 AI 平台,旨在完全离线运行。它支持各种 LLM 运行器,如 Ollama 和 OpenAI 兼容的 API,并内置了 RAG 推理引擎,使其成为强大的 AI 部署解决方案。 1.docker拉取镜像 &#x…...
知识图谱科研文献推荐系统vue+django+Neo4j的知识图谱
文章结尾部分有CSDN官方提供的学长 联系方式名片 文章结尾部分有CSDN官方提供的学长 联系方式名片 关注B站,有好处! 📑 编号:D030 📑 vuedjangoneo4jmysql 前后端分离架构、图数据库 📑 文献知识图谱&#…...
我的世界开发模组的心得体会
最头疼的问题 本人也是小白,也就跟着ai学学怎么开发模组,不会的上网搜搜,但是目前最令我头疼的就是运行rundata和runcilent时的模块冲突,解决办法就是使用以下的build.gradle代码,不要接受人工智能的建议,…...
HTML:自闭合标签简单介绍
1. 什么是自结束标签? 定义:自结束标签(Self-closing Tag)是指 不需要单独结束标签 的 HTML 标签,它们通过自身的语法结构闭合。语法形式: 在 HTML5 中:直接写作 <tag>,例如 …...
Oracle性能调优(一):时间模型统计
Oracle性能调优(一):时间模型统计 时间模型统计视图时间模型统计指标时间模型统计视图 📖 DB Time的含义: DB Time表示前台会话在数据库调用中所花费的总时间,它是衡量数据库实例总负载的一个重要指标。DB Time是从实例启动时开始累计测量的,其计算方法是将所有前台会话…...
MacBook Pro使用FFmpeg捕获摄像头与麦克风推流音视频
FFmpeg查看macos系统音视频设备列表 ffmpeg -f avfoundation -list_devices true -i "" 使用摄像头及麦克风同时推送音频及视频流: ffmpeg -f avfoundation -pixel_format yuyv422 -framerate 30 -i "0:1" -c:v libx264 -preset ultrafast -b:v 1000k -…...
【构建工具】Gradle Kotlin DSL中的大小写陷阱:BuildConfigField
在Android开发当中,BuildConfig是一个非常有用的功能,它允许我们在构建过程中定义常量,并在运行时使用它们。But!!当我们从传统的Groovy DSL迁移到Kotlin DSL时或者被Android Studio坑的时候,有一些细微的差…...
Linux网络 TCP全连接队列与tcpdump抓包
TCP全连接队列 在 Linux 网络中,TCP 全连接队列(也称为 Accept 队列)是一个重要的概念,用于管理已经完成三次握手,即已经处于 established 状态但尚未被应用程序通过 accept( ) 函数处理的 TCP 连接,避免因…...
ChatGPT与DeepSeek:开源与闭源的AI模型之争
目录 一、模型架构与技术原理 二、性能能力与应用场景 三、用户体验与部署灵活性 四、成本与商业模式 五、未来展望与市场影响 六、总结 随着人工智能技术的飞速发展,ChatGPT和DeepSeek作为两大领先的AI语言模型,成为了行业内外关注的焦点。它们在…...
泛微Ecode新增Button调用服务器中的JSP页面里的方法
前言 前端Ecode调用 后端接口编写 JSP文件方法 总结 前言 因为我们是从之前E8版本升级到E9的,所以会有一些接口是通过jsp文件来实现前后端调用的,这里介绍的就是如果你有接口是写在jsp文件里面调用的,但是你又想在Ecode中调用的对应的接…...
知识图谱+智能问诊预诊系统vue+django+neo4j架构、带问诊历史
文章结尾部分有CSDN官方提供的学长 联系方式名片 文章结尾部分有CSDN官方提供的学长 联系方式名片 关注B站,有好处! 🤍编号:D032 🤍智能问答:智能问答自诊、预诊功能,同时可以保存问答历史 &…...
redis repl_backlog_first_byte_offset 这个字段的作用
repl_backlog_first_byte_offset 是 Redis 复制积压缓冲区(Replication Backlog)中的一个关键字段,其作用是 标识积压缓冲区中第一个字节对应的全局复制偏移量。 通俗解释 当主从节点断开重连时,Redis 需要通过复制积压缓冲区&am…...
第49天:Web开发-JavaEE应用SpringBoot栈模版注入ThymeleafFreemarkerVelocity
#知识点 1、安全开发-JavaEE-开发框架-SpringBoot&路由&传参 2、安全开发-JavaEE-模版引擎-Thymeleaf&Freemarker&Velocity 一、开发框架-SpringBoot 参考:https://springdoc.cn/spring-boot/ 访问SpringBoot创建的网站 1、路由映射 RequestMapping…...
python数据容器切片
从一个序列中取出一个子序列 序列[起始位置:结束位置:步长] 起始位置和结束位置 省略,表示从头取到尾 步长省略表示1 步长负数,表示从后往前取 步长-1 等同于将序列反转了...