首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

InnoDB学习(一)之BufferPool

我们知道InnoDB数据库的数据是持久化在磁盘上的,而磁盘的IO速度很慢,如果每次数据库访问都直接访问磁盘,显然严重影响数据库的性能。为了提升数据库的访问性能,InnoDB为数据库的数据增加了内存缓存区(BufferPool),避免每次访问数据库都进行磁盘IO。 <!--more--> 缓存区BufferPool 缓存区并不是Innodb中特有的概念,操作系统中也有缓存区的概念,当用户第一次从磁盘读取文件时,会把文件缓存到内存中,后续再对这个文件进行读操作就可以直接从内存中读,从而减少磁盘IO次数。缓存只是内存中的一块连续空间,InnoDB是如何合理利用缓存区的空间的呢?本文会从以下几个方面介绍InnoDB的缓存区: 缓存区概览:InnoDB缓存区的结构和状态查询; 缓存区实例(BufferPool Instance):缓存区可以划分为多个实例; BufferChunk:缓存区实例内的数据块; 控制块和数据页:InnoDB是以什么形式缓存数据库中的数据的; 空闲空间管理;缓存区内的空闲空间管理逻辑; 用户数据管理:数据库数据和索引在缓存区缓存的管理; 自适应哈希索引:优化热点数据等值查询的哈希索引; ChangeBuffer简介:提高数据库更新效率的ChangeBuffer; 锁信息管理:InnoDB中的行锁信息也是存放在缓存区中的; 缓存区概览 InnoDB中的缓存区叫innodb_buffer_pool,当读取数据时,就会先从缓存中查看是否数据的页(page)存在,不存在的话去磁盘上检索,查到后缓存到innodb_buffer_pool中。同理,插入、修改、删除也是先操作缓存里数据,之后再以一定频率更新到磁盘上,这个刷盘机制叫做Checkpoint。 如下图所示,InnoDB中的数据主要有数据页、索引页、插入缓存、自适应哈希索引、锁信息和数据字典信息。我们经常听到的RedoLog不在缓存区中。 MySQL默认的innodb_buffer_pool的大小是128M,我们可以通过以下命令查看innodb_buffer_pool的参数,执行结果如下图所示: show variables like 'innodb_buffer_pool%'; 在MySQL使用过程中,我们可能需要查看缓存区的状态,比如已使用空间大小、脏页大小等状态,我们可以通过以下命令查看innodb_buffer_pool的状态,执行结果如下图所示,图中的执行结果中,共有8192页数据。 show global status like '%innodb_buffer_pool%'; 缓存区实例 缓存区本身是一块内存空间,在多线程并发访问缓存的情况下,为了保证缓存页数据的正确性,可能会对缓存区单实例锁互斥访问,如果缓存区非常大并且多线程并发访问非常高的情况下,单实例缓存区的可能会影响请求的处理速度。如下图所示,数据库缓存区大小为3G,并发访问QPS为3000,如果缓存区只有一个实例,那么这3000个请求可能需要竞争同一个互斥锁。 MySQL 5.5引入了缓存区实例作为减小内部锁争用来提高MySQL吞吐量的手段,用户可以通过设置innodb_buffer_pool_instances参数来指定InnoDB缓存区实例的数目,默认缓存区实例的数目为1。缓存区实例的大小均为`innodb_buffer_pool_size/innodb_buffer_pool_instances。如下图所示,数据库缓存区大小为3G,并发访问QPS为3000,如果缓存区有3个实例,理想情况下最多每1000个请求会竞争同一个互斥锁。 如果缓存区总空间大小小于1G,innodb_buffer_pool_instances会被重置为1,因为小空间的多个缓存区实例反而会影响查询性能。 缓存区实例有以下特点: 缓存区实例有自己的锁/信号量/物理块/逻辑链表,缓存区实例之间没有锁竞争关系; 所有缓存区实例的空间在数据库启动时分配,数据库关闭后释放; 缓存页按照哈希函数随机分布到不同的缓存实例中; 缓存区实例的BufferChunk 我们知道缓存区可以包含多个缓存区实例,每个缓存区实例包含一块连续的内存空间,InnoDB把这块空间划分为多个BufferChunk,BufferChunk是InnoDB中的底层的物理块,BufferChunck中包含数据页和控制块两部分。 BufferChunk是最低层的物理块,在启动阶段从操作系统申请,直到数据库关闭才释放。通过遍历chunks可以访问几乎所有的数据页,有两种状态的数据页除外: 没有被解压的压缩页(BUF_BLOCK_ZIP_PAGE); 修改过且解压页已经被驱逐的压缩页(BUF_BLOCK_ZIP_DIRTY); BufferChunck中包含数据页和控制块两部分,二者存放的数据如下: 控制块:页面管理信息/互斥锁/页面的状态等数据块控制信息; 数据页:数据库数据/锁数据/自适应哈希数据,数据页的大小默认为16K; BufferChunck数据块的大小是可配置的,MySQL配置中默认BufferChunck数据块大小如下所示,用户可以在MySQL实例启动之前通过修改配置文件或启动参数中指定,达到自定义BufferChunck数据块的大小的目的。 $> mysqld --innodb-buffer-pool-chunk-size=134217728 [mysqld] innodb_buffer_pool_chunk_size = 134217728 用户自定义innodb_buffer_pool_chunk_size参数的大小应当小于单个缓存区实例的空间大小。如果innodb_buffer_pool_chunk_size值乘以innodb_buffer_pool_instances大于初始化缓冲池总大小时, innodb_buffer_pool_chunk_size则截断为innodb_buffer_pool_size/innodb_buffer_pool_instances。 控制块和数据页 通过上文,我们知道InnoDB中的底层物理块是BufferChunk,BufferChunk中包含了控制块和数据页,本节会介绍数据页和控制块分别包含哪些数据。 控制块 InnoDB中的每个数据页都有一个相对应的控制块,用于存储数据页的管理信息,但是这些信息不需要记录到磁盘,而是根据读入数据块在内存中的状态动态生成的。查找或者修改数据页时,总是会通过控制块进行数据块操作,控制块主要包含以下数据: 页面管理的普通信息/互斥锁/页面的状态等; 空闲链表/LRU链表/FLU链表等链表的管理; 按照一定的哈希函数快速定位数据页位置; 数据页 InnoDB中,数据管理的最小单位为页,默认是16KB,页中除了存储用户数据,还可以存储控制信息的数据。InnoDB IO子系统的读写最小单位也是页。如果对表进行了压缩,则对应的数据页称为压缩页,如果需要从压缩页中读取数据,则压缩页需要先解压,形成解压页,解压页为16KB。压缩页的大小是在建表的时候指定,目前支持16K,8K,4K,2K,1K。即使压缩页大小设为16K,在blob/varchar/text的类型中也有一定好处。假设指定的压缩页大小为4K,如果有个数据页无法被压缩到4K以下,则需要做B-tree分裂操作,这是一个比较耗时的操作。 数据页可以用于存放以下类型的数据,下文中我们会对这些类型的数据结构进行详细介绍: 用户数据,聚簇索引和非聚簇索引对应的节点数据; 行锁信息,InnoDB锁过多异常时,可以通过增加BufferPool大小解决; 自适应哈希,用于缓存热点数据; ChangeBuffer缓存; 空闲空间管理 当我们最初启动服务器的时候,需要完成对的初始化过程,就是分配的内存空间,把它划分成若干对控制块和缓存页。但是此时并没有真实的磁盘页被缓存到中(因为还没有用到),之后随着程序的运行,会不断的有磁盘上的页被缓存到中,那么问题来了,从磁盘上读取一个页到中的时候该放到哪个缓存页的位置呢?或者说怎么区分中哪些缓存页是空闲的,哪些已经被使用了呢?我们最好在某个地方记录一下哪些页是可用的,我们可以把所有空闲的页包装成一个节点组成一个双向链表,这个链表也可以被称作(或者说空闲链表)。 如果InnoDB刚刚启动,缓存区的所有缓存页都是空闲的,每一个缓存页都会被加入到空闲链表中,此时空闲列表的结构如下所示(此处省略数据页,空闲链表的指针指向数据块的控制块)。 在需要加载缓存页到BufferPool的情况下,如果空闲链表不为空,我们可以从空闲链表中获取一页空闲数据页,将缓存放入空闲的数据页。以LRU(后文详细介绍)为例,InnoDB启动后,LRU加载第一个缓存页之后,BufferPool中的数据情况如下所示。 用户数据管理 用户数据管理是BufferPool中最重要的数据,包含表数据与索引数据等数据,用户数据会按照数据的状态进行管理,主要包含以下数据管理,下文会一一介绍这几种链表: 最近最少使用链表(Least Recently Used, LRU):InnoDB中最重要的链表,包含所有读取进来的数据页; 脏页链表(Flush LRU List):管理LRU中的脏页,后台线程定时写入磁盘; 解压页链表(Unzip LRU List):管理LRU中的解压页数据,解压页数据是从压缩页通过解压而来的; 压缩页链表(Zip List):顾名思义,对页数据压缩后组成的链表; 最近最少使用链表LRU 最近最少使用链表LRU用于缓存表数据与索引数据,由于内存大小通常远远小于磁盘大小,内存中无法缓存全部的数据库数据,所以缓存通常需要一定的淘汰策略,淘汰缓存中不经常使用的数据页。InnoDB的BufferPool采用了改进版的LRU的淘汰策略。 如下图所示,LRU链表的结构和空闲链表的结构类似,是一个双向链表,链表中的节点包含指向数据页控制块的指针,可以通过控制块访问数据页中的数据。 当需要将新数据页添加到缓冲池时,最近最少使用的数据页会可能会从LRU链表中淘汰,并将新数据页添加到LRU链表的中间。此插入点将列LRU链表划分为两个子链表: 头部的5/8区域,最近访问多的热数据列表; 尾部的3/8区域,最近访问少的冷数据列表; LRU算法会将经常使用的数据页保留在热数据列表中,冷数据列表中包含了不经常访问的数据页,这些数据页是LRU列表满了之后最先被淘汰的数据。默认情况下,算法的流程如下: LRU链表的的后3/8区域用于存储冷数据; LRU链表的中点是热数据尾部与冷数据头部相交的边界; 被访问的冷数据会从冷数据链表移动到热数据链表; 热数据链表中的数据如果长时间不访问,会逐渐移入冷数据链表; 冷数据长时间不被访问,并且LRU链表满了,那么末尾的冷数据会淘汰出LRU链表; 预读的数据只会插入LRU链表,不会被移动到热数据链表; LRU算法还有一个问题,当某一个SQL语句,要批量扫描大量数据时,由于这些页都会被访问,可能导致把缓冲池的所有页都替换出去,导致大量热数据被换出,MySQL性能急剧下降,这种情况叫缓冲池污染。MySQL缓冲池加入了一个冷数据停留时间窗口的机制: 假设T=冷数据停留时间窗口; 插入冷数据头部的数据页,即使立刻被访问,也不会立刻放入新生代头部; 只有满足被访问并且在冷数据区域停留时间大于T,才会被放入新生代头部; 加入冷数据停留时间窗口策略后,短时间内被大量加载的页,并不会立刻插入新生代头部,而是优先淘汰那些短期内仅仅访问了一次的页。 MySQL中LRU链表相关的参数: innodb_old_blocks_pct:冷数据占整个LRU链长度的比例,默认是3/8,即整个LRU中热数据与冷数据长度比例是5:3。 innodb_old_blocks_time:冷数据停留时间窗口机制中冷数据停留时长; 脏数据链表FLU 当需要更新一个数据页时,如果数据页在内存中就直接更新更新内存中的数据,但是由于写回磁盘的代价比较高,所以InnoDB并不会立刻把修改后的数据写回磁盘,此时,就出现了缓存区数据页和磁盘数据页中的数据不一致的情况,这种情况下缓存区数据页被称为脏页,管理所有脏页的链表叫脏数据链表,以下为脏数据链表的示例图: 脏数据链表是LRU链表的子集,LRU链表包含了所有的脏页数据。脏页中的数据最终是要写回磁盘的,将内存数据页刷到磁盘的操作称为刷脏,以下是几种会触发InnoDB刷脏的情况 InnoDB的RedoLog写满了,这时候系统会停止所有更新操作,把Checkpoint往前推进,RedoLog留出空间可以继续写; 当系统内存不足,需要把一个脏页要从LRU链表中淘汰时,要先把脏页写回磁盘; MySQL在空闲时,会自动把一部分脏页写回磁盘; MySQL正常关闭时,会把所有脏页都写回磁盘; InnoDB中可以通过一些参数设置刷脏行为: innodb_io_capacity:MySQL数据文件所在磁盘的IO能力,innodb_io_capacity参数会影响MySQL刷脏页的速度。磁盘的IOPS可以通过FIO工具来测试,测试命令如下所示: fio -filename=$filename -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest 如果不能正确地设置innodb_io_capacity参数,可能能导致数据库性能问题。举个例子说明:如果MySQL主机磁盘用的是SSD,但是innodb_io_capacity的值设置的是比较低,只有300。这种情况下,InnoDB认为这个系统的IO能力只有300,所以刷脏页刷得特别慢,甚至比脏页生成的速度还慢,这样就造成了脏页累积,影响了查询和更新性能。 innodb_flush_neighbors:在准备刷一个脏页的时候,如果这个数据页旁边的数据页刚好是脏页,就会把这个“邻居”也带着一起刷掉;而且这个把“邻居”拖下水的逻辑还可以继续蔓延,也就是对于每个邻居数据页,如果跟它相邻的数据页也还是脏页的话,也会被放到一起刷。innodb_flush_neighbors参数就是用来控制这个行为的,值为1的时候会有上述的“连坐”机制,值为0时表示不找邻居,自己刷自己的。对于SSD这类IOPS比较高的设备,IOPS往往不是瓶颈,innodb_flush_neighbors应该设置为0。在MySQL8.0中,innodb_flush_neighbors参数的默认值已经是0了。 innodb_max_dirty_pages_pct:脏页比例超过innodb_max_dirty_pages_pct之后,InnoDB会全力刷脏页,如果没超过这个比例,那么刷脏页速度=max(当前脏页比例/innodb_max_dirty_pages_pct*innodb_io_capacity, RedoLog的缓存大小计算刷脏页速度); 压缩页链表(Zip List) Mysql允许用户对表进行压缩以节省磁盘空间,这些压缩页的数据在进入内存之后,要进行解压之后才能使用。 我们可以通过以下SQL语句建立一张InnoDB数据表: create table user_info ( id int primary key, age int not null, name varchar(16), sex bool )engine=InnoDB; 对于建立好的InnoDB数据表,我们可以通过以下SQL语句对表进行压缩,压缩后表占用的磁盘空间会减小: alter table user_info row_format=compressed; InnoDB中的表压缩是针对表数据页的压缩,不仅可以压缩表数据,还可以压缩表索引。压缩页的大小可以是1k/2k/4k/8k。 压缩页链表存储的就是这些压缩后的页,压缩页在加载进内存之后,并不会立即解压,而是在需要使用的时候再进行解压。 压缩页有不同的大小1k/2k/4k/8k,InnoDB使用了伙伴管理算法来管理压缩页。有5个ZipFree链表分别管理1k/2k/4k/8k/16K的内存碎片,8K的链表里存储的都是8K的碎片,如果新读入一个8K的页面,首先从这个链表中查找,如果有则直接返回,如果没有则从16K的链表中分裂出两个8K的块,一个被使用,另外一个放入8K链表中。 解压页链表(Unzip LRU List) 压缩页链表中的数据都是被压缩的,不能直接CRUD,使用前需要解压,解压后的数据都存储在解压页链表中,解压页链表中的数据写回磁盘时需要压缩。 自适应哈希索引 我们知道B+树默认的索引数据结构是B+树,B+树对范围查询或者LIKE语法的支持比较好。 如果数据库中有大量的等值查询,使用哈希索引能显著提升查询效率。Innodb存储引擎会监控对表上二级索引的查找,如果发现某二级索引被频繁访问,二级索引成为热数据,会对该热点数据建立内存哈希索引,这个索引被称为自适应哈希索引。 自适应哈希索引默认是开启状态,可以通过设置innodb_adaptive_hash_index变量或在启动MySQL时添加--skip-innodb-adaptive-hash-index变量启用自适应哈希索引。 InnoDB中可以查看到哈希索引的使用情况,命令及输出如下所示: mysql> show engine innodb status\G …… Hash table size 34673, node heap has 0 buffer(s) 0.00 hash searches/s, 0.00 non-hash searches/s ChangeBuffer 在修改数据库数据时,如果对应的数据页刚刚好在缓存区,可以之间修改缓存区的数据页,并把数据页标记为脏页。 如果修改数据数据时,对应的数据页如果不在缓存区,就需要把数据页从磁盘加载到缓存区,然后进行修改。对于写多读少的场景,会产生大量的磁盘IO,影响数据库的性能。 Change Buffer对数据更新过程有加速作用。 如果数据页没有在内存中,会将更新操作缓存到Change Buffer 中,这样就不需要从磁盘读入这个数据页,减少了IO操作,提高了性能。 先将更新操作,记录在Change Buffer 中,之后再进行 merge,真正进行数据更新。InnoDB Change Buffer比较复杂,我会在后续单独章节中进行介绍。 行锁信息管理 InnoDB支持行锁,可以对数据库中的数据进行加锁操作,这些锁信息也存放在BufferPool中,具体存储格式此处不做详细解释。 既然锁信息都存放在BufferPool中,那么锁的数目肯定受缓存区大小的影响,如果InnoDB中锁占据的空间超过了BufferPool总大小的70%,在新添加锁时会报以下错误: [FATAL] InnoDB: Over 95 percent of the buffer pool is occupied by lock heaps or the adaptive hash index! Check that your transactions do not set too many row locks. Your buffer pool size is 8 MB. Maybe you should make the buffer pool bigger? We intentionally generate a seg fault to print a stack trace on Linux!For more information, see Help and Support Center at http://www.mysql.com. 我是御狐神,欢迎大家关注我的微信公众号:wzm2zsd 参考文档 MySQL 8.0 Reference Manual/The InnoDB Storage Engine/InnoDB Architecture Chunk Change: InnoDB Buffer Pool Resizing 玩转MySQL之十InnoDB Buffer Pool详解 InnoDB的Buffer Pool简介 Mysql的Innodb存储引擎缓冲池个人理解 InnoDB关键特性之自适应hash索引 InnoDB页压缩技术 本文最先发布至微信公众号,版权所有,禁止转载!

优秀的个人博客,低调大师

机器学习算法之BIRCH

BIRCH(BalancedIterative Reducing and Clustering Using Hierarchies)全称是:利用层次方法的平衡迭代规约和聚类。BIRCH算法是1996年由TianZhang提出来的。Birch算法就是通过聚类特征(CF)形成一个聚类特征树,root层的CF个数就是聚类个数。 BIRCH算法实现分为4个步骤: 1、扫描所有数据,建立初始化的CF树,把稠密数据分成簇,稀疏数据作为孤立点对待。 2、这个步骤是可选的,步骤3的全局或半全局聚类算法有着输入范围的要求,以达到速度与质量的要求,所以此阶段在步骤1的基础上,建立一个更小的CF树。 3、补救由于输入顺序和页面大小带来的分裂,使用全局/半全局算法对全部叶节点进行聚类。 4、这个步骤也是可选的,把步骤3的中心点作为种子,将数据点重新分配到最近的种子上,保证重复数据分到同一个簇中,同时添加簇标签。 聚类特征(CF):每一个CF都是一个三元组,可以用(N,LS,SS)表示。其中N代表了这个CF中拥有的样本点的数量;LS代表了这个CF中拥有的样本点各特征维度的和向量,SS代表了这个CF中拥有的样本点各特征维度的平方和。 比如:CF中含有N=5个点,以两维样本点值为:(3,4)、(2,6)、(4,5)、(4,7)、(3,8)。 然后计算: LS=(3+2+4+4+3,4+6+5+7+8)=(16,30) SS=(32+22+42+42+32,42+62+52+72+82)=(54,190) 对于上图中的CF Tree,限定了B=7,L=5,也就是说内部节点最多有7个CF(含有的分支点数目),而叶子节点最多有5个CF(每个叶子还有的CF个数,实际上就是每个叶子中包含的样本数目)。叶子节点是通过双向链表连通的。 BIRCH算法举例: Birch算法在sklearn.cluster中 具体代码如下 #coding = utf-8##算法涉及主要参数##n_clusters:簇数##threshold :扫描阈值##branches_factors: 每个节点中CF子集群的最大数量,默认值为50##最后通过读取:label_;来获知每个数据点的分类情况import numpy as npimport matplotlib.pyplot as pltfrom sklearn.datasets.samples_generator import make_blobsfrom sklearn.cluster import Birch## X为样本特征,Y为样本簇类别,共500个样本,每个样本2个特征,共4个簇,## 簇中心在[-1,-1],[0,0],[1,1],[2,2]X,Y = make_blobs(n_samples = 500,n_features = 2, centers=[[-1,-1],[0,0],[1,1],[2,2]], cluster_std=[0.5,0.4,0.5,0.4],random_state=9)##设置birch函数birch = Birch(n_clusters = None)##训练数据y_pred = birch.fit_predict(X)##绘图plt.figure()plt.subplot(2,2,1)plt.scatter(X[:,0],X[:,1])plt.title('DataSample')plt.subplot(2,2,2)plt.scatter(X[:,0],X[:,1],c=y_pred)plt.title('None')##设置birch函数birch =Birch(n_clusters = 4)##训练数据y_pred =birch.fit_predict(X)plt.subplot(2,2,3)plt.scatter(X[:,0], X[:, 1], c=y_pred)plt.title('n_clusters=4')plt.show() 最后的效果:

优秀的个人博客,低调大师

java学习记录-树结构

概念 树是一种重要的非线性数据结构,直观地看,它是数据元素(在树中称为结点)按分支关系组织起来的结构,很象自然界中的树那样。 树是由结点或顶点和边组成的(可能是非线性的)且不存在着任何环的一种数据结构。没有结点的树称为空(null或empty)树。一棵非空的树包括一个根结点,还(很可能)有多个附加结点,所有结点构成一个多级分层结构 结点:下面就是一个简单的树,每一个元素都称为一个结点,而最顶端的是根结点,最末端的是叶子结点, 每个结点有零个或多个子结点;每一个非根结点有且只有一个父结点;除了根结点外,每个子结点可以分为多个不相交的子树 度:度就是一棵树里面最大的宽度,下图树(包含子树)的子结点数最大都不超过2所以该树的度为2 深度:由根结点到叶子结点组成该树各结点的最大层次 二叉树 二叉树又分为二叉查找树、完全二叉树、满二叉树、平衡二叉树、红黑树、B树 为什么二叉树也还要区分那么多种类型?因为单种二叉树不能完全保证我们运行或储存效率,所以需要多种类型二叉树应对不同的使用场景 下面先看一下二叉查找树 录入的顺序 为 E-->F-->C-->B-->D-->A--G 二叉树与树的区别 1.树的度没有限制,二叉树的度最大为2 2.二叉树每个子树都是有序的,例如二叉树的左子树如果非空,左子树所以结点的值都小于根节点的值,如果右子树非空,则右子树所有结点的值都大于根结点的值(左小右大) 因为二叉树左小右大的特点,所以二叉查找树有个比较极端的情况 录入的顺序 为 A-->B-->C-->D-->E 从小到大的开始录入,大的永远在右边,就造成了右斜树,与链表(线性结构)一样这种树并不能保证我们的运行效率 平衡二叉树(AVL树) 平衡二叉树是基于二叉查找树优化而来的 录入顺序: A-->B-->C-->D-->E-->F 录入顺序与一般二叉树一样,都是从小到大开始录入,但是平衡二叉树每次插入数据会判断数据的大小,从而进行左右旋转,使得我们的数据分布均匀,保证了一定的运行效率 平衡二叉树与二叉查找树的区别主要体现在二叉查找树的根结点不能变,平衡二叉树为了保证根结点左右子树的层级差不超过1,所以会进行左右旋转操作,这个时候根结点就会发生改变 优点:因为左右子树结点数量相差较少,所以查询速度极快 缺点:插入数据时候因为旋转次数不确定,所以插速度会相对慢 红黑树 性质: 1. 节点是红色或黑色。 2. 根节点是黑色。 3 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点) 4. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点 红黑树与平衡二叉树相似,平衡二叉树基于 根结点左右子树的层级差不超过1 进行旋转操作,而红黑树则是基于 没有任何一条路径比其他路径长两倍以上进行(旋转次数不超过3次,新增不超过2次,删除不超过3次)旋转 录入顺序: A-->B-->C-->D-->E-->F-->G 录入顺序: A-->B-->C-->D-->E-->F-->G-->H 由第一张红黑树图可以看出,红黑树的平衡度并没有AVL树平衡度那么严格(只保持黑结点平衡)由于旋转次数是保证在3以内的,所以插入或删除结点时候性能要比平衡二叉树好 B树 ---Balance-tree(平衡多路查找树) 下面是一棵三阶的B树 录入顺序: A-->B-->C-->D-->E-->F-->G-->H 阶:每个B树都有个指定的阶(M),阶决定了每个结点最多拥有多少个子结点,例如三阶B树每个结点最多只能拥有三个子结点 阶与度不一样,上图树的度为2,阶为3,但是阶决定度的最大值 关键字:例如每一个结点里面的值就是一个关键字,例如,该结点就有2个关键字,G和H B树的性质 1.每个结点里面的关键字数量最多为M-1,根节点最少可以只有1个关键字,非根节点至少有M/2(上取整)个关键字。 2.每个结点至少有2个子结点,叶子结点除外 3.除根结点以外的所有结点(不包括叶子结点)的度数正好是关键字总数加1 4.B树的叶子结点都是在同一层 例如现在要录入一个A-E的三阶B树 先录入A和B 接下来要录入C,因为根结点的关键字数量为1到M-1和每个结点至少有2个子结点,所以录入C的时候应该B作为根结点,A和C作为子结点 插入D 插入E 插入元素时,如果树结构不满足B树的性质时,就会进行合并或分裂 B树与二叉树的比较 下面使用元素相同的平衡二叉树(二叉树里面查询快)与B树的查询作比较 平衡二叉树查询 H 第一步 第二步 第三步 第四步 B树查询 H 第一步 第二步 第三步 第四步 虽然看上去都是需要四步才能完成H的查询,因为树结构是非线性的,每个结点的地址是不连续的,所以B树的第四步是在一块内存内比较,要比平衡二叉树的第四步重新寻找地址快很多,并且,如果数据是存放在磁盘而不是内存的时候,每次IO操作性能消耗非常大,且随着数据量增大,两者的性能差距更加明显 B+树 B+树与B树的区别在于B+树每一个元素都存在于叶子结点,非叶子结点仅仅提供索引操作,必须到叶子结点才会命中 这样保证了我们寻找任意一个元素时,所需要的时间都是一样的 并且叶子结点以链表形式连接,这样有利于遍历所有数据时候,只需要进行线性查询,而不用一层一层遍历

优秀的个人博客,低调大师

AIoT 开发学习资料汇总

一、阿里云 AIoT 产品/资源矩阵 先盘点一下有哪些可用的产品和服务,以便快速集成,搭建出高可靠应用。 设备服务 设备接入服务 提供安全可靠的设备连接通信能力,帮助用户将海量设备数据采集到阿里云物联网平台,并且客户应用可以通过调用平台提供的 API 下发数据给设备,实现远程控制海量设备的目的。物联网平台还提供了与阿里云众多产品打通的规则引擎,帮助用户将应用快速集成。 AliOS Things(开源 AIoT 操作系统) 丨 GitHub 项目主页丨钉钉群:开发交流 AliOS Things 是 AliOS 家族旗下、面向 IoT 领域的、高可伸缩的物联网操作系统。 设备身份认证 物联网设备身份认证系统,通过可信计算和密码技术为物联网系统提供设备安全认证、安全连接、业务数据加密、密钥管理等端到端的可信接入能力。 物联网云服务 物联网平台 物联网平台提供

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册