数据结构和一致性哈希复习
41
C++/面试/24 C内存分配与初始化.md
Normal file
@@ -0,0 +1,41 @@
|
||||
## 1 alloc
|
||||
alloc是向栈申请内存,因此无需释放。
|
||||
|
||||
* 功能:返回一个指向n个连续字符存储单元的指针。利用afree§释放已经分配的存储空间,以便以后使用。
|
||||
|
||||
* 函数原型: char *alloc(int n);
|
||||
|
||||
## 2 malloc(memeory allocation,动态内存分配)
|
||||
malloc分配的内存是位于堆中的,并且没有初始化内存的内容,因此基本上malloc之后,调用函数memset来初始化这部分的内存空间。
|
||||
* **功能** 用于申请一块连续的指定大小的内存块区域以void*类型返回分配的内存区域地址,当无法知道内存具体位置的时候,想要绑定真正的内存空间,就需要用到动态的分配内存,且分配的大小就是程序要求的大小。
|
||||
* 函数原型: void *malloc(unsigned int size)
|
||||
### 工作机制
|
||||
|
||||
* malloc函数的实质体现在,他有一个将可用的内存块连接为一个常常的链表的所谓空闲链表。调用malloc函数是,他沿着连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到连接表上。调用free函数时,他将用户释放的内存块连接到空闲链表上。到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上,检查各个内存片段,对他们进行整理,将相邻的小空闲块合并成较大的内存块。如果无法获得符合要求的内存块,malloc函数会返回NULL指针,因此在调用malloc动态申请内存块时,一定要进行返回值的判断。
|
||||
|
||||
* linux Libc6采用的机制是在free的时候试图整合相邻的碎片,使其合并称为一个较大的free空间。
|
||||
|
||||
注意总结:
|
||||
1. 使用malloc一定要判断返回值是否为NULL。
|
||||
2. 使用malloc要用memset初始化。
|
||||
|
||||
|
||||
## 3 calloc
|
||||
calloc则将初始化这部分的内存设置为0。
|
||||
* 函数原型:void *calloc(unsigned int num,unsigned int size);
|
||||
* 功能:在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。num对象个数,size:对象占据的内存字节数,相较于malloc函数,calloc函数会自动将内存初始化为0。而malloc不做初始化,分配到空间中的数据是随机数据。
|
||||
|
||||
## 4 realloc
|
||||
realloc对申请的内存进行大小的调整,申请的内存最终需要通过函数free来释放。
|
||||
* 函数原型:extern void *realloc(void *mem_address,unsigned int newsize);
|
||||
* 功能:先判断当前的指针是够有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意L原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。
|
||||
|
||||
|
||||
### 注意事项
|
||||
1. 头文件:#include <stdlib.h> 有些编译器需要 #include <malloc.h>
|
||||
2. 原先的地址如果放弃使用是会被自动释放的。不要手动的free否则会造成内存泄露。
|
||||
3. newsize小于原先的大小的时候,可能会造成数据丢失。
|
||||
4. mem_address一定要是malloc,calloc或realloc申请得到指针。
|
||||
5. 如果newsize大小为0,那么释放mem_address指向的内存,并返回NULL。这里需要注意的是只对指针本身进行释放,例如对二维指针**a,对a调用realloc时只会释放一维,使用时谨防内存泄漏。
|
||||
6. 如果没有足够可用的内存用来完成重新分配(扩大原来的内存块或者分配新的内存块),则返回NULL。而原来的内存块保持不变。
|
||||
7. 传递给realloc的指针可以为空,效果等同于malloc。
|
||||
@@ -13,19 +13,21 @@
|
||||
* [x] 标准库
|
||||
* [x] 面向对象
|
||||
* [x] 设计模式
|
||||
* [ ] 并行编程
|
||||
* [ ] 并发编程
|
||||
* [ ] 网络编程
|
||||
* [ ] GO
|
||||
* [x] 重新整理go知识(在原先的基础上进行了扩充)
|
||||
* [ ] 基础知识。
|
||||
* [ ] go语言的优势。
|
||||
* [ ] 协程并发编程等,相关内容的了解。
|
||||
* [ ] 数据结构
|
||||
* [ ] 算法
|
||||
* [x] 数据结构
|
||||
* [x] 算法
|
||||
### 知识复习——基础
|
||||
* [ ] 操作系统
|
||||
* [ ] 基础知识
|
||||
* [ ] 关于同步异步机制的理解。总结各种语言、库、中间件、设计模式、数据库、操作系统的同步异步方式。
|
||||
* [ ] Linux底层原理和常见函数。为什么说一切皆文件
|
||||
* [x] 操作系统
|
||||
* [x] 基础知识
|
||||
* [x] 关于同步异步机制的理解。总结各种语言、库、中间件、设计模式、数据库、操作系统的同步异步方式。
|
||||
* [x] Linux底层原理和常见函数。为什么说一切皆文件
|
||||
* [ ] 面试内容复习
|
||||
* [x] 数据库
|
||||
* [x] 基础知识
|
||||
* [x] MySQL
|
||||
@@ -35,19 +37,18 @@
|
||||
* [x] 消息队列定义
|
||||
* [x] kafka消息队列
|
||||
* [x] redis实现消息队列
|
||||
* [ ] 计算机网络
|
||||
* [ ] 应用层
|
||||
* [ ] 网络层
|
||||
* [ ] 通信层
|
||||
* [ ] 链路层
|
||||
* [ ] 物理层
|
||||
* [ ] 面试内容复习
|
||||
* [x] 计算机网络
|
||||
* [x] 应用层
|
||||
* [x] 网络层
|
||||
* [x] 通信层
|
||||
* [x] 链路层
|
||||
* [x] 物理层
|
||||
* [ ] 面试内容复习
|
||||
### 知识复习——框架
|
||||
* [ ] C++/GO框架
|
||||
* [ ] 微服务框架
|
||||
* [ ] docker/k8s/kite
|
||||
* [ ] Django MVC框架
|
||||
* [ ] Go kitex微服务框架
|
||||
### 编程实战
|
||||
* [ ] C++/Go网络编程(总结自己的网络编程经验。自己的grpc和kite/thrift框架的应用经验。关于异步通信的一些实现和总结。)
|
||||
* [ ] web开发(总结自己的web开发经验)
|
||||
* [ ] Linux系统编程
|
||||
### 力扣刷题
|
||||
* [ ] 算法编程(力扣刷题)
|
||||
|
||||
@@ -172,7 +172,7 @@ malloc大于128k的内存,使用mmap分配内存,在堆和栈之间找一块
|
||||
|
||||
### malloc的实现方案
|
||||
1. malloc 函数的实质是它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。
|
||||
2. 调用 malloc()函数时,它沿着连接表寻找一个大到足以满足用户请求所需要的内存块。 然后,将该内存块一分为二(一块的大小与用户申请的大小相等,另一块的大小就是剩下来的字节)。 接下来,将分配给用户的那块内存存储区域传给用户,并将剩下的那块(如果有的话)返回到连接表上。
|
||||
2. 调用 malloc()函数时,它沿着连接表寻找一个大到足以满足用户请求所需要的内存块。 然后,将该内存块一分为二(一块的大小与用户申请的大小相等,另一块的大小就是剩下来的字节)。 接下来,将分配给用户的那块内存存储区域传给用户,并将剩下的那块(如果有的话)返回到连接表上。
|
||||
3. 调用 free 函数时,它将用户释放的内存块连接到空闲链表上。
|
||||
4. 到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段, 那么空闲链表上可能没有可以满足用户要求的片段了。于是,malloc()函数请求延时,并开始在空闲链表上检查各内存片段,对它们进行内存整理,将相邻的小空闲块合并成较大的内存块。
|
||||
|
||||
|
||||
BIN
数据库/MySQL/image/2021-09-09-15-21-22.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
数据库/MySQL/image/2021-09-09-15-22-11.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
数据库/MySQL/image/2021-09-09-15-23-14.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
数据库/MySQL/image/2021-09-09-15-24-05.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
数据库/MySQL/image/2021-09-09-15-24-47.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
数据库/MySQL/image/2021-09-09-15-24-57.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
数据库/MySQL/image/2021-09-09-15-25-26.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
数据库/MySQL/image/2021-09-09-15-26-10.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
88
数据库/MySQL/附录8 MySQL一致哈希.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# 一致性哈希算法
|
||||
|
||||
|
||||
|
||||
## 一致性Hash算法背景
|
||||
|
||||
一致性哈希算法在1997年由麻省理工学院的Karger等人在解决分布式Cache中提出的,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似。一致性哈希修正了CARP使用的简单哈希算法带来的问题,使得DHT可以在P2P环境中真正得到应用。
|
||||
|
||||
但现在一致性hash算法在分布式系统中也得到了广泛应用,研究过memcached缓存数据库的人都知道,memcached服务器端本身不提供分布式cache的一致性,而是由客户端来提供,具体在计算一致性hash时采用如下步骤:
|
||||
|
||||
首先求出memcached服务器(节点)的哈希值,并将其配置到0~232的圆(continuum)上。
|
||||
然后采用同样的方法求出存储数据的键的哈希值,并映射到相同的圆上。
|
||||
然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。如果超过232仍然找不到服务器,就会保存到第一台memcached服务器上。
|
||||
|
||||

|
||||
|
||||
|
||||
从上图的状态中添加一台memcached服务器。余数分布式算法由于保存键的服务器会发生巨大变化而影响缓存的命中率,但Consistent Hashing中,只有在园(continuum)上增加服务器的地点逆时针方向的第一台服务器上的键会受到影响,如下图所示
|
||||
|
||||

|
||||
|
||||
|
||||
## 2 一致性Hash性质
|
||||
考虑到分布式系统每个节点都有可能失效,并且新的节点很可能动态的增加进来,如何保证当系统的节点数目发生变化时仍然能够对外提供良好的服务,这是值得考虑的,尤其实在设计分布式缓存系统时,如果某台服务器失效,对于整个系统来说如果不采用合适的算法来保证一致性,那么缓存于系统中的所有数据都可能会失效(即由于系统节点数目变少,客户端在请求某一对象时需要重新计算其hash值(通常与系统中的节点数目有关),由于hash值已经改变,所以很可能找不到保存该对象的服务器节点),因此一致性hash就显得至关重要,良好的分布式cahce系统中的一致性hash算法应该满足以下几个方面:
|
||||
|
||||
### 平衡性(Balance)
|
||||
平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。很多哈希算法都能够满足这一条件。
|
||||
|
||||
### 单调性(Monotonicity)
|
||||
单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲区加入到系统中,那么哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲区中去,而不会被映射到旧的缓冲集合中的其他缓冲区。简单的哈希算法往往不能满足单调性的要求,如最简单的线性哈希:x = (ax + b) mod (P),在上式中,P表示全部缓冲的大小。不难看出,当缓冲大小发生变化时(从P1到P2),原来所有的哈希结果均会发生变化,从而不满足单调性的要求。哈希结果的变化意味着当缓冲空间发生变化时,所有的映射关系需要在系统内全部更新。而在P2P系统内,缓冲的变化等价于Peer加入或退出系统,这一情况在P2P系统中会频繁发生,因此会带来极大计算和传输负荷。单调性就是要求哈希算法能够应对这种情况。
|
||||
|
||||
### 分散性(Spread)
|
||||
在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。
|
||||
|
||||
### 负载(Load)
|
||||
负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。
|
||||
|
||||
### 平滑性(Smoothness)
|
||||
平滑性是指缓存服务器的数目平滑改变和缓存对象的平滑改变是一致的。
|
||||
|
||||
|
||||
|
||||
## 3 原理
|
||||
### 基本概念
|
||||
一致性哈希算法(Consistent Hashing)最早在论文《Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web》中被提出。简单来说,一致性哈希将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-2^32-1(即哈希值是一个32位无符号整形),整个哈希空间环如下:
|
||||
|
||||

|
||||
|
||||
|
||||
整个空间按顺时针方向组织。0和232-1在零点中方向重合。
|
||||
|
||||
下一步将各个服务器使用Hash进行一个哈希,具体可以选择服务器的ip或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置,这里假设将上文中四台服务器使用ip地址哈希后在环空间的位置如下:
|
||||
|
||||

|
||||
|
||||
|
||||
接下来使用如下算法定位数据访问到相应服务器:将数据key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器。
|
||||
|
||||
例如我们有Object A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:
|
||||
|
||||

|
||||
|
||||
根据一致性哈希算法,数据A会被定为到Node A上,B被定为到Node B上,C被定为到Node C上,D被定为到Node D上。
|
||||
|
||||
|
||||
### 容错性与扩展性
|
||||
|
||||
下面分析一致性哈希算法的容错性和可扩展性。现假设Node C不幸宕机,可以看到此时对象A、B、D不会受到影响,只有C对象被重定位到Node D。一般的,在一致性哈希算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响。
|
||||
|
||||
下面考虑另外一种情况,如果在系统中增加一台服务器Node X,如下图所示:
|
||||
|
||||

|
||||
|
||||
此时对象Object A、B、D不受影响,只有对象C需要重定位到新的Node X 。一般的,在一致性哈希算法中,如果增加一台服务器,则受影响的数据仅仅是新服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它数据也不会受到影响。
|
||||
|
||||
综上所述,一致性哈希算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。
|
||||
|
||||
|
||||
### 数据倾斜问题
|
||||
另外,一致性哈希算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜问题。例如系统中只有两台服务器,其环分布如下,
|
||||
|
||||

|
||||
|
||||
此时必然造成大量数据集中到Node A上,而只有极少量会定位到Node B上。为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器ip或主机名的后面增加编号来实现。例如上面的情况,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “Node A#1”、“Node A#2”、“Node A#3”、“Node B#1”、“Node B#2”、“Node B#3”的哈希值,于是形成六个虚拟节点:
|
||||
|
||||

|
||||
|
||||
同时数据定位算法不变,只是多了一步虚拟节点到实际节点的映射,例如定位到“Node A#1”、“Node A#2”、“Node A#3”三个虚拟节点的数据均定位到Node A上。这样就解决了服务节点少时数据倾斜的问题。在实际应用中,通常将虚拟节点数设置为32甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布。
|
||||
@@ -142,7 +142,7 @@ int largestRectangleArea(vector<int>& heights) {
|
||||
|
||||
### 策略选择
|
||||
|
||||
* 思路:使用暴力解法求出所有区间,再求出区间的最小值相乘跟新数据,并不是一种很好的算法,所以经过上面俩题的磨 炼,此时我们应该使用一个单调递减栈
|
||||
* 思路:使用暴力解法求出所有区间,再求出区间的最小值相乘跟新数据,并不是一种很好的算法
|
||||
|
||||
### 算法设计
|
||||
|
||||
@@ -228,13 +228,13 @@ int GetMaxSequence(vector<int>& v)
|
||||
|
||||
### 算法设计
|
||||
|
||||
* 因此,我们可以使用单调栈作为维护 22 的数据结构,并给出下面的算法:
|
||||
* 因此,我们可以使用单调栈作为维护 2 的数据结构,并给出下面的算法:
|
||||
|
||||
* 我们用单调栈维护所有可以作为 22 的候选元素。初始时,单调栈中只有唯一的元素 \textit{a}[n-1]a[n−1]。我们还需要使用一个变量 \textit{max\_k}max_k 记录所有可以真正作为 22 的元素的最大值;
|
||||
* 我们用单调栈维护所有可以作为 2 的候选元素。初始时,单调栈中只有唯一的元素 $\textit{a}[n-1]$。我们还需要使用一个变量 $\textit{max\_k}$max_ 记录所有可以真正作为 2 的元素的最大值;
|
||||
|
||||
* 随后我们从 n-2n−2 开始从右到左枚举元素 a[i]a[i]:
|
||||
* 随后我们从 n-2开始从右到左枚举元素 a[i]:
|
||||
|
||||
* 首先我们判断 a[i]a[i] 是否可以作为 11。如果 a[i] < \textit{max\_k}a[i]<max_k,那么它就可以作为 11,我们就找到了一组满足 132132 模式的三元组;
|
||||
* 首先我们判断 a[i]是否可以作为 1。如果 $a[i] < \textit{max\_k}$,那么它就可以作为 1,我们就找到了一组满足 132模式的三元组;
|
||||
|
||||
* 随后我们判断 a[i]a[i] 是否可以作为 33,以此找出哪些可以真正作为 22 的元素。我们将 a[i]a[i] 不断地与单调栈栈顶的元素进行比较,如果 a[i]a[i] 较大,那么栈顶元素可以真正作为 22,将其弹出并更新 \textit{max\_k}max_k;
|
||||
|
||||
|
||||