Hash、HASHTABLE底层原理【Redis对象篇】

1.hash是什么?

Redis Hash是一个field、value都为string的hash表,存储在Redis的内存中。

2.适用场景

适用于O(1)时间字典查找某个field对应数据的场景,比如任务信息的配置,就可以任务类型为field,任务配置参数为value。

3.常用操作

  • 创建:HSET、HSETNX
  • 查询:HGETALL、HGET、HLEN、HSCAN
  • 更新:HSET、HSETNX、HDEL
  • 删除:DEL

1.写操作

1、HSET,为集合对应field设置value数据。字段+值。
HSET key field value [field value …]
[图片]

[图片]

2、HSETNX,如果field不存在,则为集合对应设置value数据。如果存在则不设置。
[图片]

3、HDEL,删除指定字段field,可以一次删除多个。
4、DEL,删除Hash对象。
5、HMSET,可以设置多个键值对。在Redis4.0之前,HSET只能设置单个键值对,4.0之后,弃用HMSET,改用HSET。

2.读操作

1、HGETALL,查找全部数据。
[图片]

2、HGET,查询field对应的value。
3、HLEN,查找Hash中元素总数。
4、HSCAN,从指定位置查询一定数量的数据。

4.原理

Hash底层有两种编码结构,压缩列表和HASHTABLE。同时满足以下两个条件,用压缩列表:

  1. Hash对象保存的所有值和键的长度都小于64字节;
  2. Hash对象元素个数少于512个。
    两个条件任何一条不满足,编码结构就用HASHTABLE。
    ZIPLIST其实就是在数据量小的时候将数据紧凑排列,对应到Hash,就是将field-value当做entry放入ZIPLIST。查找key的时间复杂度O(N)。
    [图片]

HASHTABLE在之前无序集合SET中也有应用,区别就是,在SET中value始终为null,但是Hash中是有对应的值。查找key的时间复杂度O(1)。
[图片]

5.总结

1、Hash的编码方式是什么?
一个是ZIPLIST,一个是HASHTABLE。ZIPLIST适用于元素较少且单个元素长度较小的情况,其他情况使用HASHTABLE。

2、HASH为什么要用两种编码方式?
采用两种编码方式的原因是ZIPLIST更节约内存,所以在小数据量使用,而数据多时,需要使用HASHTABLE提高更高的查找、更新性能。

HASTABLE

别,这个模块还没结束呢。学了SET和HASH之后,我们都见到了底层有一个叫HASHTABLE的结构,接下来就去探究一下这是个啥。

1.HASHTABLE简述

简单点说,就是哈希表。那么有什么用呢?
就好比一本书,如果让你一页一页去找是不是很麻烦,要是有一个目录可以直接根据关键字就能定位,是不是效率就更高了。

2.HASHTABLE结构

// redis 5.0.5
typedef struct dictht {
    dictEntry **table;    /* 哈希桶数组,指向实际的hash存储 */
    unsigned long size;   /* 哈希表大小(桶数) */
    unsigned long sizemask; /* 哈希表大小掩码 */
    unsigned long used;   /* 哈希表已使用的桶数量 */
} dictht;

[图片]

3.渐进式扩容,缩容

// redis 5.0.5
typedef struct dict {
    dictht ht[2];         /* 目前使用的两个哈希表(用于rehash) */
    dictType *type;       /* 数据类型 */
    void *privdata;       /* 私有数据(通常为 NULL),保存需要传给那些类型特定函数的可选参数*/
    long rehashidx;       /* 正在进行的 rehash 操作的桶索引 */
    unsigned long iterators; /* 迭代器数量 */
} dict;

为了实现渐进式扩容,redis没有直接把dictht暴露给上层,而是再封装一层,如上。
可以看到dict结构里面,包含了两个dictht结构,也就是两个HASHTABLE结构。dictEntry是链表结构,也就是用拉链法解决哈希冲突,用的头插法。
[图片]

实际上平时用的都是一个HASHTABLE,在触发扩容之后,就会两个HASHTABLE同时使用,以下是详细流程:

  1. 首先,为新Hash表ht[1]分配空间。新表大小为第一个大于等于原表2倍used(已使用的桶数量)的2次方幂。然后迁移ht[0]数据到ht[1]。在ReHash(是指重新计算键的哈希值和索引值)进行期间,每次对字典执行增删改查操作,程序会顺带迁移当前rehashidx在ht[0]上对应的数据,并更新偏移索引。同时,部分情况周期函数也会进行迁移。(这里解释一下这个rehashidx是什么意思:字典同时是拥有ht[0]和ht[1],将rehashidx设置为0,表示rehash开始;在rehash期间,每次对字典crud,会顺带将ht[0]哈希表在rehashidx索引上的所有kv rehash到ht[1],当rehash完成后rehashidx+1;随着字典不断操作,最终ht[0]所有键值都会被rehash到ht[1],这时将rehashidx设置为-1,表示操作结束。注意:在渐进式rehash的过程,如果有crud,如果index大于rehashidx,访问ht[0],否则访问ht[1]。)
  2. 然后,随着字典不断执行,最终在某个时间点上,ht[0]的所有键值都会被Rehash至ht[1],此时再将ht[1]和ht[0]指针对象互换,同时把偏移索引rehashidx的值设为-1,表示Rehash已完成。
    既然知道了扩容的流程,那么扩容时机是什么时候呢?
    redis会根据负载因子的情况来扩容:
  3. 负载因子大于等于1,说明此时空间已经非常紧张。
  4. 负载因子大于5,此时即使有复制命令,也要进行Rehash扩容。
    负载因子:k=ht[0].used / ht[0].size
    如果扩容太大,但是数据已经减少了,就需要进行缩容,缩容也是渐进式的。那么什么时机缩容呢?
    当负载因子小于0.1,即负载率小于10%,此时进行缩容,新表大小为第一个等于原表used的2次方幂。

总之,ZIPLIST、HASHTABLE面试超级热点,不仅学习这些大致思路,还要掌握一些细节。

在这里插入图片描述
在这里插入图片描述

### 解决Content Security Policy (CSP) img-src 'self' data: base64 错误 当遇到 `img-src` CSP 指令违反策略的问题时,通常是由于网页尝试加载不符合指定来源的图像资源引起的。以下是可能的原因以及解决方案: #### 原因分析 1. **CSP 策略定义不足** 当前 CSP 中设置了 `img-src 'self'`,这意味着只有来自同一源(即当前域名)的图片才能被加载。如果页面中有通过 `data:` 或者其他外部 URL 加载的图片,则会被阻止[^1]。 2. **Base64 图像嵌入问题** 使用 Base64 编码的内联图像是一种常见的优化手段,但如果未显式允许 `data:` 协议,在 CSP 下这些图像将无法正常显示。 3. **动态生成的内容冲突** 动态注入 HTML 内容可能导致意外行为,尤其是当内容中包含不受信任的数据时,这可能会触发 CSP 警告或错误。 --- #### 解决方案 ##### 方法一:扩展 `img-src` 定义范围 修改 `<meta>` 标签中的 CSP 策略以支持更多类型的图像来源。例如: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; img-src 'self' data:;"> ``` 上述配置表示除了允许同源的图片外,还允许基于 Base64 的编码图像加载。 ##### 方法二:调整服务器端响应头 如果 CSP 是由 HTTP 头部设置而非 `<meta>` 标签控制,则需更新 Web 服务器配置文件。对于 Nginx 和 Apache 分别有以下示例: - **Nginx** ```nginx add_header Content-Security-Policy "default-src 'self'; script-src 'self'; img-src 'self' data;"; ``` - **Apache** ```apache Header set Content-Security-Policy "default-src 'self'; script-src 'self'; img-src 'self' data;" ``` 以上更改确保浏览器能够识别并接受 Base64 数据作为合法的图像来源[^1]。 ##### 方法三:移除违规代码片段 检查前端代码是否存在非法调用第三方资源的情况。比如某些框架库自动插入了跨域请求或者使用了未经许可的协议加载媒体资产。清理掉不必要的部分有助于减少潜在风险。 ##### 方法四:启用报告机制捕获异常事件 为了更好地监控实际运行状况,建议开启 CSP Reports 功能收集反馈日志以便后续改进措施制定。 ```html <meta http-equiv="Content-Security-Policy" content="... report-uri /csp-report-endpoint"> ``` 此操作会把每次发生的违禁访问记录提交至指定 URI 地址供管理员审查处理。 --- ### 示例代码展示 下面给出一段简单的 PHP 页面演示如何正确应用增强后的 CSP 设置防止类似问题再次发生。 ```php <?php header('Content-Security-Policy: default-src \'self\'; script-src \'self\'; img-src \'self\' data:'); ?> <!DOCTYPE html> <html lang="en"> <head> <title>CSP Example</title> </head> <body> <!-- 合法本地图片 --> <img src="/images/example.jpg"> <!-- 合法base64编码图片 --> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." alt="Embedded Image"> <h1>Secure Page Loaded Successfully!</h1> </body> </html> ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大华Coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值