static List<Object>
未清理)。-XX:PretenureSizeThreshold
参数设置的对象直接分配在老年代,导致老年代空间不足。System.gc()
System.gc()
触发 Full GC(除非通过-XX:+DisableExplicitGC
禁用)。特性 | G1(JDK 7+) | ZGC(JDK 11+) |
---|---|---|
核心目标 | 平衡吞吐量与延迟,适用于大内存服务器 | 追求极致低延迟(<10ms),支持 TB 级内存 |
停顿时间 | 可预测的低延迟(目标 < 500ms) | 几乎无停顿(<10ms) |
内存规模 | 推荐 6-128GB | 支持 TB 级内存(实验性支持 4TB 以上) |
应用场景 | 企业级应用(如电商、金融) | 实时性要求极高的场景(如游戏服务器、高频交易) |
-XX:MaxGCPauseMillis
控制)。维度 | G1 | ZGC |
---|---|---|
最大停顿时间 | 通常 100-500ms(可配置) | 始终 < 10ms |
吞吐量影响 | 约 5-10%(并发阶段消耗 CPU) | 约 10-15%(染色指针和内存屏障开销) |
大对象处理 | Humongous Region 可能导致碎片化 | 分页机制更灵活,减少碎片化 |
内存占用 | 需要 Remembered Sets 和 Card Tables | 需要染色指针和转发表(Forward Table) |
GC 线程数 | 自动调整(可通过-XX:ParallelGCThreads控制) | 通常需要更多线程(默认-XX:ConcGCThreads=1/4*ParallelGCThreads) |
调优建议:
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200 # 目标最大停顿时间
-XX:InitiatingHeapOccupancyPercent=45 # 老年代占用45%时触发GC
-XX:G1HeapRegionSize=8m # 调整Region大小
调优建议:
-XX:+UseZGC
-XX:MaxHeapSize=4g # 根据实际需求设置
-XX:ConcGCThreads=4 # 并发GC线程数
-XX:ZCollectionInterval=1000 # GC周期(毫秒)五
维度 | 优先选择 G1 | 优先选择 ZGC |
---|---|---|
堆内存规模 | 6-128GB | >256GB |
延迟要求 | 可接受 100-500ms 停顿 | 必须 < 10ms |
应用类型 | 企业级应用(Web、RPC 服务) | 实时系统(游戏、高频交易) |
JDK 版本 | JDK 8/11(长期支持) | JDK 11+(需评估稳定性) |
吞吐量敏感度 | 对吞吐量下降敏感(<5%) | 可接受 10-15% 的吞吐量损失 |
monitorenter
和monitorexit
指令实现,编译后会在同步块前后插入这两个指令。// 示例代码
public void synchronizedMethod() {
synchronized (this) {
// 同步代码块
}
}
// 对应的字节码片段
monitorenter // 进入同步块
// 业务逻辑
monitorexit // 正常退出同步块
monitorexit // 异常退出同步块(通过Exception table触发)
state
表示锁状态(0 表示未锁定,1 表示已锁定),并维护一个 FIFO 队列存储等待线程。hasQueuedPredecessors()
方法判断队列中是否有前驱线程。// 非公平锁获取锁逻辑
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) { // CAS操作获取锁
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { // 可重入
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
特性 | synchronized | ReentrantLock |
---|---|---|
锁获取方式 | 隐式获取 / 释放(JVM 自动处理) | 显式获取 / 释放(需手动调用lock()和unlock()) |
可重入性 | 支持(同一线程可多次获取同一锁) | 支持(通过state计数) |
公平性 | 非公平锁 | 可配置公平 / 非公平(默认非公平) |
中断响应 | 不支持(线程会一直等待) | 支持(lockInterruptibly()方法) |
超时机制 | 不支持 | 支持(tryLock(long timeout, TimeUnit unit)) |
条件变量 | 单一wait()/notify() | 多个Condition对象(更灵活的等待 / 通知机制) |
场景 | synchronized | ReentrantLock |
---|---|---|
单线程 / 无竞争 | 偏向锁模式,几乎无开销 | 需 CAS 操作,开销略高 |
轻度竞争 | 轻量级锁,性能接近 | 需维护 AQS 队列,开销略高 |
激烈竞争 | 重量级锁,依赖内核 Mutex,性能下降 | 可通过公平锁减少线程切换,性能更稳定 |
锁粒度 | 适合代码块或方法级锁 | 适合更细粒度的锁(如读写锁) |
场景 | synchronized | ReentrantLock |
---|---|---|
简单同步 | 优先选择(代码简洁,JVM 自动优化) | 非必要不使用 |
公平锁需求 | 无法实现 | 可配置new ReentrantLock(true) |
中断响应 / 超时 | 无法实现 | 必须使用(如线程池 Worker 中断) |
多条件变量 | 需嵌套synchronized块 | 直接使用多个Condition对象 |
锁粒度控制 | 粗粒度(方法 / 代码块) | 细粒度(如读写锁分离) |
synchronized
:
ReentrantLock
的场景:
synchronized
进行了大量优化,在无竞争或轻度竞争下性能与ReentrantLock
接近。ReentrantLock
的可配置性可能带来优势。corePoolSize
):线程池长期保持的线程数量。maximumPoolSize
):线程池允许的最大线程数量。workQueue
):任务队列的大小,影响任务缓冲能力。keepAliveTime
):超过核心线程数的空闲线程的存活时间。Java 的ThreadPoolExecutor
提供了以下动态调整方法:
java
public class DynamicThreadPool {
private final ThreadPoolExecutor executor;
public DynamicThreadPool() {
executor = new ThreadPoolExecutor(
10, // 初始核心线程数
100, // 初始最大线程数
60, TimeUnit.SECONDS, // 空闲线程存活时间
new LinkedBlockingQueue<>(1000) // 任务队列
);
}
// 动态调整核心线程数
public void setCorePoolSize(int corePoolSize) {
executor.setCorePoolSize(corePoolSize);
}
// 动态调整最大线程数
public void setMaximumPoolSize(int maximumPoolSize) {
executor.setMaximumPoolSize(maximumPoolSize);
}
// 动态调整队列容量(需自定义队列)
public void setQueueCapacity(int capacity) {
if (executor.getQueue() instanceof ResizableBlockingQueue) {
((ResizableBlockingQueue) executor.getQueue()).setCapacity(capacity);
}
}
}
java
public class ResizableBlockingQueue<E> extends LinkedBlockingQueue<E> {
private final AtomicInteger capacity;
public ResizableBlockingQueue(int capacity) {
super(capacity);
this.capacity = new AtomicInteger(capacity);
}
public void setCapacity(int newCapacity) {
int oldCapacity = capacity.getAndSet(newCapacity);
// 可添加队列大小调整的额外逻辑
}
@Override
public int remainingCapacity() {
return capacity.get() - size();
}
}
java
public class AutoScalingThreadPool {
private final ScheduledExecutorService monitorService;
private final ThreadPoolExecutor executor;
public AutoScalingThreadPool() {
executor = new ThreadPoolExecutor(10, 100, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000));
monitorService = Executors.newSingleThreadScheduledExecutor();
// 每5秒监控一次
monitorService.scheduleAtFixedRate(this::monitorAndAdjust, 0, 5, TimeUnit.SECONDS);
}
private void monitorAndAdjust() {
// 获取当前线程池状态
int activeCount = executor.getActiveCount();
int queueSize = executor.getQueue().size();
long completedTasks = executor.getCompletedTaskCount();
// 根据负载调整参数(示例策略)
if (queueSize > 800) { // 队列接近满时
executor.setCorePoolSize(Math.min(executor.getCorePoolSize() + 5, 100));
} else if (activeCount < 5 && executor.getCorePoolSize() > 10) { // 负载低时
executor.setCorePoolSize(Math.max(executor.getCorePoolSize() - 5, 10));
}
}
public void shutdown() {
monitorService.shutdown();
executor.shutdown();
}
}
将线程池参数存储在配置中心(如 Nacos、Apollo),实现实时动态调整:
java
public class ConfigCenterThreadPool {
private final ThreadPoolExecutor executor;
private final ConfigService configService; // 配置中心客户端
public ConfigCenterThreadPool() {
// 初始化线程池,使用配置中心的初始值
int coreSize = getConfig("threadPool.coreSize", 10);
int maxSize = getConfig("threadPool.maxSize", 100);
int queueSize = getConfig("threadPool.queueSize", 1000);
executor = new ThreadPoolExecutor(coreSize, maxSize, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(queueSize));
// 注册配置变更监听器
configService.addListener("threadPool.properties", this::onConfigChanged);
}
private void onConfigChanged(ConfigChangeEvent event) {
if (event.isChanged("threadPool.coreSize")) {
int newCoreSize = event.getNewValue("threadPool.coreSize");
executor.setCorePoolSize(newCoreSize);
}
if (event.isChanged("threadPool.maxSize")) {
int newMaxSize = event.getNewValue("threadPool.maxSize");
executor.setMaximumPoolSize(newMaxSize);
}
if (event.isChanged("threadPool.queueSize") &&
executor.getQueue() instanceof ResizableBlockingQueue) {
int newQueueSize = event.getNewValue("threadPool.queueSize");
((ResizableBlockingQueue<?>) executor.getQueue()).setCapacity(newQueueSize);
}
}
private int getConfig(String key, int defaultValue) {
// 从配置中心获取值,带默认值
return configService.getIntProperty(key, defaultValue);
}
}
getActiveCount()
):当前正在执行任务的线程数量。getQueue().size()
):等待执行的任务数量。getCompletedTaskCount()
):已完成的任务总数。RejectedExecutionHandler
的调用次数。maximumPoolSize
时需同步考虑队列容量,避免任务堆积或系统过载。java
// 线程池预热示例
public void warmup() {
for (int i = 0; i < executor.getCorePoolSize(); i++) {
executor.execute(() -> {
// 执行空任务或轻量级任务
});
}
}
ThreadPoolExecutor
实现简单的动态调整。设计动态调整线程池参数需结合业务场景和系统特性,遵循以下原则:
java
// 服务元数据模型
public class ServiceInstance {
private String serviceId; // 服务唯一标识
private String instanceId; // 实例唯一标识
private String host; // 主机IP
private int port; // 端口
private boolean secure; // 是否安全通信
private Map<String, String> metadata; // 自定义元数据
private long registrationTime; // 注册时间
private HealthStatus status; // 健康状态
// getters/setters
}
// 健康状态枚举
public enum HealthStatus {
UP, DOWN, STARTING, OUT_OF_SERVICE, UNKNOWN
}
java
// 服务注册接口
public interface ServiceRegistry {
void register(ServiceInstance instance); // 注册服务
void deregister(String instanceId); // 注销服务
void heartbeat(String instanceId); // 发送心跳
}
// 服务发现接口
public interface ServiceDiscovery {
List<ServiceInstance> getInstances(String serviceId); // 获取服务实例列表
List<String> getServices(); // 获取所有服务ID
}
// 基于ZooKeeper的实现示例
public class ZookeeperServiceRegistry implements ServiceRegistry, ServiceDiscovery {
private final CuratorFramework client;
private static final String BASE_PATH = "/services";
public ZookeeperServiceRegistry(String zkConnectString) {
this.client = CuratorFrameworkFactory.newClient(
zkConnectString,
15000, // 会话超时
5000, // 连接超时
new ExponentialBackoffRetry(1000, 3) // 重试策略
);
client.start();
}
@Override
public void register(ServiceInstance instance) {
String path = getServicePath(instance.getServiceId()) + "/" + instance.getInstanceId();
try {
client.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL) // 临时节点,会话断开自动删除
.forPath(path, toJsonBytes(instance));
} catch (Exception e) {
throw new RuntimeException("Failed to register service", e);
}
}
// 其他方法实现...
}
java
// 健康检查器接口
public interface HealthChecker {
HealthStatus check(ServiceInstance instance); // 检查服务健康状态
}
// 基于HTTP的健康检查实现
public class HttpHealthChecker implements HealthChecker {
private final RestTemplate restTemplate;
private static final int TIMEOUT = 3000; // 超时时间3秒
public HttpHealthChecker() {
this.restTemplate = new RestTemplate();
// 配置超时
ClientHttpRequestFactory factory = restTemplate.getRequestFactory();
if (factory instanceof SimpleClientHttpRequestFactory) {
((SimpleClientHttpRequestFactory) factory).setConnectTimeout(TIMEOUT);
((SimpleClientHttpRequestFactory) factory).setReadTimeout(TIMEOUT);
}
}
@Override
public HealthStatus check(ServiceInstance instance) {
try {
String healthUrl = buildHealthUrl(instance);
ResponseEntity<String> response = restTemplate.getForEntity(healthUrl, String.class);
return response.getStatusCode().is2xxSuccessful() ? HealthStatus.UP : HealthStatus.DOWN;
} catch (Exception e) {
return HealthStatus.DOWN;
}
}
private String buildHealthUrl(ServiceInstance instance) {
String scheme = instance.isSecure() ? "https" : "http";
return scheme + "://" + instance.getHost() + ":" + instance.getPort() + "/actuator/health";
}
}
特性 | ZooKeeper | Consul | Etcd | Nacos |
---|---|---|---|---|
一致性协议 | Zab | Raft | Raft | CP+AP |
健康检查 | 需自定义 | 支持 HTTP/TCP/gRPC | 需自定义 | 支持多种协议 |
多数据中心 | 支持 | 原生支持 | 不支持 | 支持 |
KV 存储 | 支持 | 支持 | 支持 | 支持 |
配置管理 | 需自定义 | 支持 | 支持 | 原生支持 |
生态系统 | 成熟 | 丰富 | K8s 生态为主 | 阿里系生态 |
以下是一个基于 Netty 和 ZooKeeper 的简化版服务注册中心实现:
java
// 服务注册中心主类
public class ServiceRegistryCenter {
private final ServiceRegistry registry;
private final ServiceDiscovery discovery;
private final HealthChecker healthChecker;
private final EventExecutorGroup bossGroup;
private final EventExecutorGroup workerGroup;
private Channel serverChannel;
public ServiceRegistryCenter(String zkConnectString, int port) {
this.registry = new ZookeeperServiceRegistry(zkConnectString);
this.discovery = new ZookeeperServiceDiscovery(zkConnectString);
this.healthChecker = new HttpHealthChecker();
// 初始化Netty服务器
bossGroup = new NioEventLoopGroup();
workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new LengthFieldBasedFrameDecoder(1024, 0, 4, 0, 4),
new LengthFieldPrepender(4),
new JsonDecoder(),
new JsonEncoder(),
new RegistryServerHandler(registry, discovery)
);
}
});
// 启动服务器
try {
ChannelFuture future = bootstrap.bind(port).sync();
serverChannel = future.channel();
System.out.println("Service Registry Center started on port " + port);
// 启动健康检查定时任务
ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(1);
scheduledExecutor.scheduleAtFixedRate(this::checkServicesHealth, 30, 30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void checkServicesHealth() {
List<String> serviceIds = discovery.getServices();
for (String serviceId : serviceIds) {
List<ServiceInstance> instances = discovery.getInstances(serviceId);
for (ServiceInstance instance : instances) {
HealthStatus status = healthChecker.check(instance);
if (status != instance.getStatus()) {
instance.setStatus(status);
// 更新服务状态
registry.updateStatus(instance.getInstanceId(), status);
System.out.println("Service " + instance.getInstanceId() + " status changed to " + status);
}
}
}
}
public void shutdown() {
if (serverChannel != null) {
serverChannel.close();
}
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
public static void main(String[] args) {
new ServiceRegistryCenter("localhost:2181", 8888);
}
}
RocketMQ 采用主从架构,每个 Broker 组包含一个 Master 和多个 Slave,通过同步复制或异步复制实现高可用。
// 同步发送示例(确保消息发送成功)
SendResult result = producer.send(msg, 3000); // 超时时间3秒
if (result.getSendStatus() == SendStatus.SEND_OK) {
// 消息发送成功
}
配置重试次数(默认 2 次):
producer.setRetryTimesWhenSendFailed(3); // 失败重试3次
TransactionMQProducer producer = new TransactionMQProducer("group");
producer.setTransactionListener(new TransactionListener() {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
// 执行本地事务
return LocalTransactionState.COMMIT_MESSAGE;
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
// 事务状态回查
return LocalTransactionState.COMMIT_MESSAGE;
}
});
# broker.conf配置
flushDiskType = SYNC_FLUSH # 同步刷盘,确保消息写入磁盘后才返回成功
# broker.conf配置
brokerRole = SYNC_MASTER # 同步双写模式
flushDiskType = SYNC_FLUSH
// 消费者配置
consumer.setMessageModel(MessageModel.CLUSTERING); // 集群模式
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
try {
// 处理消息
processMessage(msgs);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; // 手动提交
} catch (Exception e) {
return ConsumeConcurrentlyStatus.RECONSUME_LATER; // 失败重试
}
}
});
// 幂等消费示例(基于业务ID去重)
public void processMessage(MessageExt msg) {
String bizId = msg.getUserProperty("bizId");
if (redisClient.sismember("processed_msgs", bizId)) {
return; // 已处理,直接返回
}
// 处理业务逻辑
processBiz(msg);
// 标记为已处理
redisClient.sadd("processed_msgs", bizId);
}
java
// 推荐配置
DefaultMQProducer producer = new DefaultMQProducer("producer_group");
producer.setNamesrvAddr("nameserver:9876");
producer.setRetryTimesWhenSendFailed(3); // 同步发送失败重试3次
producer.setRetryTimesWhenSendAsyncFailed(3); // 异步发送失败重试3次
producer.setSendMsgTimeout(5000); // 发送超时5秒
properties
# broker.conf推荐配置
brokerRole = SYNC_MASTER # 同步双写模式
flushDiskType = SYNC_FLUSH # 同步刷盘
deleteWhen = 04 # 凌晨4点删除过期文件
fileReservedTime = 48 # 保留48小时
messageStorePlugIn = com.xxx.MyStorePlugin # 可选:自定义存储插件增强可靠性
java
// 推荐配置
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group");
consumer.setNamesrvAddr("nameserver:9876");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET); // 从最后位置消费
consumer.setConsumeThreadMin(20); // 最小消费线程数
consumer.setConsumeThreadMax(50); // 最大消费线程数
consumer.setMaxReconsumeTimes(16); // 最大重试次数
配置项 | 可靠性高 | 性能高 |
---|---|---|
刷盘策略 | SYNC_FLUSH(同步刷盘) | ASYNC_FLUSH(异步刷盘) |
主从复制 | SYNC_MASTER(同步双写) | ASYNC_MASTER(异步复制) |
发送模式 | 同步发送 + 重试 | 异步发送 |
消费确认 | 手动提交位点 | 自动提交位点 |
建议:
RocketMQ 通过以下机制保障消息零丢失:
使用 Lua 脚本原子性地删除锁:
lua
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
确保仅删除自己持有的锁,避免误删。
fsync=always
,确保锁信息写入磁盘,但性能显著下降。方案 | 一致性 | 可用性 | 性能 | 实现复杂度 |
---|---|---|---|---|
单节点 Redis | 低(单点故障) | 低 | 高 | 简单 |
Redlock | 中等 | 中等 | 中 | 复杂 |
ZooKeeper | 高(CP) | 高 | 低 | 中等 |
MySQL | 高(需事务) | 中等 | 低 | 简单 |
java
public class RedlockExample {
private static final int N = 5; // 节点数
private final List<Jedis> jedisClients;
private static final String LOCK_KEY = "distributed_lock";
private static final int LOCK_EXPIRE_MS = 10000; // 锁有效期10秒
public RedlockExample() {
// 初始化5个Redis节点
jedisClients = Arrays.asList(
new Jedis("redis1", 6379),
new Jedis("redis2", 6379),
new Jedis("redis3", 6379),
new Jedis("redis4", 6379),
new Jedis("redis5", 6379)
);
}
public boolean acquireLock(String requestId) {
int acquiredNodes = 0;
long startTime = System.currentTimeMillis();
// 尝试锁定所有节点
for (Jedis client : jedisClients) {
try {
// 使用SETNX + PX原子操作
String result = client.set(LOCK_KEY, requestId, "NX", "PX", LOCK_EXPIRE_MS);
if ("OK".equals(result)) {
acquiredNodes++;
}
} catch (Exception e) {
// 忽略异常,继续尝试其他节点
}
}
// 计算获取锁耗时
long elapsedTime = System.currentTimeMillis() - startTime;
// 检查是否成功锁定多数节点,且耗时小于锁有效期
return acquiredNodes >= (N / 2 + 1) && elapsedTime < LOCK_EXPIRE_MS;
}
public void releaseLock(String requestId) {
// 使用Lua脚本释放锁,确保原子性
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) else return 0 end";
for (Jedis client : jedisClients) {
try {
client.eval(script, Collections.singletonList(LOCK_KEY),
Collections.singletonList(requestId));
} catch (Exception e) {
// 忽略异常,继续释放其他节点
}
}
}
}
最终结论:Redlock 在理想条件下提供了较高可靠性,但无法达到 ZooKeeper 等 CP 系统的强一致性保障,需根据场景权衡使用。
B + 树索引和 Hash 索引是数据库中两种核心索引结构,分别适用于不同的查询场景。以下从数据结构、性能特性、适用场景三个维度展开对比:
非叶子节点: [10, 20, 30]
/ | | \
叶子节点: [1, 5, 8] [12, 15, 18] [22, 25, 28] [32, 35, 38]
Hash Table:
[0] -> (key1, value1) -> (key4, value4)
[1] -> (key2, value2)
[2] -> (key3, value3)
维度 | B + 树索引 | Hash 索引 |
---|---|---|
等值查询 | 时间复杂度 O (log n),需遍历树 | 时间复杂度 O (1),直接定位哈希桶 |
范围查询 | 支持高效范围查询(通过叶子链表) | 不支持,需全表扫描 |
排序查询 | 支持,可利用索引有序性 | 不支持,需额外排序操作 |
插入 / 删除性能 | 需维护树平衡,开销较大 | 平均 O (1),冲突时可能退化 |
索引空间占用 | 较大(存储索引键和指针) | 较小(仅哈希表和冲突链表) |
索引维护成本 | 高(插入 / 删除可能触发树旋转) | 低(仅需处理哈希冲突) |
WHERE age BETWEEN 20 AND 30
。ORDER BY create_time DESC
。LIKE 'abc%'
(基于索引有序性)。WHERE id = 123
。CREATE INDEX idx_age ON users(age); -- B+树索引
维度 | B + 树索引 | Hash 索引 |
---|---|---|
优点 | 支持范围查询和排序 | 等值查询性能极高 |
索引有序,适合前缀匹配 | 插入 / 删除效率高 | |
稳定性好,不会因冲突退化 | 内存占用少 | |
缺点 | 等值查询性能低于 Hash 索引 | 不支持范围查询和排序 |
插入 / 删除开销大 | 哈希冲突可能影响性能 | |
索引空间占用大 | 不支持部分索引(如前缀索引) |
最终结论:B + 树索引是通用性最强的索引结构,而 Hash 索引在特定场景(如高频等值查询)下性能更优,需根据业务需求合理选择。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。