当前位置: 首页 > news >正文

redis7.x源码分析:(3) dict字典

dict字典采用经典hash表数据结构实现,由键值对组成,类似于C++中的unordered_map。两者在代码实现层面存在一些差异,比如gnustl的unordered_map分配的桶数组个数是(质数n),而dict分配的桶数组个数是(2^n);另外,dict对hash值相同的key采用了常规的开链法存储,而unordered_map在采用开链法的前提下,又使用了_M_before_begin将不同桶中的链表串联成了一个大链表,从而将遍历速度优化为O(n);还有就是,dict为应对服务器性能上的特殊要求,设计成了双hash表的形式,这也使得它的rehash等操作存在一些特殊性。

dict在redis里面的用途十分广泛,几乎所有的模块都会用到,其中的两大核心用途是:
 1. 16个数据库空间
 2. hash和zset类型数据的存储

dict相关结构定义:

// 节点
typedef struct dictEntry {// 任意类型键void *key;// 存储的值union {void *val;uint64_t u64;int64_t s64;double d;} v;// 同一个桶中链表的下一个元素struct dictEntry *next;     /* Next entry in the same hash bucket. */void *metadata[];           /* An arbitrary number of bytes (starting at a* pointer-aligned address) of size as returned* by dictType's dictEntryMetadataBytes(). */
} dictEntry;typedef struct dict dict;// 存储不同类型数据的字典,设置不同的处理函数
typedef struct dictType {uint64_t (*hashFunction)(const void *key);void *(*keyDup)(dict *d, const void *key);void *(*valDup)(dict *d, const void *obj);int (*keyCompare)(dict *d, const void *key1, const void *key2);void (*keyDestructor)(dict *d, void *key);void (*valDestructor)(dict *d, void *obj);int (*expandAllowed)(size_t moreMem, double usedRatio);/* Allow a dictEntry to carry extra caller-defined metadata.  The* extra memory is initialized to 0 when a dictEntry is allocated. */size_t (*dictEntryMetadataBytes)(dict *d);
} dictType;#define DICTHT_SIZE(exp) ((exp) == -1 ? 0 : (unsigned long)1<<(exp))
#define DICTHT_SIZE_MASK(exp) ((exp) == -1 ? 0 : (DICTHT_SIZE(exp))-1)struct dict {// 字典类型,不同的类型有不同的hash函数,dup函数dictType *type;// 双哈希表指针数组dictEntry **ht_table[2];// 存放的节点数unsigned long ht_used[2];// rehash索引(哈希表的下标),大于 -1 表示正在rehashlong rehashidx; /* rehashing not in progress if rehashidx == -1 *//* Keep small vars at end for optimal (minimal) struct padding */// rehash暂停标志int16_t pauserehash; /* If >0 rehashing is paused (<0 indicates coding error) */// 表示2的多少次幂,哈希表的大小=2^ht_size_expsigned char ht_size_exp[2]; /* exponent of size. (size = 1<<exp) */
};

hash表的创建比较简单直接略过,先看下 _dictExpand 的实现,它在hash表扩容缩容和创建时都会用到。当添加dictAdd时,存储的节点数 used / size >= 1,就需要调用它扩容。

int _dictExpand(dict *d, unsigned long size, int* malloc_failed)
{if (malloc_failed) *malloc_failed = 0;/* the size is invalid if it is smaller than the number of* elements already inside the hash table */// 正在rehash或者 used / size > 1直接退出if (dictIsRehashing(d) || d->ht_used[0] > size)return DICT_ERR;/* the new hash table */dictEntry **new_ht_table;unsigned long new_ht_used;// 获取第一个 2^N > size 的N的大小signed char new_ht_size_exp = _dictNextExp(size);/* Detect overflows */// 2^N 作为hash数组的长度, 另外判断分配的大小是否合法size_t newsize = 1ul<<new_ht_size_exp;if (newsize < size || newsize * sizeof(dictEntry*) < newsize)return DICT_ERR;/* Rehashing to the same table size is not useful. */if (new_ht_size_exp == d->ht_size_exp[0]) return DICT_ERR;/* Allocate the new hash table and initialize all pointers to NULL */if (malloc_failed) {new_ht_table = ztrycalloc(newsize*sizeof(dictEntry*));*malloc_failed = new_ht_table == NULL;if (*malloc_failed)return DICT_ERR;} elsenew_ht_table = zcalloc(newsize*sizeof(dictEntry*));new_ht_used = 0;/* Is this the first initialization? If so it's not really a rehashing* we just set the first hash table so that it can accept keys. */// 如果是第一次创建hash表,则设置完第一个表后直接退出if (d->ht_table[0] == NULL) {d->ht_size_exp[0] = new_ht_size_exp;d->ht_used[0] = new_ht_used;d->ht_table[0] = new_ht_table;return DICT_OK;}/* Prepare a second hash table for incremental rehashing */// 设置第二个表后退出,并且开始rehashd->ht_size_exp[1] = new_ht_size_exp;d->ht_used[1] = new_ht_used;d->ht_table[1] = new_ht_table;d->rehashidx = 0;return DICT_OK;
}
// 检查是否需要缩容
int htNeedsResize(dict *dict) {long long size, used;// 哈希表大小size = dictSlots(dict);// 哈希表已用节点数量used = dictSize(dict);// 当哈希表的大小大于 > 4 并且用量小于 10%时缩容return (size && used && size > DICT_HT_INITIAL_SIZE &&(used*100/size < REDIS_HT_MINFILL));
}

在执行databasesCron时,如果数据库满足 htNeedsResize 会进行缩容,另外hash和zset类型数据在执行删除操作时,也会判断是否需要缩容。

扩容缩容都会触发开启rehash,最终调用 dictRehash 实现rehash的动作,以下是它的代码:

int dictRehash(dict *d, int n) {// 累计访问多少个空桶后退出rehashint empty_visits = n*10; /* Max number of empty buckets to visit. */if (!dictIsRehashing(d)) return 0;// 进行n次rehashwhile(n-- && d->ht_used[0] != 0) {dictEntry *de, *nextde;/* Note that rehashidx can't overflow as we are sure there are more* elements because ht[0].used != 0 */assert(DICTHT_SIZE(d->ht_size_exp[0]) > (unsigned long)d->rehashidx);while(d->ht_table[0][d->rehashidx] == NULL) {// 遇到空桶, 索引后移d->rehashidx++;if (--empty_visits == 0) return 1;}// 得到桶中链表第一个节点de = d->ht_table[0][d->rehashidx];/* Move all the keys in this bucket from the old to the new hash HT */// 遍历链表上所有的节点, 添加到hash表2中while(de) {uint64_t h;nextde = de->next;/* Get the index in the new hash table */// 计算hash值对应的索引 = hash & (2^exp - 1)h = dictHashKey(d, de->key) & DICTHT_SIZE_MASK(d->ht_size_exp[1]);// 添加到hash表2中de->next = d->ht_table[1][h];d->ht_table[1][h] = de;d->ht_used[0]--;d->ht_used[1]++;de = nextde;}// 清空hash表1的桶d->ht_table[0][d->rehashidx] = NULL;d->rehashidx++;}/* Check if we already rehashed the whole table... */// 如果rehash全部完成了, 则用表2替换表1, 并且释放原来的表1if (d->ht_used[0] == 0) {zfree(d->ht_table[0]);/* Copy the new ht onto the old one */d->ht_table[0] = d->ht_table[1];d->ht_used[0] = d->ht_used[1];d->ht_size_exp[0] = d->ht_size_exp[1];_dictReset(d, 1);d->rehashidx = -1;return 0;}/* More to rehash... */return 1;
}// 执行多少毫秒的rehash操作
int dictRehashMilliseconds(dict *d, int ms) {if (d->pauserehash > 0) return 0;long long start = timeInMilliseconds();int rehashes = 0;// 还没超时的情况下,一次尝试执行100个桶的rehashwhile(dictRehash(d,100)) {rehashes += 100;if (timeInMilliseconds()-start > ms) break;}return rehashes;
}

再分别看一下 dictAddRaw、dictGenericDelete、dictFind的代码:

/* Low level add or find:* This function adds the entry but instead of setting a value returns the* dictEntry structure to the user, that will make sure to fill the value* field as they wish.** This function is also directly exposed to the user API to be called* mainly in order to store non-pointers inside the hash value, example:** entry = dictAddRaw(dict,mykey,NULL);* if (entry != NULL) dictSetSignedIntegerVal(entry,1000);** Return values:** If key already exists NULL is returned, and "*existing" is populated* with the existing entry if existing is not NULL.** If key was added, the hash entry is returned to be manipulated by the caller.*/
dictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing)
{long index;dictEntry *entry;int htidx;// 正在rehash过程中,则执行一次rehash操作(只把老表中一个桶对应链表内的数据节点重新hash到新表的不同桶中)if (dictIsRehashing(d)) _dictRehashStep(d);/* Get the index of the new element, or -1 if* the element already exists. */// 获取新节点用的数组索引if ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == -1)return NULL;/* Allocate the memory and store the new entry.* Insert the element in top, with the assumption that in a database* system it is more likely that recently added entries are accessed* more frequently. */// 如果在rehash的话,新节点只会加到第二个哈希表中htidx = dictIsRehashing(d) ? 1 : 0;size_t metasize = dictMetadataSize(d);entry = zmalloc(sizeof(*entry) + metasize);if (metasize > 0) {memset(dictMetadata(entry), 0, metasize);}// 新节点添加到链表头entry->next = d->ht_table[htidx][index];d->ht_table[htidx][index] = entry;d->ht_used[htidx]++;/* Set the hash entry fields. */// 设置新节点的key, 如果设置了 type->KeyDup 函数, 则复制出一个key的副本保存到节点中dictSetKey(d, entry, key);return entry;
}/* Search and remove an element. This is a helper function for* dictDelete() and dictUnlink(), please check the top comment* of those functions. */
static dictEntry *dictGenericDelete(dict *d, const void *key, int nofree) {uint64_t h, idx;dictEntry *he, *prevHe;int table;/* dict is empty */if (dictSize(d) == 0) return NULL;// 正在rehash过程中,则执行一次rehash操作if (dictIsRehashing(d)) _dictRehashStep(d);h = dictHashKey(d, key);// 在hash表1和2中查找for (table = 0; table <= 1; table++) {// 计算索引获取桶中链表节点idx = h & DICTHT_SIZE_MASK(d->ht_size_exp[table]);he = d->ht_table[table][idx];prevHe = NULL;while(he) {// 遍历链表找到对应key的节点并删除if (key==he->key || dictCompareKeys(d, key, he->key)) {/* Unlink the element from the list */if (prevHe)prevHe->next = he->next;elsed->ht_table[table][idx] = he->next;if (!nofree) {// 释放节点中key/valuedictFreeUnlinkedEntry(d, he);}d->ht_used[table]--;return he;}prevHe = he;he = he->next;}if (!dictIsRehashing(d)) break;}return NULL; /* not found */
}dictEntry *dictFind(dict *d, const void *key)
{dictEntry *he;uint64_t h, idx, table;if (dictSize(d) == 0) return NULL; /* dict is empty */// 执行一次rehashif (dictIsRehashing(d)) _dictRehashStep(d);h = dictHashKey(d, key);// 在hash表1和2中查找for (table = 0; table <= 1; table++) {idx = h & DICTHT_SIZE_MASK(d->ht_size_exp[table]);he = d->ht_table[table][idx];while(he) {if (key==he->key || dictCompareKeys(d, key, he->key))return he;he = he->next;}if (!dictIsRehashing(d)) return NULL;}return NULL;
}

dict的rehash并不是一次完成的,这个是基于性能、响应时间上的考虑。会执行rehash有以下几个函数:

  • databasesCron函数,它会定时执行,对16个db中的dict和expires进行rehash,每次调用dictRehashMilliseconds执行1ms时间
  • dictAddRaw、dictGenericDelete、dictFind、dictGetRandomKey、dictGetSomeKeys函数,它们每次rehash只处理一个桶

最后看下迭代器的定义:

/* If safe is set to 1 this is a safe iterator, that means, you can call* dictAdd, dictFind, and other functions against the dictionary even while* iterating. Otherwise it is a non safe iterator, and only dictNext()* should be called while iterating. */
typedef struct dictIterator {// 迭代器对应的字典dict *d;// 正在迭代的hash索引long index;// table表示迭代的表是两张表中的哪一张;safe表示是否是安全迭代器int table, safe;// entry表示迭代器当前的节点;nextEntry表示下个节点dictEntry *entry, *nextEntry;/* unsafe iterator fingerprint for misuse detection. */// 指纹,判断迭代过程中有没执行被禁止的操作(dictNext以外的函数)unsigned long long fingerprint;
} dictIterator;

迭代器分为安全和不安全迭代器:

  • 安全迭代器,针对字典可以执行 dictAdd 等有可能修改的函数
  • 不安全迭代器,针对字典只能执行 dictNext

迭代的实现:

dictEntry *dictNext(dictIterator *iter)
{while (1) {if (iter->entry == NULL) {// 第一个迭代或者桶中的链表遍历完了if (iter->index == -1 && iter->table == 0) {// 安全模式暂停rehashif (iter->safe)dictPauseRehashing(iter->d);elseiter->fingerprint = dictFingerprint(iter->d);}// 依次轮询两张hash表,查找非空节点iter->index++;if (iter->index >= (long) DICTHT_SIZE(iter->d->ht_size_exp[iter->table])) {if (dictIsRehashing(iter->d) && iter->table == 0) {iter->table++;iter->index = 0;} else {break;}}iter->entry = iter->d->ht_table[iter->table][iter->index];} else {// 取当前节点的下一个节点iter->entry = iter->nextEntry;}if (iter->entry) {// 如果不为空,返回当前节点,保存下个节点/* We need to save the 'next' here, the iterator user* may delete the entry we are returning. */iter->nextEntry = iter->entry->next;return iter->entry;}}return NULL;
}

相关文章:

redis7.x源码分析:(3) dict字典

dict字典采用经典hash表数据结构实现&#xff0c;由键值对组成&#xff0c;类似于C中的unordered_map。两者在代码实现层面存在一些差异&#xff0c;比如gnustl的unordered_map分配的桶数组个数是&#xff08;质数n&#xff09;&#xff0c;而dict分配的桶数组个数是&#xff0…...

连续九届EI稳定|江苏科技大学主办

【九届EI检索稳定|江苏科技大学主办 | IEEE出版 】 &#x1f388;【截稿倒计时】&#xff01;&#xff01;&#xff01; ✨徐秘书&#xff1a;gsra_huang ✨往届均已检索&#xff0c;已上线IEEE官网 &#x1f38a;第九届清洁能源与发电技术国际学术会议&#xff08;CEPGT 2…...

HarmonyOS NEXT应用开发实战 ( 应用的签名、打包上架,各种证书详解)

前言 没经历过的童鞋&#xff0c;首次对HarmonyOS的应用签名打包上架可能感觉繁琐。需要各种秘钥证书生成和申请&#xff0c;混在一起也分不清。其实搞清楚后也就那会事&#xff0c;各个文件都有它存在的作用。 HarmonyOS通过数字证书与Profile文件等签名信息来保证鸿蒙应用/…...

【CICD】CICD 持续集成与持续交付在测试中的应用

一、什么是CICD&#xff1f; CI/CD 是指持续集成&#xff08;Continuous Integration&#xff09;和持续部署&#xff08;Continuous Deployment&#xff09;或持续交付&#xff08;Continuous Delivery&#xff09; 1.1 持续集成&#xff08;Continuous Integration&#xf…...

Dolby TrueHD和Dolby Digital Plus (E-AC-3)编码介绍

文章目录 1. Dolby TrueHD特点总结 2. Dolby Digital Plus (E-AC-3)特点总结 Dolby TrueHD 与 Dolby Digital Plus (E-AC-3) 的对比 Dolby TrueHD和Dolby Digital Plus (E-AC-3) 是两种高级的杜比音频编码格式&#xff0c;常用于蓝光影碟、流媒体、影院等高品质音频传输场景。它…...

数字频率计的设计-- 基于 HDL 方法

目录 数字频率计的设计 1.计数、锁存与显示译码电路设计 2.主控电路设计 3.分频电路设计 4.顶层电路设计 伪随机序列发生器 的设计 数字频率计的设计 基于HDL设计数字系统时&#xff0c;可以根据需要应用Verilog HDL描述所需要的功能电路&#xff0c;既有利于节约资源&am…...

[程序员] 没有产生core文件的原因

最近和同事一块看一个core文件没有产生的问题,总结了一些在CSDN的专栏里。分析的过程,参考使用了ftrace的功能,感觉非常实用。 如果有需要可以参考。大体上就这么几种情况:信号的特殊处理,coredump相关的配置没有设置正确,文件系统访问权限问题,setuid相关的不匹配问题。…...

【数字图像处理+MATLAB】基于 Sobel 算子计算图像梯度并进行边缘增强:使用 imgradientxy 函数

引言 在图像处理中&#xff0c;边缘通常是图像中像素强度变化最大的地方&#xff0c;这种变化可以通过计算图像的梯度来量化。梯度是一个向量&#xff0c;它的方向指向像素强度增加最快的方向&#xff0c;它的大小&#xff08;或者说幅度&#xff09;表示像素强度增加的速度。…...

P10901 [蓝桥杯 2024 省 C] 封闭图形个数

铁子们好呀&#xff0c;今天博主给大家更新一道编程题&#xff01;&#xff01;&#xff01; 题目链接如下&#xff1a; P10901 [蓝桥杯 2024 省 C] 封闭图形个数 好&#xff0c;接下来&#xff0c;我将从三个方面讲解这道例题。分别是 题目解析算法原理代码实现 文章目录 1.题…...

ubuntu-desktop-24.04上手指南(更新阿里源、安装ssh、安装chrome、设置固定IP、安装搜狗输入法)

ubuntu-desktop-24.04上手指南(更新阿里源、安装ssh、安装chrome、设置固定IP、安装搜狗输入法) 一、更新并安装基础软件 #切换root用户 sudo su -#更新 apt update #升级 apt upgrade#install vim apt install vim#install net-tools apt install net-tools二、安装ssh并设置…...

手机直连卫星NTN通信初步研究

目录 1、手机直连卫星之序幕 2、卫星NTN及其网络架构 2.1 NTN 2.2 NTN网络架构 3、NTN的3GPP标准化进程 3.1 NTN需要适应的特性 3.2 NTN频段 3.3 NTN的3GPP标准化进程概况 3.4 NTN的3GPP标准化进程的详情 3.4.1 NR-NTN 3.4.1.1 NTN 的无线相关 SI/WI 3.4.1.2…...

蓝桥杯c++算法学习【2】之搜索与查找(九宫格、穿越雷区、迷宫与陷阱、扫地机器人:::非常典型的必刷例题!!!)

别忘了请点个赞收藏关注支持一下博主喵&#xff01;&#xff01;&#xff01; 关注博主&#xff0c;更多蓝桥杯nice题目静待更新:) 搜索与查找 一、九宫格 【问题描述】 小明最近在教邻居家的小朋友小学奥数&#xff0c;而最近正好讲述到了三阶幻方这个部分&#xff0c;三 …...

Android加载pdf

依赖 implementation com.squareup.okhttp3:okhttp:4.9.1 implementation com.github.barteksc:android-pdf-viewer:3.2.0-beta.1在project.build中添加该源 maven { url "https://repository.liferay.com/nexus/content/repositories/public/" }XML <LinearLa…...

IOT物联网低代码可视化大屏解决方案汇总

目录 参考来源云服务商阿里云物联网平台产品主页产品文档 开源项目DGIOT | 轻量级工业物联网开源平台项目特点项目地址开源许可 IoTGateway | 基于.NET6的跨平台工业物联网网关项目特点项目地址开源许可 IoTSharp | 基于.Net Core开源的物联网基础平台项目特点项目地址开源许可…...

Python的面向对象day7

1、什么是面向对象 面向对象称为OO&#xff0c;他通过将数据和功能封装在一个被称为‘对象’的实体中&#xff0c;来组织和管理代码。面向对象变成&#xff08;OOP&#xff09;具有四个特性&#xff0c;封装、继承、多态、抽象 优点&#xff1a;模块化、安全性高、代码重用性…...

计算机网络(11)和流量控制补充

这一篇对数据链路层中的和流量控制进行详细学习 流量控制&#xff08;Flow Control&#xff09;是计算机网络中确保数据流平稳传输的技术&#xff0c;旨在防止数据发送方发送过多数据&#xff0c;导致接收方的缓冲区溢出&#xff0c;进而造成数据丢失或传输失败。流量控制通常…...

Rust 所有权机制

Rust 所有权机制 本文示例代码地址 所有权是Rust中最独特的特性&#xff0c;它让Rust无需GC就可以保证内存安全。 什么是所有权&#xff1f; 所有权&#xff08;ownership&#xff09;是 Rust 用于如何管理内存的一组规则。所有程序都必须管理其运行时使用计算机内存的方式…...

Pwn VM writeup

国赛期间&#xff0c;做了一个很有意思的pwn题&#xff0c;顺便学了一下现在常见的pwn的板子题是什么样子的&#xff0c;这里做一下记录 Magic VM 题目逻辑 题目本身其实非常的有趣&#xff0c;它实现了一个简易流水线的功能&#xff0c;程序中包含四个结构体&#xff0c;其中三…...

LSTM(长短期记忆网络)详解

1️⃣ LSTM介绍 标准的RNN存在梯度消失和梯度爆炸问题&#xff0c;无法捕捉长期依赖关系。那么如何理解这个长期依赖关系呢&#xff1f; 例如&#xff0c;有一个语言模型基于先前的词来预测下一个词&#xff0c;我们有一句话 “the clouds are in the sky”&#xff0c;基于&…...

机器学习 贝叶斯公式

这是条件概率的计算公式 &#x1d443;(&#x1d434;|&#x1d435;)&#x1d443;(B|A)&#x1d443;(&#x1d434;)/&#x1d443;(&#x1d435;) 全概率公式 &#x1d443;(&#x1d435;)&#x1d443;(&#x1d435;|&#x1d434;)&#x1d443;(&#x1d434;)&am…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

宇树科技,改名了!

提到国内具身智能和机器人领域的代表企业&#xff0c;那宇树科技&#xff08;Unitree&#xff09;必须名列其榜。 最近&#xff0c;宇树科技的一项新变动消息在业界引发了不少关注和讨论&#xff0c;即&#xff1a; 宇树向其合作伙伴发布了一封公司名称变更函称&#xff0c;因…...

消息队列系统设计与实践全解析

文章目录 &#x1f680; 消息队列系统设计与实践全解析&#x1f50d; 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡&#x1f4a1; 权衡决策框架 1.3 运维复杂度评估&#x1f527; 运维成本降低策略 &#x1f3d7;️ 二、典型架构设计2.1 分布式事务最终一致…...

微服务通信安全:深入解析mTLS的原理与实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言&#xff1a;微服务时代的通信安全挑战 随着云原生和微服务架构的普及&#xff0c;服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...

比特币:固若金汤的数字堡垒与它的四道防线

第一道防线&#xff1a;机密信函——无法破解的哈希加密 将每一笔比特币交易比作一封在堡垒内部传递的机密信函。 解释“哈希”&#xff08;Hashing&#xff09;就是一种军事级的加密术&#xff08;SHA-256&#xff09;&#xff0c;能将信函内容&#xff08;交易细节&#xf…...

Oracle实用参考(13)——Oracle for Linux物理DG环境搭建(2)

13.2. Oracle for Linux物理DG环境搭建 Oracle 数据库的DataGuard技术方案,业界也称为DG,其在数据库高可用、容灾及负载分离等方面,都有着非常广泛的应用,对此,前面相关章节已做过较为详尽的讲解,此处不再赘述。 需要说明的是, DG方案又分为物理DG和逻辑DG,两者的搭建…...