科普文:从源码解读5种Redis基本数据类型

键值对字符串

char* 与 SDS

char* 的不足:

  • 操作效率低:获取长度需遍历,O(N)复杂度

  • 二进制不安全:无法存储包含 \0 的数据

SDS 的优势:

  • 操作效率高:获取长度无需遍历,O(1)复杂度(通过len和alloc,快速获取字符长度大小以及跳转到字符串末尾)

  • 二进制安全:因单独记录长度字段,所以可存储包含 \0 的数据

  • 兼容 C 字符串函数,可直接使用字符串 API

  • 紧凑型内存设计(按照字符串类型,len和alloc使用不同的类型节约内存,并且关闭内存对齐来达到内存高效利用,在redis中除了sds,intset和ziplist也有类似的目底)

  • 避免频繁的内存分配。除了sds部分类型存在预留空间,sds设计了sdsfree和sdsclear两种字符串清理函数,其中sdsclear,只是修改len为0以及buf为'\0',并不会实际释放内存,避免下次使用带来的内存开销

SDS中的优化

Redis 在操作 SDS 时,为了避免频繁操作字符串时,每次「申请、释放」内存的开销,还做了这些优化:

  • 内存预分配:SDS 扩容,会多申请一些内存(小于 1MB 翻倍扩容,大于 1MB 按 1MB 扩容)

  • 多余内存不释放:SDS 缩容,不释放多余的内存,下次使用可直接复用这些内存

这种策略,是以多占一些内存的方式,换取「追加」操作的速度。这个内存预分配策略,详细逻辑可以看 sds.c 的 sdsMakeRoomFor 函数。

SDS在Redis内部模块中的实现

SDS 字符串在 Redis 内部模块实现中也被广泛使用,在 Redis server 和客户端的实现中,能找到使用 SDS 字符串的地方很多:

  • Redis 中所有 key 的类型就是 SDS(详见 db.c 的 dbAdd 函数)

  • Redis Server 在读取 Client 发来的请求时,会先读到一个缓冲区中,这个缓冲区也是 SDS(详见 server.h 中 struct client 的 querybuf 字段)

  • 写操作追加到 AOF 时,也会先写到 AOF 缓冲区,这个缓冲区也是 SDS (详见 server.h 中 struct client 的 aof_buf 字段)

Hash表

dict数据结构

  • Redis 中的 dict 数据结构,采用「链式哈希」的方式存储,当哈希冲突严重时,会开辟一个新的哈希表,翻倍扩容,并采用「渐进式 rehash」的方式迁移数据

  • Redis 中凡是需要 O(1) 时间获取 k-v 数据的场景,都使用了 dict 这个数据结构,也就是说 dict 是 Redis 中重中之重的「底层数据结构」

  • dict 封装好了友好的「增删改查」API,并在适当时机「自动扩容、缩容」,这给上层数据类型(Hash/Set/Sorted Set)、全局哈希表的实现提供了非常大的便利

  • 例如,Redis 中每个 DB 存放数据的「全局哈希表、过期key」都用到了 dict:
    // server.h
    typedef struct redisDb {dict *dict;     // 全局哈希表,数据键值对存在这dict *expires;  // 过期 key + 过期时间 存在这...
    }
    

rehash

  • 所谓「渐进式 rehash」是指,把很大块迁移数据的开销,平摊到多次小的操作中,目的是降低主线程的性能影响

  • 「全局哈希表」在触发渐进式 rehash 的情况有 2 个:

    • 增删改查哈希表时:每次迁移 1 个哈希桶( dict.c 中的 _dictRehashStep 函数)

    • 定时 rehash:如果 dict 一直没有操作,无法渐进式迁移数据,那主线程会默认每间隔 100ms 执行一次迁移操作。这里一次会以 100 个桶为基本单位迁移数据,并限制如果一次操作耗时超时 1ms 就结束本次任务,待下次再次触发迁移(dict.c 的 dictRehashMilliseconds 函数)

  • dict 在负载因子超过 1 时(used: bucket size >= 1),会触发 rehash。但如果 Redis 正在 RDB 或 AOF rewrite,为避免父进程大量写时复制,会暂时关闭触发 rehash。但这里有个例外,如果负载因子超过了 5(哈希冲突已非常严重),依旧会强制做 rehash(重点)

  • dict 在 rehash 期间,查询旧哈希表找不到结果,还需要在新哈希表查询一次

  • SipHash 哈希算法是在 Redis 4.0 才开始使用的,3.0-4.0 使用的是 MurmurHash2 哈希算法,3.0 之前是 DJBX33A 哈希算法

问题解答

  • redis的dict结构核心就是链式hash,其原理其实和JDK的HashMap类似(JDK1.7之前的版本,1.8开始是红黑树或链表),这里就有一个问题为什么Redis要使用链式而不引入红黑树呢,或者直接使用红黑树?

    • hash冲突不使用红黑树:redis需要高性能,如果hash冲突使用红黑树,红黑树和链表的转换会引起不必要的开销(hash冲突不大的情况下红黑树其实比链表沉重,还会浪多余的空间)

    • dict不采用红黑树:在负载因子较低,hash冲突较低的情况下,hash表的效率O(1)远远高于红黑树

    • 当采用渐进式rehash的时候,以上问题都可以解决

  • 何为渐进式rehash?本质原理是什么?当内存使用变小会缩容吗?

    • 渐进式rehash的本质是分治思想,通过把大任务划分成一个个小任务,每个小任务只执行一小部分数据,最终完成整个大任务的过程

    • 渐进式rehash可以在不影响运行中的redis使用来完成整改hash表的扩容(每次可以控制只执行1ms)

    • 初步判定会,因为dictResize中用于计算hash表大小的minimal就是来源于实际使用的大小,并且htNeedsResize方法中(used*100/size < HASHTABLE_MIN_FILL)来判断是否触发缩容来节约内存,而缩容也是渐进式rehash

渐进式rehash怎么去执行?

在了解渐进式rehash之前,我们需要了解一个事情,就是正在运行执行任务的redis,其实本身就是一个单线程的死循环(不考虑异步以及其他fork的场景),其循环的方法为aeMain(),位于ae.c文件中,在这个循环中每次执行都会去尝试执行已经触发的时间事件和文件事件,而渐进式rehash的每个小任务就是位于redis,serverCron时间事件中,redis每次循环的时候其实都会经过如下所示的调用流程:

  • serverCron -> updateDictResizePolicy (先判断是否能执行rehash,当AOF重写等高压力操作时候不执行)

  • serverCron -> databasesCron -> incrementallyRehash -> dictRehashMilliseconds -> dictRehash (dictRehashMilliseconds默认要求每次rehash最多只能执行1ms)

  • 通过这种方式最终完成整改hash表的扩容

SDS

redisObject

  • 要想理解 Redis 数据类型的设计,必须要先了解 redisObject。Redis 的 key 是 String 类型,但 value 可以是很多类型(String/List/Hash/Set/ZSet等),所以 Redis 要想存储多种数据类型,就要设计一个通用的对象进行封装,这个对象就是 redisObject。

  • 代码中定义如下:
    // server.h
    typedef struct redisObject {unsigned type:4;unsigned encoding:4;unsigned lru:LRU_BITS;int refcount;void *ptr;
    } robj;
    
  • 其中,最重要的 2 个字段:

    • type:面向用户的数据类型(String/List/Hash/Set/ZSet等)

    • encoding:每一种数据类型,可以对应不同的底层数据结构来实现(SDS/ziplist/intset/hashtable/skiplist等)

  • 举例说明

    • 例如 String,可以用 embstr(嵌入式字符串,redisObject 和 SDS 一起分配内存),也可以用 rawstr(redisObject 和 SDS 分开存储)实现。

    • 又或者,当用户写入的是一个「数字」时,底层会转成 long 来存储,节省内存。

    • 同理,Hash/Set/ZSet 在数据量少时,采用 ziplist 存储,否则就转为 hashtable 来存。

  • 所以,redisObject 的作用在于:

    • 为多种数据类型提供统一的表示方式

    • 同一种数据类型,底层可以对应不同实现,节省内存

    • 支持对象共享和引用计数,共享对象存储一份,可多次使用,节省内存

  • redisObject 更像是连接「上层数据类型」「底层数据结构」之间的桥梁。

String

  • String 类型的实现,底层对应 3 种数据结构:

    • embstr:小于 44 字节,嵌入式存储,redisObject 和 SDS 一起分配内存,只分配 1 次内存

    • rawstr:大于 44 字节,redisObject 和 SDS 分开存储,需分配 2 次内存

    • long:整数存储(小于 10000,使用共享对象池存储,但有个前提:Redis 没有设置淘汰策略,详见 object.c 的 tryObjectEncoding 函数)

ziplist

  • ziplist 的特点:

    • 连续内存存储:每个元素紧凑排列,内存利用率高

    • 变长编码:存储数据时,采用变长编码(满足数据长度的前提下,尽可能少分配内存)

    • 寻找元素需遍历:存放太多元素,性能会下降(适合少量数据存储)

    • 级联更新:更新、删除元素,会引发级联更新(因为内存连续,前面数据膨胀/删除了,后面要跟着一起动)

  • List、Hash、Set、ZSet 底层都用到了 ziplist。

intset

intset 的特点:

  • Set 存储如果都是数字,采用 intset 存储

  • 变长编码:数字范围不同,intset 会选择 int16/int32/int64 编码(intset.c 的 _intsetValueEncoding 函数)

  • 有序:intset 在存储时是有序的,这意味着查找一个元素,可使用「二分查找」(intset.c 的 intsetSearch 函数)

  • 编码升级/降级:添加、更新、删除元素,数据范围发生变化,会引发编码长度升级或降级

SDS使用嵌入式字符串的条件

  • SDS 判断是否使用嵌入式字符串的条件是 44 字节

  • jemalloc 分配内存机制,jemalloc 为了减少分配的内存空间大小不是2的幂次,在每次分配内存的时候都会返回2的幂次的空间大小,比如我需要分配5字节空间,jemalloc 会返回8字节,15字节会返回16字节。其常见的分配空间大小有:8, 16, 32, 64, ..., 2kb, 4kb, 8kb。

  • 但是这种方式也可能会造成,空间的浪费,比如我需要33字节,结果给我64字节,为了解决这个问题jemalloc将内存分配划分为,小内存(small_class)和大内存(large_class)通过不同的内存大小使用不同阶级策略,比如小内存允许存在48字节等方式。

  • 嵌入式字符串会把 redisObject 和 SDS 一起分配内存,那在存储时结构是这样的:

    • redisObject:16 个字节

    • SDS:sdshdr8(3 个字节)+ SDS 字符数组(N 字节 + \0 结束符 1 个字节)

  • Redis 规定嵌入式字符串最大以 64 字节存储,所以 N = 64 - 16(redisObject) - 3(sdshr8) - 1(\0), N = 44 字节。

redis充分提高内存利用率的手段

  • 淘汰不再使用的内存空间

  • 紧凑型的内存设计

    • 设计实现了SDS

    • 设计实现了ziplist

    • 设计实现了intset

    • 搭配redisObject

    • 设计了嵌入式字符串

  • 实例内存共享

    • 设计了共享对象(共享内存大部是常量实例)

有序集合

ZSet

  • ZSet 当数据比较少时,采用 ziplist 存储,每个 member/score 元素紧凑排列,节省内存

  • 当数据超过阈值(zset-max-ziplist-entries、zset-max-ziplist-value)后,转为 hashtable + skiplist 存储,降低查询的时间复杂度

  • hashtable 存储 member->score 的关系,所以 ZSCORE 的时间复杂度为 O(1)

skiplist

  • skiplist 是一个「有序链表 + 多层索引」的结构,把查询元素的复杂度降到了 O(logN),服务于 ZRANGE/ZREVRANGE 这类命令

  • skiplist 的多层索引,采用「随机」的方式来构建,也就是说每次添加一个元素进来,要不要对这个元素建立「多层索引」?建立「几层索引」?都要通过「随机数」的方式来决定

  • 每次随机一个 0-1 之间的数,如果这个数小于 0.25(25% 概率),那就给这个元素加一层指针,持续随机直到大于 0.25 结束,最终确定这个元素的层数(层数越高,概率越低,且限制最多 64 层,详见 t_zset.c 的 zslRandomLevel 函数)

  • 这个预设「概率」决定了一个跳表的内存占用和查询复杂度:概率设置越低,层数越少,元素指针越少,内存占用也就越少,但查询复杂会变高,反之亦然。这也是 skiplist 的一大特点,可通过控制概率,进而控制内存和查询效率

  • skiplist 新插入一个节点,只需修改这一层前后节点的指针,不影响其它节点的层数,降低了操作复杂度(相比平衡二叉树的再平衡,skiplist 插入性能更优)

问题解答

  • 关于 Redis 的 ZSet 为什么用 skiplist 而不用平衡二叉树实现的问题,原因是:

    • skiplist 更省内存:25% 概率的随机层数,可通过公式计算出 skiplist 平均每个节点的指针数是 1.33 个,平衡二叉树每个节点指针是 2 个(左右子树)

    • skiplist 遍历更友好:skiplist 找到大于目标元素后,向后遍历链表即可,平衡树需要通过中序遍历方式来完成,实现也略复杂

    • skiplist 更易实现和维护:扩展 skiplist 只需要改少量代码即可完成,平衡树维护起来较复杂

  • 在使用跳表和哈希表相结合的双索引机制时,在获得高效范围查询和单点查询的同时,有哪些不足之处?

    • 这种发挥「多个数据结构」的优势,来完成某个功能的场景,最大的特点就是「空间换时间」,所以内存占用多是它的不足。

    • 不过也没办法,想要高效率查询,就得牺牲内存,鱼和熊掌不可兼得。

    • 不过 skiplist 在实现时,Redis 作者应该也考虑到这个问题了,就是上面提到的这个「随机概率」,Redis 后期维护可以通过调整这个概率,进而达到「控制」查询效率和内存平衡的结果。当然,这个预设值是固定写死的,不可配置,应该是 Redis 作者经过测试和权衡后的设定,我们这里只需要知晓原理就好。

  • redis作为一款优化到极致的中间件,不会单纯使用一种数据类型去实现一个功能,而会根据当前的情况选择最合适的数据结构,比如zset就是dict + skiplist,甚至当元素较少的时候zsetAdd方法会优先选择ziplist而不直接使用skiplist,以到达节约内存的效果(当小key泛滥的时候很有效果),当一种数据结构存在不足的情况下,可以通过和其它数据结构搭配来弥补自身的不足(软件设计没有银弹,只有最合适)

  • redis仰仗c语言指针的特性,通过层高level数组实现的skiplist从内存和效率上来说都是非常优秀的,如果对比JDK的ConcurrentSkipListMap的实现(使用了大量引用和频繁的new操作),指针的优势无疑显现出来了

  • skiplist的随机率层高。既保证每层的数量相对为下一层的一半,又保证了代码执行效率

quicklist,listpack

ziplist

  • ziplist 设计的初衷就是「节省内存」,在存储数据时,把内存利用率发挥到了极致:

    • 数字按「整型」编码存储,比直接当字符串存内存占用少

    • 数据「长度」字段,会根据内容的大小选择最小的长度编码

    • 甚至对于极小的数据,干脆把内容直接放到了「长度」字段中(前几个位表示长度,后几个位存数据)

  • 但 ziplist 的劣势也很明显:

    • 寻找元素只能挨个遍历,存储过长数据,查询性能很低

    • 每个元素中保存了「上一个」元素的长度(为了方便反向遍历),这会导致上一个元素内容发生修改,长度超过了原来的编码长度,下一个元素的内容也要跟着变,重新分配内存,进而就有可能再次引起下一级的变化,一级级更新下去,频繁申请内存

quicklist

  • 想要缓解 ziplist 的问题,比较简单直接的方案就是,多个数据项,不再用一个 ziplist 来存,而是分拆到多个 ziplist 中,每个 ziplist 用指针串起来,这样修改其中一个数据项,即便发生级联更新,也只会影响这一个 ziplist,其它 ziplist 不受影响,这种方案就是 quicklist

     qucklist: ziplist1(也叫quicklistNode) <-> ziplist2 <-> ziplist3 <-> ... 
    

List

  • List 数据类型底层实现,就是用的 quicklist,因为它是一个链表,所以 LPUSH/LPOP/RPUSH/RPOP 的复杂度是 O(1)

  • List 中每个 ziplist 节点可以存的元素个数/总大小,可以通过 list-max-ziplist-size 配置:

    • 正数:ziplist 最多包含几个数据项

    • 负数:取值 -1 ~ -5,表示每个 ziplist 存储最大的字节数,默认 -2,每个ziplist 8KB

    • ziplist 超过上述配置,添加新元素就会新建 ziplist 插入到链表中。

  • List 因为更多是两头操作,为了节省内存,还可以把中间的 ziplist「压缩」,具体可看 list-compress-depth 配置项,默认配置不压缩

listpack

  • 要想彻底解决 ziplist 级联更新问题,本质上要修改 ziplist 的存储结构,也就是不要让每个元素保存「上一个」元素的长度即可,所以才有了 listpack

  • listpack 每个元素项不再保存上一个元素的长度,而是优化元素内字段的顺序,来保证既可以从前也可以向后遍历

  • listpack 是为了替代 ziplist 为设计的,但因为 List/Hash/ZSet 都严重依赖 ziplist,所以这个替换之路很漫长,目前只有 Stream 数据类型用到了 listpack。set底层是intset和dict实现的,并没有使用到ziplist。

Stream使用的Radix Tree

Radix Tree的优势和不足

作为有序索引,Radix Tree 也能提供范围查询,和 B+ 树、跳表相比,你觉得 Radix Tree 有什么优势和不足么?

  1. Radix Tree 优势

    Stream 在存消息时,推荐使用默认自动生成的「时间戳+序号」作为消息 ID,不建议自己指定消息 ID,这样才能发挥 Radix Tree 公共前缀的优势。

    • 本质上是前缀树,所以存储有「公共前缀」的数据时,比 B+ 树、跳表节省内存

    • 没有公共前缀的数据项,压缩存储,value 用 listpack 存储,也可以节省内存

    • 查询复杂度是 O(K),只与「目标长度」有关,与总数据量无关

    • 这种数据结构也经常用在搜索引擎提示、文字自动补全等场景

  2. Radix Tree 不足

    • 如果数据集公共前缀较少,会导致内存占用多

    • 增删节点需要处理其它节点的「分裂、合并」,跳表只需调整前后指针即可

    • B+ 树、跳表范围查询友好,直接遍历链表即可,Radix Tree 需遍历树结构

    • 实现难度高比 B+ 树、跳表复杂

每种数据结构都是在面对不同问题场景下,才被设计出来的,结合各自场景中的数据特点,使用优势最大的数据结构才是正解。

B+树和跳跃表的关联

  • B+树和跳跃表这两种数据结构在本身设计上是有亲缘关系的,其实如果把B+树拉直来看不难发现其结构和跳跃表很相似,甚至B+树的父亲结点其实类似跳跃表的level层级。

  • 在当前计算机硬件存储设计上,B+树能比跳表存储更大量级的数据,因为跳表需要通过增加层高来提高索引效率,而B+树只需要增加树的深度。此外B+树同一叶子的连续性更加符合当代计算机的存储结构。然而跳表的层高具有随机性,当层高较大的时候磁盘插入会带来一定的开销,且不利于分块。

Redis不使用B+树而选择跳表

因为数据有序性的实现B+树不如跳表,跳表的时间性能是优于B+树的(B+树不是二叉树,二分的效率是比较高的)。此外跳表最低层就是一条链表,对于需要实现范围查询的功能是比较有利的,而且Redis是基于内存设计的,无需考虑海量数据的场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/387084.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

60个常见的 Linux 指令

常见60个Linux指令 1.ssh 登录到计算机主机2.ls 列出目录内容3.pwd 当前终端会话所在的完整路径4.cd 切换当前工作目录5.touch 创建空文件或更新文件的时间戳6.echo 终端输出文本或变量值7.nano 在终端中编辑文件8.vim 文本编辑器9.cat 查看、连接和创建文件10.shred 安全删除敏…

XPathParser类

XPathParser类是mybatis对 javax.xml.xpath.XPath的包装类。 接下来我们来看下XPathParser类的结构 1、属性 // 存放读取到的整个XML文档private final Document document;// 是否开启验证private boolean validation;// 自定义的DTD约束文件实体解析器&#xff0c;与valida…

科研绘图系列:R语言山脊图(Ridgeline Chart)

介绍 山脊图(Ridge Chart)是一种用于展示数据分布和比较不同类别或组之间差异的数据可视化技术。它通常用于展示多个维度或变量之间的关系,以及它们在不同组中的分布情况。山脊图的特点: 多变量展示:山脊图可以同时展示多个变量的分布情况,允许用户比较不同变量之间的关…

FastAPI(七十二)实战开发《在线课程学习系统》接口开发-- 留言列表开发

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 之前我们分享了FastAPI&#xff08;七十一&#xff09;实战开发《在线课程学习系统》接口开发-- 查看留言&#xff0c;这次我们分享留言列表开发。 获…

i2c中结构体 数据传输 i2c Tools使用

I2C中重要结构体 在I2C&#xff08;Inter-Integrated Circuit&#xff09;通信中&#xff0c;涉及的主要结构体通常用于描述设备、消息和传输的配置。以下是一些常见的I2C结构体及其作用&#xff1a; i2c_adapter: 这是一个代表I2C总线适配器的结构体。它包含与该I2C总线相关的…

Hive3:Centos7环境部署Hive服务

一、安装说明 1、Hadoop集群情况 3台机器&#xff1a;4G2C、2G2C、2G2C 安装教程&#xff1a;Centos7环境安装Hadoop集群 2、安装MySQL&#xff0c;用于存储Hive的元数据 在102机器上安装MySQL 安装MySQL使用服务器的root账号 3、最后安装Hive 安装hive过程使用服务器的atgu…

dpdk编译安装以及接收udp报文(基于ubuntu)

目录 1、编译 2、设置运行环境 3、使用dpdk接收udp报文 3.1、设置发送端arp信息 3.2、测试 3.3、代码 4、其他 1、编译 代码下载&#xff1a; DPDK 下载版本&#xff1a;DPDK 19.08.2 export RTE_SDK/root/dpdk-stable-19.08.2/ export RTE_TARGETx86_64-native-li…

STM32简介

1.STM32的三个重要特征 32位微控制器&#xff0c;也称作MCU。 由ST&#xff08;意法半导体&#xff09;公司开发。 以ARM-Cortex-M为核心。 2.STM32的优点 3.ARM ARM是RISC精简指令集的代表&#xff0c;很多移动设备都是基于ARM架构的。ARM自2004年以后放弃使用数字命名法…

Fantastic-admin:Vue 中后台管理系统

Fantastic-admin&#xff1a;Vue 中后台管理系统 在当今的前端开发世界里&#xff0c;fantastic-admin 作为一款功能强大的 Vue 中后台管理系统框架&#xff0c;简直是开发者的福音。本文将介绍 fantastic-admin 的基本信息、特点&#xff0c;以及如何快速上手和使用。 项目简介…

快速搞定分布式RabbitMQ---RabbitMQ进阶与实战

本篇内容是本人精心整理&#xff1b;主要讲述RabbitMQ的核心特性&#xff1b;RabbitMQ的环境搭建与控制台的详解&#xff1b;RabbitMQ的核心API&#xff1b;RabbitMQ的高级特性;RabbitMQ集群的搭建&#xff1b;还会做RabbitMQ和Springboot的整合&#xff1b;内容会比较多&#…

内存泄漏详解

文章目录 什么是内存泄漏内存泄漏的原因排查及解决内存泄漏避免内存泄漏及时释放资源设置合理的变量作用域及时清理不需要的对象避免无限增长避免内部类持有外部类引用使用弱引用 什么是内存泄漏 内存泄漏是指不使用的对象持续占有内存使得内存得不到释放&#xff0c;从而造成…

AI绘画进阶工具 ComfyUI 新版来啦!操作界面详解!取消悬浮面板,自带工作流管理功能!(附安装包)

大家好&#xff0c;我是画画的小强 在 7 月初的一次更新中&#xff0c;ComfyUI 官方推出了 Beta 版 UI&#xff0c;取消了原本的悬浮面板&#xff0c;还新增了工作流管理功能&#xff0c;整体使用体验比之前好了很多。今天就为大家详细介绍一些新版 UI 的特点和用法。 一、启…

HDBaseT远距离无压缩传输系统源头厂家

HDBaseT双绞线延长器是一款集成HDBaseT的远距离高清信号无压缩、无延时传输器&#xff0c;HDMI信号从接收端输出&#xff0c; 信号分辨率高达4Kx2K可以通过单根CAT5/CAT6网线将信号长距离传输高清无压缩音视频信号&#xff0c; 采用单根网线最远可传输70/100米&#xff0c; …

Hive-内部表和外部表

区别 内部表实例 准备数据 查看数据 删除数据 外部表实例 准备数据 查看数据 删除数据 区别 内部表&#xff1a;管理元数据&#xff08;记录数据的文件和目录的信息&#xff09;和数据。当删除内部表时&#xff0c;会删除数据和表的元数据&#xff0c;所以当多个表关…

LLM推理优化——KV Cache篇(百倍提速)

LLM推理优化——KV Cache篇&#xff08;百倍提速&#xff09; 注意&#xff1a;KV Cache本质上是空间换时间的技术。与计算机组成原理中的cache不同&#xff0c;它不涉及访存优化。 不知道大家在用LLM的时候&#xff0c;有没有注意到一个问题&#xff1a;我们在输入我们的问题…

vscode搭建rust开发环境

由于rustrover不是免费的&#xff0c;此处教学搭建一套基于vscode的rust开发环境&#xff0c;可运行&#xff0c;可调式 1.下载vscode1.91.1 Download Visual Studio Code - Mac, Linux, Windows 2.下载插件 打开网站下载插件 rust-analyzer-0.4.2049、vscode-lldb-1.10.0、…

java使用hutool工具判断ip或者域名是否可用,java使用ping判断ip或者域名是否可用

1.导入hutool的maven依赖 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.16</version></dependency>2.复制以下代码直接运行 import cn.hutool.core.net.NetUtil;public class …

论文解读:DiAD之SG网络

目录 一、SG网络功能介绍二、SG网络代码实现 一、SG网络功能介绍 DiAD论文最主要的创新点就是使用SG网络解决多类别异常检测中的语义信息丢失问题&#xff0c;那么它是怎么实现的保留原始图像语义信息的同时重建异常区域&#xff1f; 与稳定扩散去噪网络的连接&#xff1a; S…

将 magma example 改写成 cusolver example eqrf

1&#xff0c;简单安装Magma 1.1 下载编译 OpenBLAS $ git clone https://github.com/OpenMathLib/OpenBLAS.git $ cd OpenBLAS/ $ make -j DEBUG1 $ make install PREFIX/home/hipper/ex_magma/local_d/OpenBLAS/1.2 下载编译 magma $ git clone https://bitbucket.org/icl…

【Kubernetes】二进制部署k8s集群(中)之cni网络插件flannel和calico

&#xff01;&#xff01;&#xff01;继续上一篇实验部署&#xff01;&#xff01;&#xff01; 目录 一.k8s的三种网络模式 1.Pod 内容器与容器之间的通信 2.同一个 Node 内 Pod 之间的通信 3.不同 Node 上 Pod 之间的通信 二.k8s的三种接口 三.Flannel 网络插件 1.U…