【架构设计(二)】高可用、高并发的 Java 架构设计

【架构设计(二)】高可用、高并发的 Java 架构设计

在互联网业务爆发式增长的今天,高可用和高并发已成为 Java 系统架构设计的核心目标。本文将围绕负载均衡与高可用架构、缓存设计与优化、数据库读写分离与分库分表三大关键领域,深入剖析其原理,并结合完整的代码示例,帮助开发者构建稳定高效的系统架构。

一、负载均衡与高可用架构

1.1 负载均衡实现方案

负载均衡是高并发系统的基础,它通过将请求均匀分配到多个服务节点,避免单点过载。

1.1.1 Nginx 负载均衡配置示例

Nginx 作为高性能反向代理服务器,常用于 HTTP 层负载均衡。在nginx.conf的核心配置中,upstream模块定义了服务集群,采用least_conn(最少连接数)策略,将请求优先分配给连接数最少的节点。同时配置了健康检查health_check,每 3 秒检查一次节点状态,3 次失败则剔除节点,2 次成功重新启用,确保流量始终导向健康服务。

# nginx.conf 核心配置​
upstream java_service {​
    # 轮询策略​
    least_conn;​
    # 服务节点配置​
    server 192.168.1.101:8080 weight=10;​
    server 192.168.1.102:8080 weight=10;​
    server 192.168.1.103:8080 weight=10;​
    ​
    # 健康检查配置​
    health_check interval=3000 fail=3 pass=2;}​
​
server {​
    listen 80;​
    server_name java_service.com;​
    ​
    location / {​
        proxy_pass http://java_service;​
        proxy_set_header Host $host;​
        proxy_set_header X-Real-IP $remote_addr;​
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}}

总结:Nginx 适用于七层负载均衡,通过简单配置即可实现流量分发与健康管理,是 Web 服务负载均衡的常用方案。

1.1.2 Spring Cloud Ribbon 负载均衡代码

在微服务架构中,Ribbon 提供客户端负载均衡能力。通过LoadBalancerClient接口,服务消费者可从注册中心获取服务实例,并根据配置策略选择节点。示例中配置了RandomRule随机策略,也可切换为RoundRobinRule轮询或WeightedResponseTimeRule权重策略。

Maven依赖​
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId></dependency>
配置类
@Configurationpublic class RibbonConfig {// 自定义负载均衡策略​
    @Beanpublic IRule ribbonRule() {// 随机策略​
        return new RandomRule();// 轮询策略:return new RoundRobinRule();​
        // 权重策略:return new WeightedResponseTimeRule();​
    }}
服务调用示例
@Servicepublic class UserService {@Autowiredprivate LoadBalancerClient loadBalancer;​
    ​
    @Autowiredprivate RestTemplate restTemplate;​
    ​
    public User getUserById(Long id) {// 从注册中心获取服务实例​
        ServiceInstance instance = loadBalancer.choose("user-service");String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/users/" + id;return restTemplate.getForObject(url, User.class);}}
启动类配置
@SpringBootApplication@EnableDiscoveryClientpublic class Application {@Bean@LoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}​
    ​
    public static void main(String[] args) {SpringApplication.run(Application.class, args);}}
总结:Ribbon 实现了客户端负载均衡,与 Spring Cloud 生态无缝集成,适合微服务内部的服务调用场景。

1.2 高可用架构设计

高可用架构旨在确保系统在部分组件故障时仍能持续提供服务。​

1.2.1 服务注册与发现实现(Nacos)

Nacos 作为服务注册中心,服务提供者通过配置spring.cloud.nacos.discovery将自身注册到 Nacos Server,消费者则通过DiscoveryClient从注册中心获取服务实例列表。示例中,order-service在启动时注册自身,调用user-service时先从 Nacos 获取实例,再进行远程调用。

Maven依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
配置文件 application.yml
spring:​
  application:name: order-service​
  cloud:​
    nacos:​
      discovery:server-addr: nacos-server:8848​
        # 健康检查配置​
        health-check-enabled: true​
        # 服务权重​
        weight: 10
服务提供者示例
@RestController@RequestMapping("/orders")public class OrderController {@GetMapping("/{id}")public Order getOrderById(@PathVariable Long id) {// 业务逻辑​
        return orderService.getOrderById(id);}}
服务消费者通过Nacos发现服务
@Servicepublic class OrderService {@Autowiredprivate DiscoveryClient discoveryClient;​
    ​
    public Order getOrderDetail(Long orderId) {// 获取用户服务实例​
        List<ServiceInstance> instances = discoveryClient.getInstances("user-service");if (instances.isEmpty()) {throw new ServiceNotFoundException("用户服务不可用");}// 随机选择一个实例​
        ServiceInstance instance = instances.get(new Random().nextInt(instances.size()));// 调用用户服务​
        String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/users/" + order.getUserId();User user = restTemplate.getForObject(url, User.class);// 组装订单详情​
        return buildOrderDetail(order, user);}}
总结:Nacos 简化了服务注册与发现流程,提供健康检查、权重配置等功能,是微服务架构中保障服务高可用的关键组件。
1.2.2 断路器实现(Sentinel)

Sentinel通过熔断、限流机制保护系统。示例中,OrderService的getOrderDetail方法添加了@SentinelResource注解,指定了fallback熔断降级方法和blockHandler限流阻断方法。当服务调用失败或触发限流规则时,自动执行相应逻辑,避免雪崩效应。

Maven依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>
配置文件 application.yml
spring:​
  cloud:​
    sentinel:​
      transport:# Sentinel控制台地址​
        dashboard: sentinel-dashboard:8080​
      datasource:# 限流规则配置(从Nacos获取)​
        flow-rule:​
          nacos:server-addr: nacos-server:8848​
            dataId: ${spring.application.name}-flow-rules​
            groupId: SENTINEL_GROUP​
            data-type: json
服务熔断示例
@Servicepublic class OrderService {)public OrderDetail getOrderDetail(Long orderId) {// 正常业务逻辑​
        Order order = orderDao.getOrderById(orderId);User user = userService.getUserById(order.getUserId());return new OrderDetail(order, user);}​
    ​
    // 熔断降级方法​
    public OrderDetail getOrderDetailFallback(Long orderId, Throwable ex) {​
        log.error("获取订单详情失败,订单ID:{}", orderId, ex);// 返回兜底数据​
        return new OrderDetail();}​
    ​
    // 限流阻断方法​
    public OrderDetail getOrderDetailBlockHandler(Long orderId, BlockException ex) {​
        log.warn("获取订单详情被限流,订单ID:{}", orderId);// 返回限流提示​
        throw new ServiceBlockedException("服务繁忙,请稍后再试");}}
总结:Sentinel 提供可视化控制台和灵活的规则配置,有效提升系统的容错能力和稳定性。

二、缓存设计与优化

2.1 缓存架构设计

缓存通过存储热点数据减少数据库压力,提升系统响应速度。

2.1.1 多级缓存架构实现

多级缓存结合了本地缓存(Caffeine)的快速访问和分布式缓存(Redis)的共享能力。MultiLevelCacheImpl类实现了三级查询逻辑:优先从本地 Caffeine 缓存获取,未命中则查询 Redis,最后从数据库加载并回写缓存。

多级缓存接口定义
public interface MultiLevelCache {// 从一级缓存(本地)获取​
    <T> T getFromFirstLevel(String key, Class<T> type);​
    ​
    // 从二级缓存(分布式)获取​
    <T> T getFromSecondLevel(String key, Class<T> type);​
    ​
    // 从数据库获取并填充缓存​
    <T> T getFromDBAndCache(String key, Class<T> type, Function<String, T> dbLoader);​
    ​
    // 清除缓存​
    void clear(String key);}
多级缓存实现类
@Servicepublic class MultiLevelCacheImpl implements MultiLevelCache {​
        ​
        // 再查二级缓存​
        result = getFromSecondLevel(key, type);if (result != null) {// 填充一级缓存​
            firstLevelCache.put(key, result);return result;}​
        ​
        // 查数据库​
        result = dbLoader.apply(key);if (result != null) {// 填充二级缓存​
            secondLevelCache.put(key, result);// 填充一级缓存​
            firstLevelCache.put(key, result);}return result;}​
    ​
    @Overridepublic void clear(String key) {​
        firstLevelCache.clear(key);​
        secondLevelCache.clear(key);}}
本地缓存实现(Caffeine)
@Servicepublic class CaffeineCache {private final com.github.benmanes.caffeine.cache.Cache<String, Object> cache;​
    ​
    public CaffeineCache() {this.cache = Caffeine.newBuilder().maximumSize(10000)        // 最大缓存条目数​
                .expireAfterWrite(10, TimeUnit.MINUTES)  // 写入后过期时间​
                .weakValues()             // 使用弱引用值​
                .build();}​
    ​
    public <T> T get(String key, Class<T> type) {return type.cast(cache.getIfPresent(key));}​
    ​
    public void put(String key, Object value) {​
        cache.put(key, value);}​
    ​
    public void clear(String key) {​
        cache.invalidate(key);}}
分布式缓存实现(Redis)
@Servicepublic class RedisCache {return null;}try {return (T) JSON.parseObject(value, type);} catch (Exception e) {​
            log.error("Redis缓存反序列化失败,key:{}", key, e);return null;}}​
    ​
    public void put(String key, Object value) {try {String jsonValue = JSON.toJSONString(value);// 缓存10分钟​
            stringRedisTemplate.opsForValue().set(key, jsonValue, 10, TimeUnit.MINUTES);} catch (Exception e) {​
            log.error("Redis缓存序列化失败,key:{}", key, e);}}​
    ​
    public void clear(String key) {​
        stringRedisTemplate.delete(key);}}
总结:多级缓存架构平衡了性能与一致性,适用于读多写少的场景,有效降低数据库负载。

2.2 缓存核心策略实现

2.2.1 缓存穿透解决方案(布隆过滤器)

缓存穿透指恶意请求频繁查询不存在的数据,导致请求直达数据库。布隆过滤器通过概率性数据结构快速判断数据是否存在,示例中初始化时加载所有存在的用户 ID,后续请求先经布隆过滤器预检,避免无效数据库查询。

Maven依赖
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.1-jre</version></dependency>
布隆过滤器实现
@Servicepublic class BloomFilterService {​
        log.info("开始初始化布隆过滤器...");// 从数据库获取所有存在的key​
        List<String> existKeys = userDao.getAllExistUserIds();for (String key : existKeys) {​
            bloomFilter.put(key);}​
        log.info("布隆过滤器初始化完成,共加载 {} 个键", existKeys.size());}​
    ​
    // 判断key是否存在​
    public boolean mightContain(String key) {return bloomFilter.mightContain(key);}​
    ​
    // 添加key到布隆过滤器​
    public void put(String key) {​
        bloomFilter.put(key);}}
应用示例
@Servicepublic class UserService {private UserDao userDao;​
    ​
    public User getUserById(String userId) {// 布隆过滤器预检,防止缓存穿透​
        if (!bloomFilterService.mightContain(userId)) {​
            log.warn("检测到缓存穿透攻击,userId:{}", userId);throw new IllegalArgumentException("用户不存在");}​
        ​
        // 多级缓存获取​
        return multiLevelCache.getFromDBAndCache("user:" + userId,User.class,​
                id -> userDao.getUserById(id));}​
    ​
    // 新增用户时更新布隆过滤器​
    public void addUser(User user) {​
        userDao.addUser(user);​
        bloomFilterService.put(user.getUserId());// 清除缓存​
        multiLevelCache.clear("user:" + user.getUserId());}}
总结:布隆过滤器以极小的空间代价大幅提升缓存防护能力,但存在误判可能,需结合业务容忍度使用。
2.2.2 缓存雪崩解决方案(Redisson 分布式锁)

缓存雪崩指大量缓存同时失效导致数据库瞬间压力激增。Redisson 分布式锁确保同一时间只有一个线程重建缓存,示例中lockAndExecuteWithRetry方法通过重试机制获取锁,避免线程饥饿。当线程获取锁失败时,会等待一段时间后再次尝试,最多重试指定次数,有效防止因锁竞争导致的请求堆积。

Maven依赖
<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.16.5</version></dependency>
分布式锁工具类
@Servicepublic class DistributedLockService {@Autowiredprivate RedissonClient redissonClient;// 加锁方法​
    public <T> T lockAndExecute(String lockKey, long timeout, TimeUnit unit,Callable<T> task) throws Exception {RLock lock = redissonClient.getLock(lockKey);try {// 尝试加锁,timeout为等待锁的时间,unit为时间单位​
            boolean locked = lock.tryLock(500, timeout, unit);if (locked) {// 获得锁后执行任务​
                return task.call();} else {// 未获得锁,直接从缓存获取​
                log.warn("获取锁失败,直接返回缓存数据,lockKey:{}", lockKey);// 这里可以添加从缓存获取数据的逻辑​
                return null;}} finally {// 确保释放锁​
            if (lock.isHeldByCurrentThread()) {​
                lock.unlock();}}// 带重试的加锁方法​
    public <T> T lockAndExecuteWithRetry(String lockKey, long timeout, TimeUnit unit,int retryTimes, long retryInterval, TimeUnit retryUnit,Callable<T> task) throws Exception {int attempts = 0;while (attempts <= retryTimes) {try {return lockAndExecute(lockKey, timeout, unit, task);} catch (Exception e) {if (attempts >= retryTimes) {​
                    log.error("获取锁重试次数已用完,lockKey:{}", lockKey, e);throw e;}​
                log.warn("获取锁失败,准备重试,attempts:{},lockKey:{}", attempts, lockKey, e);​
                retryUnit.sleep(retryInterval);​
                attempts++;}}return null;}}
场景:

在电商大促场景下,商品详情页的缓存可能会在同一时间大量失效。此时,多个请求并发访问商品详情接口,如果没有分布式锁控制,这些请求都会直接查询数据库,导致数据库负载过高甚至崩溃。通过 Redisson 分布式锁,只有一个请求能获取到锁并重建缓存,其他请求要么获取缓存数据,要么等待重试,从而避免了缓存雪崩问题。

总结:

Redisson 分布式锁基于 Redis 实现,具有高可靠性和易用性,能有效应对缓存雪崩问题,保障系统在缓存失效时的稳定性。使用时需合理设置锁的超时时间、重试次数和重试间隔等参数,避免出现死锁、性能损耗以及因锁竞争导致的请求长时间等待等问题 。同时,它与 Spring 框架集成方便,适用于分布式环境下的多种并发控制场景,是解决缓存雪崩的重要技术手段之一。

三、数据库读写分离与分库分表

3.1 读写分离实现

读写分离是提升数据库性能的重要手段,通过将读操作和写操作分离到不同数据库,减少主库压力,提高系统吞吐量。

3.1.1 MyBatis Plus 多数据源配置

MyBatis Plus 结合dynamic-datasource-spring-boot-starter实现多数据源切换。在application.yml中配置多个数据源,包括主库master和从库slave1、slave2,通过dynamic.datasource.primary指定主数据源。通过自定义@DataSource注解和切面DataSourceAspect,在方法或类上标注注解即可实现数据源的动态切换。

数据源切换注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String value() default "";
}
切面实现数据源切换
@Aspect
@Service
public class DataSourceAspect {
    @Pointcut("@annotation(com.example.datasource.DataSource)")
    public void dataSourcePointCut() {}
    
    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        
        // 获取注解上的数据源名称
        DataSource dataSource = method.getAnnotation(DataSource.class);
        if (dataSource != null) {
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value());
        } else {
            // 类上获取数据源
            Class<?> declaringClass = method.getDeclaringClass();
            dataSource = declaringClass.getAnnotation(DataSource.class);
            if (dataSource != null) {
                DynamicDataSourceContextHolder.setDataSourceType(dataSource.value());
            }
        }
        
        try {
            return point.proceed();
        } finally {
            // 清除数据源
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }
}
数据源上下文Holder
public class DynamicDataSourceContextHolder {
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
    
    public static void setDataSourceType(String dataSourceType) {
        CONTEXT_HOLDER.set(dataSourceType);
    }
    
    public static String getDataSourceType() {
        return CONTEXT_HOLDER.get();
    }
    
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }
}

在DataSourceAspect切面类中,@Around环绕通知会在目标方法执行前后进行拦截。当检测到方法或类上有@DataSource注解时,会将对应的数据源名称设置到DynamicDataSourceContextHolder中,从而实现数据源的切换。在方法执行完毕后,清除当前线程绑定的数据源,避免影响其他方法的数据源选择。

总结:

MyBatis Plus 的多数据源配置灵活方便,能满足不同业务场景下读写分离需求,但需注意事务管理,避免跨数据源事务问题。在实际应用中,对于写操作频繁的业务方法,应确保使用主数据源;而读操作较多的查询方法,可选择从数据源以减轻主库压力。

3.2 分库分表实践

当数据量和访问量持续增长,单个数据库无法满足性能需求时,分库分表是有效的解决方案。

3.2.1 Sharding-JDBC 分库分表配置

Sharding-JDBC 是一款轻量级 Java 分库分表框架,无需额外中间件。在application.yml中配置数据源、分表规则和读写分离策略。actual-data-nodes定义了逻辑表order与实际数据库表的映射关系,table-strategy和database-strategy分别配置了表分片和库分片算法,示例中采用取模算法,根据order_id和user_id进行分片。

配置文件 application.yml
spring:
  shardingsphere:
    datasource:
      names: master_db,slave_db_0,slave_db_1
      
      master_db:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://master:3306/sharding_master?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
        
      slave_db_0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://slave0:3306/sharding_slave_0?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
        
      slave_db_1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://slave1:3306/sharding_slave_1?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
        
    sharding:
      tables:
        order:
          actual-data-nodes: master_db.order_${0..1},slave_db_${0..1}.order_${0..1}
          table-strategy:
            inline:
              sharding-column: order_id
              algorithm-expression: order_${order_id % 2}
          database-strategy:
            inline:
              sharding-column: user_id
              algorithm-expression: slave_db_${user_id % 2} if user_id is not null else master_db
          key-generator:
            column: order_id
            type: SNOWFLAKE
            
      binding-tables:
        - order,order_item
        
      default-database-strategy:
        inline:
          sharding-column: user_id
          algorithm-expression: master_db
          
      default-table-strategy:
        none:
        
    masterslave:
      load-balance-algorithm-type: round_robin
      names: master_db_slave
      
      master_db_slave:
        master-data-source-name: master_db
        slave-data-source-names: slave_db_0,slave_db_1

以订单表order为例,actual-data-nodes配置指定了逻辑表order对应多个实际数据库表,分布在主库和从库中。当执行插入或查询订单操作时,Sharding-JDBC 会根据order_id对 2 取模来确定具体的表,根据user_id对 2 取模(或默认使用主库)来确定具体的数据库。同时,key-generator配置使用雪花算法生成order_id,保证分布式环境下 ID 的唯一性和有序性。

总结:

Sharding-JDBC 通过配置化方式实现分库分表,对业务代码侵入性小,支持多种分片算法,但分片规则设计需谨慎,避免跨分片查询性能问题。在设计分片策略时,要充分考虑业务查询场景,尽量让常用查询操作能定位到单一分片,以提高查询效率。

3.2.2 分表键设计与分布式 ID 生成

分表键的选择直接影响分表效果,需根据业务查询场景确定。示例中使用雪花算法生成分布式 ID,ID 中可包含分表键信息,如取 ID 低 10 位作为分表键。
SnowflakeIdGenerator类通过位运算生成唯一 ID,确保在分布式环境下的唯一性和有序性。

// 雪花算法ID生成器
public class SnowflakeIdGenerator {
    // 起始的时间戳
    private final long startEpoch = 1577808000000L; // 2020-01-01 00:00:00
    
    // 服务器ID所占的位数
    private final long workerIdBits = 5L;
    
    // 数据中心ID所占的位数
    private final long datacenterIdBits = 5L;
    
    // 支持的最大服务器ID,结果为31 (2^5 - 1)
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    
    // 支持的最大数据中心ID,结果为31 (2^5 - 1)
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    
    // 序列号所占的位数
    private final long sequenceBits = 12L;
    
    // 服务器ID左移的位数
    private final long workerIdShift = sequenceBits;
    
    // 数据中心ID左移的位数
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    
    // 时间戳左移的位数
    private final long timestampShift = sequenceBits + workerIdBits + datacenterIdBits;
    
    // 序列号的掩码,用于防止序列号溢出
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    
    // 工作机器ID
    private long workerId;
    
    // 数据中心ID
    private long datacenterId;
    
    // 序列号
    private long sequence = 0L;
    
    // 上次生成ID的时间戳
    private long lastTimestamp = -1L;
    
    public SnowflakeIdGenerator(long workerId, long datacenterId) {
        // 校验工作机器ID和数据中心ID是否超出范围
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }
    
    // 生成下一个ID
    public synchronized long nextId() {
        long timestamp = getCurrentTimeStamp();
        
        // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回拨过,抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        
        // 如果是同一时间生成的,则进行序列号自增
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & sequenceMask;
            // 同一毫秒内序列号溢出,等待到下一毫秒
            if (sequence == 0) {
                timestamp = waitNextMillis(lastTimestamp);
            }
        } else {
            // 不同时间,序列号重置为0
            sequence = 0L;
        }
        
        // 保存当前时间戳为最后一次生成ID的时间戳
        lastTimestamp = timestamp;
        
        // 生成ID:(时间戳-起始时间戳)<<22 | 数据中心ID<<17 | 工作机器ID<<12 | 序列号
        return ((timestamp - startEpoch) << timestampShift) |
                (datacenterId << datacenterIdShift) |
                (workerId << workerIdShift) |
                sequence;
    }
    
    // 获取当前时间戳
    private long getCurrentTimeStamp() {
        return System.currentTimeMillis();
    }
    
    // 等待到下一毫秒
    private long waitNextMillis(long lastTimestamp) {
        long timestamp = getCurrentTimeStamp();
        while (timestamp <= lastTimestamp) {
            timestamp = getCurrentTimeStamp();
        }
        return timestamp;
    }
}

雪花算法生成的 ID 由时间戳、数据中心 ID、工作机器 ID 和序列号四部分组成。在生成 ID 时,首先获取当前时间戳,若与上次生成 ID 的时间戳相同,则递增序列号;若序列号溢出,则等待到下一毫秒重新计数。通过这种方式,保证了生成的 ID 在分布式环境下的唯一性和大致有序性。将 ID 的部分位作为分表键,既能满足分表需求,又能利用 ID 的有序性提高查询效率,例如按时间顺序查询数据时,数据会相对集中在某些分表中。

总结:

合理的分表键设计和分布式 ID 生成策略,能有效提升分库分表后的数据查询效率和系统扩展性,但需考虑 ID 生成的性能和 ID 与分表键的关联逻辑。在实际应用中,要根据业务特点和数据增长趋势,选择合适的分表键和 ID 生成算法,并对 ID 生成器进行性能测试和优化,确保其在高并发场景下稳定可靠运行。

相关资料已更新
关注公众号:回复:资料领取,即可获取全部面试题以及1000+份学习资料

Java-Base64算法_创新_防止表单重复提交 JAVA企业级基础课题(HashMap那些事) 企业架构师必备技能(JAVA核心技术反射) JavaWeb之基础(手写实现Tomcat服务器) java多线程编程 纯手写实现SpringIOC实现过程 JEE企业级开发(企业级项目开发权威指南) 网络爬虫之JAVA正则表达式 手写springMVC框架 老司机带你透析springMVC内部实现方式 打造高效代码结构(java性能优化) 新版本通俗易懂_观察者模式递进时讲解 ibatis连接数据库 高并发之单()生产者消费者线程 高并发复用数据库链接技术详解之数据库连接池 类加载器的高级特性(自定义类加器实现加密解密) iBATIS开源主流框架(实现半自动化hibernate) 企业实用技能之详解(眼睛横纹模式验证码防止恶意登陆) 动态页面的静态化处理 图片上传技术 在springMVC中实现原始的Excel文件下载方式 企业级分布式缓存技术之(redis详解) 企业高并发基石(Tomcat服务器性能优化) spring事务处理 课程文档 高并发之基础数据MySql调优 mongodb 三级联动课程资料 应用架构之灵魂设计模式 应用架构之魂设计模式实战演练应用架构之魂设计模式实战演练 揭开springAOP神秘面纱(动态代理) Mysql性能优化之索引优化 写实现Tomcat服务器 移动后台端框架设计 公司级框架原理解析 解密公司内部框架开发(打造属于自己的专属框架) 手写Tomca之深度解析动态资源请求原理 深度解析springMVC实现原理(手写springMVC框架) Java验证码 正则黑名单爬虫系统 深入数据库连接池内部运转原理 分布式服务下的交易一致 企业必备技能之面向服务编程Web-Service详解 分布式服务下的交易一致性原理及解决 分布式服务框架(dubbo+zookpeer) WEB高级前后台分离思维-懒加载无限级树形菜单 动态页面的静态化处理 大并发展示优化,动态页面的静态化 深入理解JDK动态代理本质 企业级高并发缓存解决方案 性能优化之Oracle语句优化雾区 前后台数据验证架构源码级解析 session跨域共享 JAVANIO原理详解 高并发数据库(Mysql数据库性能优化) 软件质量管控 企业常用框架springMVC基于注解+xml配置方式实现链接 WEB服务器优化之Tomcat7性能调优 JVM概述 Java开发技术之(项目工程的日志管理) 数据库连接池原理详解 Java企业级框架之核心技术(反射) Java-Base64算法(创新_防止表单重复提交) 揭开springAOP神秘面纱之动态代理 网络爬虫之JAVA正则表达式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值