0%

Redis ziplist(压缩列表)实现详解(基于 6.0.10 版本)

ziplist(压缩列表)是 Redis 为节省内存设计的紧凑数据结构,通过连续内存块存储数据,避免了传统链表的指针开销。它并非通过 “压缩算法” 压缩数据,而是通过紧凑布局减少内存浪费,广泛用于 Hash、List、ZSet 等数据类型的底层实现(当数据量较小时)。

ziplist 核心设计目标

  • 节省内存:通过连续内存存储,消除链表节点的指针(prev/next)开销(传统双向链表每个节点需 16 字节指针)。
  • 支持双向遍历:通过特殊结构设计,既支持正向遍历,也能高效反向遍历。
  • 适配小数据:针对小尺寸元素(如短字符串、整数)优化,不适合存储大型数据或大量元素。

ziplist 整体结构

ziplist 由一系列连续的内存块组成,整体结构如下(单位:字节):

1
2
3
4
+--------+--------+--------+----------------+----------------+--------+
| zlbytes| zltail | zllen | entry 1 | entry 2 | zlend |
| (4B) | (4B) | (2B) | ... | ... | (1B) |
+--------+--------+--------+----------------+----------------+--------+

各字段含义:

字段名 长度(字节) 作用
zlbytes 4 记录 ziplist 总字节数(包括自身),用于快速计算内存大小和重分配。
zltail 4 记录表尾节点距离 ziplist 起始地址的偏移量(字节),支持快速定位尾节点(无需遍历)。
zllen 2 记录节点数量(entry 个数),最大值为 65535(2^16-1)。若超过此值,需遍历整个列表获取真实数量。
entry 可变 存储实际数据的节点(每个 entry 结构不同,见下文)。
zlend 1 标记列表结尾,固定值为 0xFF(255)。

entry 节点结构

每个 entry 是 ziplist 的数据单元,结构如下:

阅读全文 »

Git 底层剖析:深入理解 Git 的数据存储机制

Git 之所以高效且灵活,核心在于其独特的底层数据模型。不同于传统的版本控制系统(如 SVN)以 “差异” 存储版本,Git 以 “快照” 形式保存完整数据,并通过哈希值(SHA-1)唯一标识每个对象。本文将深入解析 Git 底层的核心文件和数据结构,揭开 HEADindexobjectsrefs 的神秘面纱。

Git 底层核心组件

Git 的所有版本数据和元信息都存储在仓库根目录的 .git 文件夹中,其中四个核心组件构成了其底层基础:

组件 作用
HEAD 特殊指针,指向当前检出的分支(或直接指向某个提交)。
index 暂存区(Staging Area)的快照,记录待提交文件的元数据(哈希、权限等)。
objects/ 存储所有版本数据(提交对象、树对象、数据对象)。
refs/ 存储分支、标签等引用的指针(指向对应的提交对象)。

objects 目录:Git 的 “数据库”

objects/ 是 Git 最核心的目录,所有版本数据都以 “对象” 形式存储于此。每个对象通过 SHA-1 哈希值 命名(40 位十六进制字符),前 2 位为目录名,后 38 位为文件名(如哈希 a1b2c3... 对应 objects/a1/b2c3...)。

Git 有三种基础对象类型,共同构成版本快照:

1. Blob 对象:存储文件内容

  • 作用:保存单个文件的原始内容(如代码、文本),与文件名无关(相同内容的文件共享同一个 blob)。
  • 特点:仅包含文件数据,不包含文件名、权限等元信息。
示例:创建并查看 blob 对象
阅读全文 »

Nginx 负载均衡配置详解:策略与实战

负载均衡是分布式系统应对高并发的核心手段,Nginx 通过upstream模块实现请求的分发,将流量均衡到多台后端服务器,提升系统吞吐量与可用性。本文详细讲解 Nginx 负载均衡的配置方法、核心策略及参数优化,帮助构建高可靠的服务集群。

负载均衡基础配置

Nginx 负载均衡的核心是通过upstream块定义后端服务器集群,再通过proxy_pass将请求转发到该集群。

基本架构

  • 前端:Nginx 作为负载均衡器(Director),接收客户端请求;
  • 后端:多台 Real Server(如127.0.0.1:8080127.0.0.1:8082),处理实际业务;
  • 配置逻辑upstream定义集群 → server块中通过proxy_pass关联集群。

基础配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. 定义后端服务器集群(http块内)
upstream myserver {
# 后端服务器列表(IP:端口)
server 127.0.0.1:8080;
server 127.0.0.1:8082;
}

# 2. 配置负载均衡入口(server块内)
server {
listen 9000; # Nginx监听端口(客户端访问端口)
server_name localhost;

location / {
proxy_pass http://myserver; # 转发到upstream定义的集群
# 传递客户端信息(可选但推荐)
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
  • 效果:客户端访问http://localhost:9000时,Nginx 会将请求分发到80808082端口的服务器。

后端服务器状态参数

upstream中可通过参数控制服务器的可用性、权重等,常用参数如下:

阅读全文 »

Java 中的哈希(Hash)机制:HashMap 与 Hashtable 的实现对比

哈希(Hash)是 Java 集合框架中实现高效存储和查询的核心机制,尤其是在 HashMapHashtable 中,哈希算法的设计直接影响哈希冲突的概率和性能。本文将深入解析两者的哈希计算方法、索引定位逻辑及优化策略,揭示其减少哈希碰撞的底层原理。

哈希的核心目标

哈希的核心是通过哈希函数将任意长度的输入(如对象的 hashCode)映射到固定长度的输出(如数组索引),目标是:

  1. 均匀分布:使键值对在数组中均匀分布,减少哈希冲突(不同键映射到同一索引);
  2. 高效计算:哈希函数和索引计算应简单快速,避免额外性能损耗。

HashMap 的哈希机制(JDK 1.8)

HashMap 采用 “二次哈希 + 与运算” 的方式,通过扰动函数增强哈希值的随机性,减少冲突。

哈希值计算(扰动函数)

HashMap 对键的原始哈希值(key.hashCode())进行二次处理,代码如下:

1
2
3
4
5
6
static final int hash(Object key) {
int h;
// 1. 若 key 为 null,哈希值为 0;否则计算 h = key.hashCode()
// 2. 高 16 位与低 16 位异或(h ^ (h >>> 16)),增强随机性
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
扰动的原因:
  • 原始 hashCode 是 32 位整数,但数组容量(n)通常较小(默认 16,最大 2^30),导致计算索引时仅低 log2(n) 位有效(高位被忽略)。
  • 例如:若两个键的 hashCode 高位不同但低位相同,直接使用原始哈希值会导致索引冲突。
  • 扰动通过 “高 16 位异或低 16 位”,将高位信息混入低位,增加低位的随机性,减少冲突。

索引计算(与运算)

通过扰动后的哈希值计算数组索引:

阅读全文 »

Nginx 反向代理配置详解:从基础到高级应用

反向代理是 Nginx 最核心的功能之一,通过将客户端请求转发到后端服务器(如应用服务器、静态资源服务器等),实现请求分发、隐藏真实服务地址、负载均衡等效果。本文详细讲解 Nginx 反向代理的配置方法、核心参数及不同场景的实战案例。

反向代理基础配置

Nginx 通过proxy_pass指令实现反向代理,核心是在location块中定义转发规则,将匹配的请求转发到指定的后端服务。

1. 最简单的反向代理

将所有请求转发到单个后端服务(如本地 8080 端口的 Java 应用):

1
2
3
4
5
6
7
8
9
server {
listen 80; # Nginx监听80端口
server_name example.com; # 绑定域名

# 匹配所有根路径请求
location / {
proxy_pass http://127.0.0.1:8080; # 转发到后端服务
}
}
  • 效果:客户端访问http://example.com时,Nginx 会将请求转发到http://127.0.0.1:8080,并将后端响应返回给客户端。

2. 按路径转发到不同服务

根据请求路径的不同,转发到不同的后端服务(如区分 API 服务和静态资源服务):

阅读全文 »