P2 B+树索引
文章目录
- Task1 B+树页
- B+树页
- B+树内部结点
- B+树叶子结点
- Task2 B+树操作
- Task2 B+树插入和搜索的单一值
- 插入单一值
- 搜索单一值
- Task2 B+树删除
- Task3 叶子扫描的迭代器
- Task4 并行索引
Task1 B+树页
B+树页
实际上是每个B+树页面的标题部分,包含叶子页面和内部页面共享的信息。
报头格式(大小以字节为单位,共12字节):
| PageType (4) | CurrentSize (4) | MaxSize (4) |// define page type enum
enum class IndexPageType { INVALID_INDEX_PAGE = 0, LEAF_PAGE, INTERNAL_PAGE };
// 成员变量,内部页和叶页共享的属性
IndexPageType page_type_ __attribute__((__unused__));
int size_ __attribute__((__unused__)); // 键值对的数量,内部页的第一个键为无效值
int max_size_ __attribute__((__unused__)); // 键值对的容量
B+树内部结点
在内部页面中存储n个索引键和n+1个子指针(page_id)。
指针PAGE_ID(i)指向一个子树,其中所有键K满足:K(i) <= K < K(i+1)。
注意:由于键的数量不等于子指针的数量,所以第一个键总是无效的。也就是说,任何搜索/查找都应该忽略第一个键。
内部页面格式(键按递增顺序存储):
| HEADER | KEY(1)+PAGE_ID(1) | KEY(2)+PAGE_ID(2) | ... | KEY(n)+PAGE_ID(n) |#define MappingType std::pair<KeyType, ValueType>
//key -> page_id 的映射
MappingType array_[0];
B+树叶子结点
只支持唯一键。
叶页格式(键按顺序存储):
| HEADER | KEY(1) + RID(1) | KEY(2) + RID(2) | ... | KEY(n) + RID(n)HEADER格式(大小为字节,共16字节):
| PageType (4) | CurrentSize (4) | MaxSize (4) | NextPageId (4)page_id_t next_page_id_; // 先定位到一个叶子,再顺序向后扫描
//key->record_id 的映射,record id = page id combined with slot id
MappingType array_[0];
Task2 B+树操作
大部分操作在b_plus_tree.cpp实现
Context类用于记录操作B+树过程中需要记忆的路径、读写保护等
class Context {public:// 当您插入/删除B+树时,将头页的写保护存储在这里。// 当您想要解锁所有内容时,请记住取消标题页保护并将其设置为空值,即header_page.reset()std::optional<WritePageGuard> header_page_{std::nullopt};//在这里保存根页面id,以便更容易地知道当前页面是否是根页面。page_id_t root_page_id_{INVALID_PAGE_ID};// 要修改的页面的写保护的队列。std::deque<WritePageGuard> write_set_;// 读保护队列std::deque<ReadPageGuard> read_set_;auto IsRootPage(page_id_t page_id) -> bool { return page_id == root_page_id_; }
};
Task2 B+树插入和搜索的单一值
插入单一值
BPLUSTREE_TYPE::Insert()实现,若BPLUSTREE_TYPE::header_page_id_属性标识的根节点的页(BPlusTreeHeaderPage)的根节点页id无效,说明没有根页面,调用缓冲区的NewPage()新建root页,然后强转为BPlusTreeLeafPage类型,此时根节点就是叶子节点,然后BPlusTreeLeafPage::InsertAtLast()根叶后插k-v,标识正在使用该页,清空header_page_(检查header_page_id_标识的根节点页时,页就存在这里)。若有根节点就获取根节点页id,调用InsertIntoLeaf()
BPLUSTREE_TYPE::InsertIntoLeaf()开头就调用了BPLUSTREE_TYPE::FindLeaf(),此函数的作用是为插入/删除结点操作寻找目标叶,结果存在B+树类的Context类成员的write_set_中。回到函数,取出查找到的页,强转为BPlusTreeLeafPage后直接调用BPlusTreeLeafPage::Insert()插入k-v,若插入失败说明是重复键值对,清空header_page_和write_set_后退出,并且若叶页大小没超过叶子最大容量则此插入是安全插入,清空header_page_和write_set_后退出,剩下的情况就是叶页满了,需要分裂,调用Split()进行分裂,返回的新页引用是满页分裂出的兄弟叶页,之后调用InsertIntoParent()修改父结点结构。
BPLUSTREE_TYPE::InsertIntoParent()是个直接递归,在函数开头有个判断write_set_内的锁有一个时,这些锁是在InsertIntoLeaf()中调用FindLeaf()时产生的,表示修改该叶子会导致write_set_中的这些结点页被修改,即最接近叶子的安全结点到叶子节点这一路径上的所有结点的锁。在write_set_中只剩一个页时,NewPage()新建一个页,当作新根节点,然后旧根节点和分裂出的新结点做新根的子节点,返回。通过write_set_获取父结点,然后调用BPlusTreeInternalPage::InsertAfterValue()将分裂后产生的新节点插入父节点,write_set_弹出旧结点(原来不安全的结点)。当父节点的大小超过了最大容量,则父节点不安全,调用Split()分裂并调用InsertIntoParent()递归,若是安全结点,则清理header_page_和write_set_,结束。
BPLUSTREE_TYPE::Split()分裂结点,返回新的兄弟页。NewPage()创建新页,叶子节点分裂就将新页和待分裂的页强转为叶页,初始化新叶页后旧叶页调用BPlusTreeLeafPage::SplitLeafTo(),取出旧叶页的后一半数据给新叶页,并且设置旧叶页和新叶页的next_page_id。最后设置正在使用新叶页,然后返回。若分裂内部节点,将新页和待分裂的页强转为内部结点页,新结点页初始化后旧结点页调用BPlusTreeInternalPage::SplitInternalTo()取内部节点的最大容量加一的一半(后半段)的内容给新内部节点,限定结点大小。设置新结点页正在使用后返回。
这里我不理解的是在InsertIntoParent()中仅仅用if (ctx.write_set_.size() == 1)就确定是根节点分裂
搜索单一值
BPLUSTREE_TYPE::GetValue()实现,获取头部页的写锁header_page_,通过它获得根节点id,将根节点读锁加入read_set_然后强转为BPlusTreePage,清空header_page_,强转结点为BPlusTreeInternalPage,调用BPlusTreeInternalPage::FindInternelKey()按key二分查找中间节点的子节点引用,查到的页加读锁,加入read_set_然后将原先在read_set_中的父页弹出,这样就实现了读锁的"下沉",之后进入循环直到页是叶页。循环出来后将页强转为BPlusTreeLeafPage类,之后调用BPlusTreeLeafPage::FindKey()二分查找叶子节点的值,找到后组装传参vector。
Task2 B+树删除
BPLUSTREE_TYPE::Remove()实现,获取头部页写锁并得到根节点id,调用FindLeaf()查找key,获取查到的页强转成BPlusTreeLeafPage,调用BPlusTreeLeafPage::DeleteKey()先二分查找叶子结点key,若没有调用DeleteKeyAt()删除数组中对应的k-v,删除失败就清理header_page_和write_set_并退出,若删除的k-v所在的叶页大小大于等于最小容量(叶子结点是max_size_/2,内部节点是(max_size_+1)/2)时,即为安全删除,清空write_set_并退出,若根节点时叶子,叶子根节点有一个键值对就能退出。最后都没有退出的就是需要非安全删除情况,可能会有修改父节点key、小于最小尺寸需从兄弟节点处拿甚至兄弟节点也不足直接合并,调用DealNode()合并或删除叶页。
BPLUSTREE_TYPE::DealNode()是个间接递归,它调用Merge()而Merge()调用它。处理借取键值对或合并节点,write_set_的最后一个值是这个函数体需要处理的不安全的节点(已进行删除工作)。
-
若是根节点
- 根叶:清理
write_set_和header_page_ - 不是叶:强转为内部节点,取第0个的引用做根节点id(
header_page_强转为BPlusTreeHeaderPage可取root_page_id_)
返回
- 根叶:清理
-
不是根
从
write_set_取出该页的父页,强转为内部节点-
是叶
强转为叶页,父页调用FindInternelKey()二分查找叶子key获得index左边有值,有左兄弟(index > 0):取左兄弟(父页的index-1)的写锁存入
write_set_,强转为叶页后保存变量右边有值,有右兄弟(index < 父页容量-1):取右兄弟(父页的index+1)的写锁存入
write_set_,强转为叶页后保存变量- 左右兄弟都在(左叶页变量 && 右叶页变量):
- 两个兄弟都不足(兄弟大小 <= 最小容量):调用
Merge()与右节点合并,返回 - 右边有剩余(右兄弟大小 > 最小容量):清空
header_page_,向兄弟节点借键值对表示父页是安全节点,释放write_set_中父页的祖先节点及根的锁,叶页调用LendFromBrother()向右兄弟借,之后父页调用SetKeyAt()更新父页中的右兄弟结点在父页的索引,然后清理write_set_ - 左边有剩余(else):清空
header_page_,释放write_set_中父页的祖先节点及根的锁,叶页调用LendFromBrother()向左兄弟借,父页调用SetKeyAt()更新父页中的右兄弟结点在父页的索引,然后清理write_set_
- 两个兄弟都不足(兄弟大小 <= 最小容量):调用
- 只有左兄弟(左叶页变量):
- 左兄弟不足(左兄弟大小 <= 最小容量):调用
Merge()与左兄弟合并,返回 - 左边有剩余:清空
header_set_,释放write_set_中父页祖先节点及根的锁,叶页调用LendFromBrother()向左兄弟借,父页调用SetKeyAt()更新父页中的左兄弟结点在父页的索引,然后清理write_set_
- 左兄弟不足(左兄弟大小 <= 最小容量):调用
- 只有右兄弟(右叶页变量):
- 右兄弟不足(右兄弟大小 <= 最小容量):调用
Merge()与右兄弟合并,返回 - 右边有剩余:清空
header_set_,释放write_set_中父页祖先节点及根的锁,叶页调用LendFromBrother()向右兄弟借,父页调用SetKeyAt()更新父页中的右兄弟结点在父页的索引,然后清理write_set_
- 右兄弟不足(右兄弟大小 <= 最小容量):调用
- 左右兄弟都在(左叶页变量 && 右叶页变量):
-
是内部结点
强转为内部结点页,父页调用
FindInternelKey()二分查找内部结点获得index左边有值,有左兄弟(index > 0):取左兄弟(父页的index-1)的写锁存入
write_set_,强转为内部结点页后保存变量右边有值,有右兄弟(index < 父页容量-1):取右兄弟(父页的index+1)的写锁存入
write_set_,强转为内部结点后保存变量- 左右兄弟都在(左结点页变量 && 右结点页变量):
- 两个兄弟都不足(兄弟大小 <= 最小容量):调用
Merge()与右节点合并,返回 - 右边有剩余(右兄弟大小 > 最小容量):清空
header_page_,释放write_set_中父页的祖先节点及根的锁,内部结点页调用LendFromBrother()向右兄弟借,之后父页调用SetKeyAt()更新父页中的右兄弟结点在父页的索引,然后清理write_set_ - 左边有剩余(else):清空
header_page_,释放write_set_中父页的祖先节点及根的锁,叶页调用LendFromBrother()向左兄弟借,父页调用SetKeyAt()更新父页中的右兄弟结点在父页的索引,然后清理write_set_
- 两个兄弟都不足(兄弟大小 <= 最小容量):调用
- 只有左兄弟(左结点页变量):
- 左兄弟不足(左兄弟大小 <= 最小容量):调用
Merge()与左兄弟合并,返回 - 左边有剩余:清空
header_set_,释放write_set_中父页祖先节点及根的锁,叶页调用LendFromBrother()向左兄弟借,父页调用SetKeyAt()更新父页中的左兄弟结点在父页的索引,然后清理write_set_
- 左兄弟不足(左兄弟大小 <= 最小容量):调用
- 只有右兄弟(右结点页变量):
- 右兄弟不足(右兄弟大小 <= 最小容量):调用
Merge()与右兄弟合并,返回 - 右边有剩余:清空
header_set_,释放write_set_中父页祖先节点及根的锁,叶页调用LendFromBrother()向右兄弟借,父页调用SetKeyAt()更新父页中的右兄弟结点在父页的索引,然后清理write_set_
- 右兄弟不足(右兄弟大小 <= 最小容量):调用
- 左右兄弟都在(左结点页变量 && 右结点页变量):
-
BPLUSTREE_TYPE::Merge()总是把右边节点的键值对转移给左边,再删除右边节点,这样方便更新NextPageId,若传入的经删除操作的页是叶子节点,将该页和兄弟页强转为叶页后,判断是右兄弟,兄弟结点就调用BPlusTreeLeafPage::MoveAllTo()将自己的k-v顺序插入该页的末尾,将该页的NextPageId设置为右兄弟的,设置删除结点索引为右兄弟索引(该页索引+1)。若判断是左兄弟,该页调用BPlusTreeLeafPage::MoveAllTo()将自己的k-v顺序插入左兄弟的尾部,左兄弟的NextPageId设置为该页的,设置删除节点索引为该页索引。若进行删除操作的页是内部结点,强转该页和兄弟页为内部节点页,判断是右兄弟就把兄弟k-v插入该页末尾,设置删除索引为兄弟,反之同理。之后将write_set_中该页父页的孩子出栈解锁,只剩父页和其祖先节点,把父页强转为内部节点页,调用DeleteKeyAt()将删除结点索引指定的结点页删除。若父页大小大于等于最小容量(安全删除),清理write_set_后就可返回,若是根结点,内部根节点右两个键值对(容量大于1)就行,返回,其他情况就是不安全删除,调用DealNode()进行间接递归。
BPlusTreeLeafPage::LendFromBrother()从brother移动一个键值对过来,兄弟调用KeyAt()和ValueAt(),针对传入是否是左兄弟,将k-v插入结点的头或尾并设置兄弟的大小或删除k-v,最后返回新key。
Task3 叶子扫描的迭代器
必须添加一个c++迭代器,以有效地支持对叶页中的数据进行有序扫描。基本思想是存储兄弟指针,这样就可以有效地遍历叶页,然后实现一个迭代器,按顺序遍历每个叶页中的每个键值对。
Begin()和End()函数在b_plus_true.cpp中,而迭代器类在index_iterator.cpp中实现
//索引迭代器类的私有属性
BPlusTreeLeafPage<KeyType, ValueType, KeyComparator> *leaf_page_{nullptr}; // 此迭代器指向的叶子
int index_{-1}; // 表示此迭代器现在指向leaf_page_中某叶页的第几个键值对
page_id_t my_page_id_;//表示迭代器指向的leaf_page_中某叶页的页id
BufferPoolManager *bpm_{nullptr};
重载了* ++ == !=四个符号来对索引迭代器进行操作
BPLUSTREE_TYPE::Begin()有重载,分别对应处理没给key和给了key的情况。没给就直接调用FindLeafPage()传入空key并指定找最左边的k-v,这样就返回了叶页链表首页,然后强转为叶页,构造索引迭代器后返回。传入key的Begin()多加了一层检测传回的页是否真的有key,没有说明FindLeafPage()没有找到,返回空索引迭代器。
BPLUSTREE_TYPE::End()同无参Begin()只不过指定找的是最右边的k-v(即index=internal_page->GetSize()-1)
BPLUSTREE_TYPE::FindLeafPage()无锁获取根节点,根据传入的indextype判断需要的是最左/右的页还是进行k-v查找。
Task4 并行索引
见Task 2
相关文章:
P2 B+树索引
文章目录 Task1 B树页B树页B树内部结点B树叶子结点 Task2 B树操作Task2 B树插入和搜索的单一值插入单一值搜索单一值 Task2 B树删除 Task3 叶子扫描的迭代器Task4 并行索引 Task1 B树页 B树页 实际上是每个B树页面的标题部分,包含叶子页面和内部页面共享的信息。 …...
爬虫知识之BeautifulSoup库安装及简单介绍
一. 前言 在前面的几篇文章中我介绍了如何通过Python分析源代码来爬取博客、维基百科InfoBox和图片,其文章链接如下: 其中核心代码如下: # coding=utf-8 import urllib import re #下载静态HTML网页 url=http://www.csdn.net/ content = urllib.urlopen(url).read…...
如何有效取代FTP来帮助企业快速传输大文件
在互联网的发展历史上,FTP是一种具有里程碑意义的协议,它最早出现在1971年,是实现网络上文件传输的基础。FTP的优点是简单、稳定、兼容性强,可以在不同的操作系统和平台之间进行文件交换。然而,时代在进步,…...
免登陆积分商城原理
有客户需要免登陆积分商城,研究了一下发现免登陆用途广泛,实现原理也很简单。如果是浏览器无非就是使用fingerprintjs2之类的扩展来实现获取浏览器指纹ID,如果是APP就获取设备唯一标识,然后在使用cryptojs加密来传递到php…...
muduo源码学习base——Atomic(原子操作与原子整数)
Atomic(原子操作与原子整数) 前置知识AtomicIntegerTget()getAndAdd()getAndSet() 关于原子操作实现无锁队列(lock-free-queue) 前置知识 happens-before: 用来描述两个操作的内存可见性 如果操作 X happens-before 操作 Y,那么 X 的结果对于…...
最短路相关笔记
Floyd Floyd 算法,是一种在图中求任意两点间最短路径的算法。 Floyd 算法适用于求解无负边权回路的图。 时间复杂度为 O ( n 3 ) O(n^3) O(n3),空间复杂度 O ( n 2 ) O(n^2) O(n2)。 对于两点 ( i , j ) (i,j) (i,j) 之间的最短路径,有…...
Web前端-Vue2+Vue3基础入门到实战项目-Day5(自定义指令, 插槽, 案例商品列表, 路由入门)
自定义指令 基本使用 自定义指令: 自己定义的指令, 可以封装一些dom操作, 扩展额外功能全局注册// 1. 全局注册指令 Vue.directive(focus, {// inserted 会在 指令所在的元素, 被插入到页面中时触发inserted (el) {// el 就是指令所绑定的元素// console.log(el)el.focus()} …...
mysql json数据类型 相关函数
创建JSON文本的函数 1.JSON_ARRAY(转换json数组) 2.JSON_OBJECT(转换json对象) 3.JSON_QUOTE(转义字符串) 搜索JSON文本的函数 1.JSON_CONTAINS(json当中是否包含指定value) 2.J…...
如何实现前端实时通信(WebSocket、Socket.io等)?
聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...
使用 SSSD 进行网络用户身份验证
文章目录 使用 SSSD 进行网络用户身份验证SSSD和Active Directory先决条件、假设和要求软件安装加域 SSSD配置自动创建HOME目录 检查和验证 Kerberos票证已知的问题参考 使用 SSSD 进行网络用户身份验证 SSSD 代表系统安全服务守护程序,它实际上是处理来自各种网络…...
紫光展锐携中国联通完成RedCap芯片V517孵化测试
近日,紫光展锐携手中国联通5G物联网OPENLAB开放实验室(简称“OPENLAB实验室”)共同完成RedCap芯片V517创新孵化,并实现在联通5G全频段3.5GHz、2.1GHz、900MHz下的端到端业务验证测试。 V517是一款基于紫光展锐5G成熟平台设计与研发…...
算法通关村第十一关青铜挑战——移位运算详解
大家好,我是怒码少年小码。 计算机到底是怎么处理数字的? 数字在计算机中的表示 机器数 一个数在计算机中的二进制表示形式,叫做这个数的机器数。 机器数是带符号的,在计算机用一个数的最高位存放符号,正数为0&am…...
2023年面试测试工程师一般问什么问题?
面试和项目一起,是自学路上的两大拦路虎。面试测试工程师一般会被问什么问题,总结下来一般是下面这4类: 1.做好自我介绍 2.项目相关问题 3.技术相关问题 4.人事相关问题 接下来,主要从以上四个方向分别展开介绍。为了让大家更有获…...
2023年中国汽车覆盖件模具竞争格局、市场规模及行业需求前景[图]
汽车覆盖件模具是汽车车身生产的重要工艺装备,其设计和制造时间约占汽车开发周期的 2/3,是汽车换型的重要制约因素之一。汽车覆盖件模具具有尺寸大、工作型面复杂、技术标准高等特点,属于技术密集型产品。汽车覆盖件模具按以其冲压的汽车覆盖…...
vue3项目运行报错import zhCn from “element-plus/lib/locale/lang/zh-cn“
解决办法 import zhCn from "element-plus/lib/locale/lang/zh-cn";修改为 import zhCn from "element-plus/dist/locale/zh-cn.mjs";...
读书笔记:Effective C++ 2.0 版,条款26(歧义)、条款27(禁止部分隐式生成的函数)
条款26: 当心潜在的歧义 即使cpp支持潜在二义性/歧义,也不要使用。 void f(int); void f(char); double d 6.02; f(d); //需要明确转换多继承充满了潜在二义性/歧义的可能。 class Base1 {public: int doIt();}; class Base2 {public: void doIt();}; class Deri…...
MySQL基本操作之数据库设计理论
1、数据的设计准则 1)糟糕的数据库设计表现在以下几个方面: 访问数据效率低下存在大量的数据冗余,浪费存储空间更新和检索数据时会出现许多问题2)良好的数据库设计表现在以下几方面: 访问效率高减少数据冗余,节省存储空间便于进一步扩展可以使应用程序的开发变得更容易…...
SpringBoot的日志系统(日志分组、文件输出、滚动归档)
[toc](目录) > SpringBoot3需要jdk17 # 1. 简介 1. Spring5及以后Spring自己实现了commons-logging,来作为内部的日志。日志的jar包是org.springframework:spring-jcl:6.0.10。查看org.apache.commons.logging.LogAdapter Java package org.apache.commons.log…...
一种基于HTTPS实现的Web账号登录Linux桌面系统的实现方案
问题由来 客户需求计划列入支持第三方帐号系统,包括Web账号。需求来源是用户想要用它们的帐号直接登录Linux Deepin操作系统。一个失败的实现方案是用户以较小的成本改造帐号管理系统发布HTTP服务,我们开发一个PAM模块与Web服务器交互,数据格…...
【Linux】psplash制作Linux开机动画
1. 下载psplash软件 下载psplash源码到ubuntu中: 下载地址:https://git.yoctoproject.org/psplash/commit/安装依赖环境 sudo apt-get install libgdk-pixbuf2.0-dev2. 准备图片 开机动画静态图片:psplash-poky.png开机动画进度条图片&…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...
从零开始了解数据采集(二十八)——制造业数字孪生
近年来,我国的工业领域正经历一场前所未有的数字化变革,从“双碳目标”到工业互联网平台的推广,国家政策和市场需求共同推动了制造业的升级。在这场变革中,数字孪生技术成为备受关注的关键工具,它不仅让企业“看见”设…...
CppCon 2015 学习:REFLECTION TECHNIQUES IN C++
关于 Reflection(反射) 这个概念,总结一下: Reflection(反射)是什么? 反射是对类型的自我检查能力(Introspection) 可以查看类的成员变量、成员函数等信息。反射允许枚…...
Linux操作系统共享Windows操作系统的文件
目录 一、共享文件 二、挂载 一、共享文件 点击虚拟机选项-设置 点击选项,设置文件夹共享为总是启用,点击添加,可添加需要共享的文件夹 查询是否共享成功 ls /mnt/hgfs 如果显示Download(这是我共享的文件夹)&…...
Redis上篇--知识点总结
Redis上篇–解析 本文大部分知识整理自网上,在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库,Redis 的键值对中的 key 就是字符串对象,而 val…...
基于 HTTP 的单向流式通信协议SSE详解
SSE(Server-Sent Events)详解 🧠 什么是 SSE? SSE(Server-Sent Events) 是 HTML5 标准中定义的一种通信机制,它允许服务器主动将事件推送给客户端(浏览器)。与传统的 H…...
代理服务器-LVS的3种模式与调度算法
作者介绍:简历上没有一个精通的运维工程师。请点击上方的蓝色《运维小路》关注我,下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 我们上一章介绍了Web服务器,其中以Nginx为主,本章我们来讲解几个代理软件:…...
视觉slam--框架
视觉里程计的框架 传感器 VO--front end VO的缺点 后端--back end 后端对什么数据进行优化 利用什么数据进行优化的 后端是怎么进行优化的 回环检测 建图 建图是指构建地图的过程。 构建的地图是点云地图还是什么信息的地图? 建图并没有一个固定的形式和算法…...
