前言

分布式ID生成器是分布式系统中的核心组件之一,需要保证全局唯一性、高性能、高可用等特性。MySQL作为关系型数据库,虽然在高并发场景下存在性能瓶颈,但通过合理的架构设计和优化策略,仍然可以实现高性能的分布式ID生成器。本文从分布式ID设计到MySQL优化,从并发控制到企业级方案,系统梳理基于MySQL的分布式ID生成器完整解决方案。

一、分布式ID生成器架构设计

1.1 分布式ID生成器架构

1.2 分布式ID设计原则

二、MySQL分布式ID生成器实现

2.1 基于数据库自增ID方案

2.1.1 基础实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 创建ID生成表
CREATE TABLE id_generator (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
business_type VARCHAR(50) NOT NULL COMMENT '业务类型',
step_size INT NOT NULL DEFAULT 1000 COMMENT '步长',
current_max_id BIGINT NOT NULL DEFAULT 0 COMMENT '当前最大ID',
version INT NOT NULL DEFAULT 0 COMMENT '版本号',
created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
UNIQUE KEY uk_business_type (business_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='ID生成器表';

-- 插入初始数据
INSERT INTO id_generator (business_type, step_size, current_max_id) VALUES
('user', 1000, 0),
('order', 1000, 0),
('product', 1000, 0);

2.1.2 Java实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
* 基于MySQL的分布式ID生成器
*/
@Service
public class MySQLDistributedIdGenerator {

@Autowired
private IdGeneratorMapper idGeneratorMapper;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private final Map<String, IdRange> localCache = new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

/**
* 获取分布式ID
*/
public Long generateId(String businessType) {
// 1. 尝试从本地缓存获取
IdRange idRange = localCache.get(businessType);
if (idRange != null && idRange.hasNext()) {
return idRange.next();
}

// 2. 从Redis缓存获取
idRange = getFromRedisCache(businessType);
if (idRange != null && idRange.hasNext()) {
localCache.put(businessType, idRange);
return idRange.next();
}

// 3. 从数据库获取新的ID段
idRange = getNewIdRange(businessType);
localCache.put(businessType, idRange);

// 4. 异步更新Redis缓存
updateRedisCache(businessType, idRange);

return idRange.next();
}

/**
* 批量获取分布式ID
*/
public List<Long> generateIds(String businessType, int count) {
List<Long> ids = new ArrayList<>();

for (int i = 0; i < count; i++) {
ids.add(generateId(businessType));
}

return ids;
}

/**
* 从数据库获取新的ID段
*/
private IdRange getNewIdRange(String businessType) {
return idGeneratorMapper.getNewIdRange(businessType);
}

/**
* 从Redis缓存获取ID段
*/
private IdRange getFromRedisCache(String businessType) {
String cacheKey = "id_range:" + businessType;
return (IdRange) redisTemplate.opsForValue().get(cacheKey);
}

/**
* 更新Redis缓存
*/
private void updateRedisCache(String businessType, IdRange idRange) {
CompletableFuture.runAsync(() -> {
String cacheKey = "id_range:" + businessType;
redisTemplate.opsForValue().set(cacheKey, idRange, Duration.ofMinutes(30));
});
}

/**
* ID段类
*/
public static class IdRange {
private long start;
private long end;
private long current;

public IdRange(long start, long end) {
this.start = start;
this.end = end;
this.current = start;
}

public boolean hasNext() {
return current <= end;
}

public long next() {
if (!hasNext()) {
throw new IllegalStateException("ID段已用完");
}
return current++;
}

// getter和setter方法
public long getStart() { return start; }
public void setStart(long start) { this.start = start; }
public long getEnd() { return end; }
public void setEnd(long end) { this.end = end; }
public long getCurrent() { return current; }
public void setCurrent(long current) { this.current = current; }
}
}

2.1.3 MyBatis Mapper实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
* ID生成器Mapper
*/
@Mapper
public interface IdGeneratorMapper {

/**
* 获取新的ID段
*/
@Select("SELECT business_type, step_size, current_max_id FROM id_generator WHERE business_type = #{businessType}")
IdGeneratorInfo getById(@Param("businessType") String businessType);

/**
* 更新ID段(使用乐观锁)
*/
@Update("UPDATE id_generator SET current_max_id = current_max_id + step_size, version = version + 1, updated_time = NOW() WHERE business_type = #{businessType} AND version = #{version}")
int updateIdRange(@Param("businessType") String businessType, @Param("version") int version);

/**
* 获取新的ID段(使用悲观锁)
*/
@Select("SELECT business_type, step_size, current_max_id FROM id_generator WHERE business_type = #{businessType} FOR UPDATE")
IdGeneratorInfo getByIdForUpdate(@Param("businessType") String businessType);

/**
* 更新ID段(悲观锁)
*/
@Update("UPDATE id_generator SET current_max_id = current_max_id + step_size, updated_time = NOW() WHERE business_type = #{businessType}")
int updateIdRangeForUpdate(@Param("businessType") String businessType);

/**
* 获取新的ID段(原子操作)
*/
default MySQLDistributedIdGenerator.IdRange getNewIdRange(String businessType) {
// 使用乐观锁重试机制
int maxRetries = 3;
for (int i = 0; i < maxRetries; i++) {
IdGeneratorInfo info = getById(businessType);
if (info == null) {
throw new IllegalArgumentException("业务类型不存在: " + businessType);
}

long start = info.getCurrentMaxId() + 1;
long end = info.getCurrentMaxId() + info.getStepSize();

int updated = updateIdRange(businessType, info.getVersion());
if (updated > 0) {
return new MySQLDistributedIdGenerator.IdRange(start, end);
}

// 重试前等待一小段时间
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("线程被中断", e);
}
}

throw new RuntimeException("获取ID段失败,重试次数已用完");
}
}

2.2 基于数据库序列方案

2.2.1 序列表设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 创建序列表
CREATE TABLE sequence_generator (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
sequence_name VARCHAR(100) NOT NULL COMMENT '序列名称',
current_value BIGINT NOT NULL DEFAULT 0 COMMENT '当前值',
increment_by INT NOT NULL DEFAULT 1 COMMENT '步长',
max_value BIGINT NOT NULL DEFAULT 9223372036854775807 COMMENT '最大值',
min_value BIGINT NOT NULL DEFAULT 1 COMMENT '最小值',
cycle BOOLEAN NOT NULL DEFAULT FALSE COMMENT '是否循环',
cache_size INT NOT NULL DEFAULT 1000 COMMENT '缓存大小',
version INT NOT NULL DEFAULT 0 COMMENT '版本号',
created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
UNIQUE KEY uk_sequence_name (sequence_name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='序列生成器表';

-- 插入序列配置
INSERT INTO sequence_generator (sequence_name, current_value, increment_by, cache_size) VALUES
('user_id_seq', 0, 1, 1000),
('order_id_seq', 0, 1, 1000),
('product_id_seq', 0, 1, 1000);

2.2.2 序列生成器实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/**
* 基于MySQL的序列生成器
*/
@Service
public class MySQLSequenceGenerator {

@Autowired
private SequenceMapper sequenceMapper;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private final Map<String, SequenceRange> localCache = new ConcurrentHashMap<>();

/**
* 获取下一个序列值
*/
public Long nextValue(String sequenceName) {
// 1. 尝试从本地缓存获取
SequenceRange range = localCache.get(sequenceName);
if (range != null && range.hasNext()) {
return range.next();
}

// 2. 从Redis缓存获取
range = getFromRedisCache(sequenceName);
if (range != null && range.hasNext()) {
localCache.put(sequenceName, range);
return range.next();
}

// 3. 从数据库获取新的序列段
range = getNewSequenceRange(sequenceName);
localCache.put(sequenceName, range);

// 4. 异步更新Redis缓存
updateRedisCache(sequenceName, range);

return range.next();
}

/**
* 批量获取序列值
*/
public List<Long> nextValues(String sequenceName, int count) {
List<Long> values = new ArrayList<>();

for (int i = 0; i < count; i++) {
values.add(nextValue(sequenceName));
}

return values;
}

/**
* 获取新的序列段
*/
private SequenceRange getNewSequenceRange(String sequenceName) {
return sequenceMapper.getNewSequenceRange(sequenceName);
}

/**
* 从Redis缓存获取序列段
*/
private SequenceRange getFromRedisCache(String sequenceName) {
String cacheKey = "sequence_range:" + sequenceName;
return (SequenceRange) redisTemplate.opsForValue().get(cacheKey);
}

/**
* 更新Redis缓存
*/
private void updateRedisCache(String sequenceName, SequenceRange range) {
CompletableFuture.runAsync(() -> {
String cacheKey = "sequence_range:" + sequenceName;
redisTemplate.opsForValue().set(cacheKey, range, Duration.ofMinutes(30));
});
}

/**
* 序列段类
*/
public static class SequenceRange {
private long start;
private long end;
private long current;
private int incrementBy;

public SequenceRange(long start, long end, int incrementBy) {
this.start = start;
this.end = end;
this.current = start;
this.incrementBy = incrementBy;
}

public boolean hasNext() {
return current <= end;
}

public long next() {
if (!hasNext()) {
throw new IllegalStateException("序列段已用完");
}
long value = current;
current += incrementBy;
return value;
}

// getter和setter方法
public long getStart() { return start; }
public void setStart(long start) { this.start = start; }
public long getEnd() { return end; }
public void setEnd(long end) { this.end = end; }
public long getCurrent() { return current; }
public void setCurrent(long current) { this.current = current; }
public int getIncrementBy() { return incrementBy; }
public void setIncrementBy(int incrementBy) { this.incrementBy = incrementBy; }
}
}

2.3 基于数据库表方案

2.3.1 分段表设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 创建分段表
CREATE TABLE id_segment (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
business_type VARCHAR(50) NOT NULL COMMENT '业务类型',
segment_no INT NOT NULL COMMENT '分段号',
start_id BIGINT NOT NULL COMMENT '起始ID',
end_id BIGINT NOT NULL COMMENT '结束ID',
current_id BIGINT NOT NULL COMMENT '当前ID',
status TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0-未使用,1-使用中,2-已用完',
created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
UNIQUE KEY uk_business_segment (business_type, segment_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='ID分段表';

-- 创建分段索引
CREATE INDEX idx_business_status ON id_segment (business_type, status);
CREATE INDEX idx_current_id ON id_segment (current_id);

2.3.2 分段生成器实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/**
* 基于MySQL的分段ID生成器
*/
@Service
public class MySQLSegmentIdGenerator {

@Autowired
private IdSegmentMapper idSegmentMapper;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private final Map<String, SegmentInfo> localCache = new ConcurrentHashMap<>();

/**
* 获取分段ID
*/
public Long generateId(String businessType) {
// 1. 尝试从本地缓存获取
SegmentInfo segment = localCache.get(businessType);
if (segment != null && segment.hasNext()) {
return segment.next();
}

// 2. 从Redis缓存获取
segment = getFromRedisCache(businessType);
if (segment != null && segment.hasNext()) {
localCache.put(businessType, segment);
return segment.next();
}

// 3. 从数据库获取新的分段
segment = getNewSegment(businessType);
localCache.put(businessType, segment);

// 4. 异步更新Redis缓存
updateRedisCache(businessType, segment);

return segment.next();
}

/**
* 获取新的分段
*/
private SegmentInfo getNewSegment(String businessType) {
return idSegmentMapper.getAvailableSegment(businessType);
}

/**
* 从Redis缓存获取分段
*/
private SegmentInfo getFromRedisCache(String businessType) {
String cacheKey = "segment:" + businessType;
return (SegmentInfo) redisTemplate.opsForValue().get(cacheKey);
}

/**
* 更新Redis缓存
*/
private void updateRedisCache(String businessType, SegmentInfo segment) {
CompletableFuture.runAsync(() -> {
String cacheKey = "segment:" + businessType;
redisTemplate.opsForValue().set(cacheKey, segment, Duration.ofMinutes(30));
});
}

/**
* 分段信息类
*/
public static class SegmentInfo {
private long segmentId;
private long startId;
private long endId;
private long currentId;

public SegmentInfo(long segmentId, long startId, long endId, long currentId) {
this.segmentId = segmentId;
this.startId = startId;
this.endId = endId;
this.currentId = currentId;
}

public boolean hasNext() {
return currentId <= endId;
}

public long next() {
if (!hasNext()) {
throw new IllegalStateException("分段已用完");
}
return currentId++;
}

// getter和setter方法
public long getSegmentId() { return segmentId; }
public void setSegmentId(long segmentId) { this.segmentId = segmentId; }
public long getStartId() { return startId; }
public void setStartId(long startId) { this.startId = startId; }
public long getEndId() { return endId; }
public void setEndId(long endId) { this.endId = endId; }
public long getCurrentId() { return currentId; }
public void setCurrentId(long currentId) { this.currentId = currentId; }
}
}

三、MySQL性能优化策略

3.1 数据库优化

3.1.1 索引优化

1
2
3
4
5
6
7
8
9
10
11
12
13
-- 优化索引设计
-- 1. 主键索引(自动创建)
-- 2. 唯一索引
CREATE UNIQUE INDEX uk_business_type ON id_generator (business_type);

-- 3. 复合索引
CREATE INDEX idx_business_version ON id_generator (business_type, version);

-- 4. 覆盖索引
CREATE INDEX idx_business_current ON id_generator (business_type, current_max_id, step_size);

-- 5. 分区索引(按业务类型分区)
ALTER TABLE id_generator PARTITION BY HASH(business_type) PARTITIONS 4;

3.1.2 查询优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
-- 优化查询语句
-- 1. 使用覆盖索引
SELECT business_type, current_max_id, step_size, version
FROM id_generator
WHERE business_type = 'user';

-- 2. 使用FOR UPDATE锁定
SELECT business_type, current_max_id, step_size, version
FROM id_generator
WHERE business_type = 'user'
FOR UPDATE;

-- 3. 使用原子更新
UPDATE id_generator
SET current_max_id = current_max_id + step_size,
version = version + 1,
updated_time = NOW()
WHERE business_type = 'user'
AND version = ?;

-- 4. 批量操作
INSERT INTO id_generator (business_type, step_size, current_max_id) VALUES
('user', 1000, 0),
('order', 1000, 0),
('product', 1000, 0)
ON DUPLICATE KEY UPDATE
current_max_id = VALUES(current_max_id),
updated_time = NOW();

3.1.3 连接池优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# application.yml
spring:
datasource:
hikari:
# 连接池配置
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000

# 性能优化
leak-detection-threshold: 60000
connection-test-query: SELECT 1

# 事务优化
auto-commit: true
transaction-isolation: READ_COMMITTED

# 缓存优化
cache-prep-stmts: true
prep-stmt-cache-size: 250
prep-stmt-cache-sql-limit: 2048

3.2 并发控制优化

3.2.1 乐观锁实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* 乐观锁ID生成器
*/
@Service
public class OptimisticLockIdGenerator {

@Autowired
private IdGeneratorMapper idGeneratorMapper;

private final int MAX_RETRIES = 3;

/**
* 使用乐观锁获取ID段
*/
public IdRange getNewIdRangeWithOptimisticLock(String businessType) {
for (int i = 0; i < MAX_RETRIES; i++) {
try {
// 1. 获取当前版本信息
IdGeneratorInfo info = idGeneratorMapper.getById(businessType);
if (info == null) {
throw new IllegalArgumentException("业务类型不存在: " + businessType);
}

// 2. 计算新的ID段
long start = info.getCurrentMaxId() + 1;
long end = info.getCurrentMaxId() + info.getStepSize();

// 3. 使用乐观锁更新
int updated = idGeneratorMapper.updateIdRange(businessType, info.getVersion());
if (updated > 0) {
return new IdRange(start, end);
}

// 4. 更新失败,等待后重试
Thread.sleep(10 + i * 10); // 递增等待时间

} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("线程被中断", e);
}
}

throw new RuntimeException("获取ID段失败,重试次数已用完");
}
}

3.2.2 悲观锁实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 悲观锁ID生成器
*/
@Service
public class PessimisticLockIdGenerator {

@Autowired
private IdGeneratorMapper idGeneratorMapper;

/**
* 使用悲观锁获取ID段
*/
@Transactional
public IdRange getNewIdRangeWithPessimisticLock(String businessType) {
// 1. 使用FOR UPDATE锁定记录
IdGeneratorInfo info = idGeneratorMapper.getByIdForUpdate(businessType);
if (info == null) {
throw new IllegalArgumentException("业务类型不存在: " + businessType);
}

// 2. 计算新的ID段
long start = info.getCurrentMaxId() + 1;
long end = info.getCurrentMaxId() + info.getStepSize();

// 3. 更新ID段
int updated = idGeneratorMapper.updateIdRangeForUpdate(businessType);
if (updated == 0) {
throw new RuntimeException("更新ID段失败");
}

return new IdRange(start, end);
}
}

3.2.3 分布式锁实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* 分布式锁ID生成器
*/
@Service
public class DistributedLockIdGenerator {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private IdGeneratorMapper idGeneratorMapper;

private final String LOCK_PREFIX = "id_generator_lock:";
private final int LOCK_TIMEOUT = 30; // 秒

/**
* 使用分布式锁获取ID段
*/
public IdRange getNewIdRangeWithDistributedLock(String businessType) {
String lockKey = LOCK_PREFIX + businessType;
String lockValue = UUID.randomUUID().toString();

try {
// 1. 获取分布式锁
boolean acquired = acquireLock(lockKey, lockValue, LOCK_TIMEOUT);
if (!acquired) {
throw new RuntimeException("获取分布式锁失败");
}

// 2. 获取ID段
IdGeneratorInfo info = idGeneratorMapper.getById(businessType);
if (info == null) {
throw new IllegalArgumentException("业务类型不存在: " + businessType);
}

long start = info.getCurrentMaxId() + 1;
long end = info.getCurrentMaxId() + info.getStepSize();

// 3. 更新ID段
int updated = idGeneratorMapper.updateIdRangeForUpdate(businessType);
if (updated == 0) {
throw new RuntimeException("更新ID段失败");
}

return new IdRange(start, end);

} finally {
// 4. 释放分布式锁
releaseLock(lockKey, lockValue);
}
}

/**
* 获取分布式锁
*/
private boolean acquireLock(String lockKey, String lockValue, int timeout) {
return redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, Duration.ofSeconds(timeout));
}

/**
* 释放分布式锁
*/
private void releaseLock(String lockKey, String lockValue) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(lockKey), lockValue);
}
}

3.3 缓存优化

3.3.1 多级缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* 多级缓存ID生成器
*/
@Service
public class MultiLevelCacheIdGenerator {

@Autowired
private IdGeneratorMapper idGeneratorMapper;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private final Map<String, IdRange> localCache = new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

/**
* 多级缓存获取ID
*/
public Long generateId(String businessType) {
// 1. 本地缓存
IdRange range = localCache.get(businessType);
if (range != null && range.hasNext()) {
return range.next();
}

// 2. Redis缓存
range = getFromRedisCache(businessType);
if (range != null && range.hasNext()) {
localCache.put(businessType, range);
return range.next();
}

// 3. 数据库
range = getNewIdRange(businessType);
localCache.put(businessType, range);

// 4. 异步更新缓存
updateCacheAsync(businessType, range);

return range.next();
}

/**
* 从Redis缓存获取
*/
private IdRange getFromRedisCache(String businessType) {
String cacheKey = "id_range:" + businessType;
return (IdRange) redisTemplate.opsForValue().get(cacheKey);
}

/**
* 异步更新缓存
*/
private void updateCacheAsync(String businessType, IdRange range) {
CompletableFuture.runAsync(() -> {
// 更新Redis缓存
String cacheKey = "id_range:" + businessType;
redisTemplate.opsForValue().set(cacheKey, range, Duration.ofMinutes(30));

// 预热下一个ID段
preloadNextIdRange(businessType);
});
}

/**
* 预热下一个ID段
*/
private void preloadNextIdRange(String businessType) {
try {
IdRange nextRange = getNewIdRange(businessType);
String cacheKey = "id_range:" + businessType;
redisTemplate.opsForValue().set(cacheKey, nextRange, Duration.ofMinutes(30));
} catch (Exception e) {
log.warn("预热ID段失败: {}", e.getMessage());
}
}

/**
* 定期清理本地缓存
*/
@PostConstruct
public void startCacheCleanup() {
scheduler.scheduleAtFixedRate(() -> {
localCache.entrySet().removeIf(entry -> !entry.getValue().hasNext());
}, 5, 5, TimeUnit.MINUTES);
}
}

3.3.2 缓存预热

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
* 缓存预热服务
*/
@Service
public class CacheWarmupService {

@Autowired
private IdGeneratorMapper idGeneratorMapper;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

/**
* 预热缓存
*/
@PostConstruct
public void warmupCache() {
// 获取所有业务类型
List<String> businessTypes = idGeneratorMapper.getAllBusinessTypes();

// 并行预热
businessTypes.parallelStream().forEach(businessType -> {
try {
warmupBusinessType(businessType);
} catch (Exception e) {
log.error("预热业务类型失败: {}", businessType, e);
}
});
}

/**
* 预热单个业务类型
*/
private void warmupBusinessType(String businessType) {
// 获取ID段
IdRange range = getNewIdRange(businessType);

// 存储到Redis
String cacheKey = "id_range:" + businessType;
redisTemplate.opsForValue().set(cacheKey, range, Duration.ofMinutes(30));

log.info("预热业务类型完成: {}", businessType);
}

/**
* 定期预热
*/
@Scheduled(fixedRate = 300000) // 5分钟
public void scheduledWarmup() {
// 检查缓存使用情况
checkCacheUsage();

// 预热低使用率的缓存
warmupLowUsageCache();
}

/**
* 检查缓存使用情况
*/
private void checkCacheUsage() {
// 实现缓存使用情况检查逻辑
}

/**
* 预热低使用率缓存
*/
private void warmupLowUsageCache() {
// 实现低使用率缓存预热逻辑
}
}

四、高可用架构设计

4.1 主从复制架构

4.1.1 主从配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 主库配置
[mysqld]
server-id = 1
log-bin = mysql-bin
binlog-format = ROW
sync-binlog = 1
innodb-flush-log-at-trx-commit = 1

-- 从库配置
[mysqld]
server-id = 2
relay-log = mysql-relay-bin
read-only = 1
slave-skip-errors = 1062,1032

4.1.2 读写分离

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/**
* 读写分离ID生成器
*/
@Service
public class ReadWriteSplitIdGenerator {

@Autowired
@Qualifier("masterDataSource")
private DataSource masterDataSource;

@Autowired
@Qualifier("slaveDataSource")
private DataSource slaveDataSource;

@Autowired
private IdGeneratorMapper idGeneratorMapper;

/**
* 写操作使用主库
*/
@Transactional
public IdRange getNewIdRange(String businessType) {
// 使用主库进行写操作
return idGeneratorMapper.getNewIdRange(businessType);
}

/**
* 读操作使用从库
*/
public IdGeneratorInfo getById(String businessType) {
// 使用从库进行读操作
return idGeneratorMapper.getById(businessType);
}

/**
* 健康检查
*/
public boolean checkHealth() {
try {
// 检查主库
boolean masterHealthy = checkDataSourceHealth(masterDataSource);

// 检查从库
boolean slaveHealthy = checkDataSourceHealth(slaveDataSource);

return masterHealthy && slaveHealthy;
} catch (Exception e) {
log.error("健康检查失败", e);
return false;
}
}

/**
* 检查数据源健康状态
*/
private boolean checkDataSourceHealth(DataSource dataSource) {
try (Connection connection = dataSource.getConnection()) {
return connection.isValid(5);
} catch (SQLException e) {
return false;
}
}
}

4.2 集群架构

4.2.1 分片策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* 分片ID生成器
*/
@Service
public class ShardedIdGenerator {

@Autowired
private List<IdGeneratorMapper> shardMappers;

@Autowired
private ShardRouter shardRouter;

/**
* 分片获取ID
*/
public Long generateId(String businessType) {
// 1. 计算分片
int shardIndex = shardRouter.getShardIndex(businessType);

// 2. 获取对应的Mapper
IdGeneratorMapper mapper = shardMappers.get(shardIndex);

// 3. 获取ID段
IdRange range = mapper.getNewIdRange(businessType);

return range.next();
}

/**
* 分片路由器
*/
@Component
public static class ShardRouter {

private final int shardCount;

public ShardRouter(@Value("${id.generator.shard.count:4}") int shardCount) {
this.shardCount = shardCount;
}

/**
* 计算分片索引
*/
public int getShardIndex(String businessType) {
return Math.abs(businessType.hashCode()) % shardCount;
}
}
}

4.2.2 负载均衡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
* 负载均衡ID生成器
*/
@Service
public class LoadBalancedIdGenerator {

@Autowired
private List<IdGeneratorService> idGeneratorServices;

@Autowired
private LoadBalancer loadBalancer;

/**
* 负载均衡获取ID
*/
public Long generateId(String businessType) {
// 1. 选择服务实例
IdGeneratorService service = loadBalancer.select(idGeneratorServices);

// 2. 获取ID
return service.generateId(businessType);
}

/**
* 负载均衡器
*/
@Component
public static class LoadBalancer {

private final AtomicInteger counter = new AtomicInteger(0);

/**
* 轮询选择
*/
public IdGeneratorService select(List<IdGeneratorService> services) {
int index = counter.getAndIncrement() % services.size();
return services.get(index);
}

/**
* 随机选择
*/
public IdGeneratorService selectRandom(List<IdGeneratorService> services) {
int index = ThreadLocalRandom.current().nextInt(services.size());
return services.get(index);
}

/**
* 加权轮询选择
*/
public IdGeneratorService selectWeighted(List<IdGeneratorService> services, List<Integer> weights) {
// 实现加权轮询逻辑
return services.get(0);
}
}
}

4.3 故障转移

4.3.1 故障检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* 故障检测服务
*/
@Service
public class FailureDetectionService {

@Autowired
private List<IdGeneratorService> idGeneratorServices;

private final Map<String, HealthStatus> healthStatus = new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

/**
* 启动故障检测
*/
@PostConstruct
public void startFailureDetection() {
scheduler.scheduleAtFixedRate(this::detectFailures, 0, 30, TimeUnit.SECONDS);
}

/**
* 检测故障
*/
private void detectFailures() {
idGeneratorServices.forEach(service -> {
String serviceId = service.getServiceId();
try {
// 健康检查
boolean healthy = service.healthCheck();
updateHealthStatus(serviceId, healthy);
} catch (Exception e) {
updateHealthStatus(serviceId, false);
log.warn("服务健康检查失败: {}", serviceId, e);
}
});
}

/**
* 更新健康状态
*/
private void updateHealthStatus(String serviceId, boolean healthy) {
HealthStatus status = healthStatus.computeIfAbsent(serviceId, k -> new HealthStatus());
status.setHealthy(healthy);
status.setLastCheckTime(System.currentTimeMillis());

if (!healthy) {
status.incrementFailureCount();
} else {
status.resetFailureCount();
}
}

/**
* 获取健康服务
*/
public List<IdGeneratorService> getHealthyServices() {
return idGeneratorServices.stream()
.filter(service -> {
HealthStatus status = healthStatus.get(service.getServiceId());
return status != null && status.isHealthy();
})
.collect(Collectors.toList());
}

/**
* 健康状态类
*/
public static class HealthStatus {
private boolean healthy = true;
private long lastCheckTime = System.currentTimeMillis();
private int failureCount = 0;

public void incrementFailureCount() {
this.failureCount++;
}

public void resetFailureCount() {
this.failureCount = 0;
}

// getter和setter方法
public boolean isHealthy() { return healthy; }
public void setHealthy(boolean healthy) { this.healthy = healthy; }
public long getLastCheckTime() { return lastCheckTime; }
public void setLastCheckTime(long lastCheckTime) { this.lastCheckTime = lastCheckTime; }
public int getFailureCount() { return failureCount; }
}
}

4.3.2 故障转移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* 故障转移ID生成器
*/
@Service
public class FailoverIdGenerator {

@Autowired
private FailureDetectionService failureDetectionService;

@Autowired
private List<IdGeneratorService> idGeneratorServices;

/**
* 故障转移获取ID
*/
public Long generateId(String businessType) {
List<IdGeneratorService> healthyServices = failureDetectionService.getHealthyServices();

if (healthyServices.isEmpty()) {
throw new RuntimeException("没有可用的ID生成服务");
}

// 尝试获取ID
for (IdGeneratorService service : healthyServices) {
try {
return service.generateId(businessType);
} catch (Exception e) {
log.warn("服务调用失败,尝试下一个服务: {}", service.getServiceId(), e);
}
}

throw new RuntimeException("所有服务都不可用");
}

/**
* 批量获取ID(故障转移)
*/
public List<Long> generateIds(String businessType, int count) {
List<Long> ids = new ArrayList<>();

for (int i = 0; i < count; i++) {
ids.add(generateId(businessType));
}

return ids;
}
}

五、性能监控与优化

5.1 性能监控

5.1.1 监控指标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
* ID生成器性能监控
*/
@Component
public class IdGeneratorMetrics {

private final MeterRegistry meterRegistry;
private final Counter idGenerationCounter;
private final Timer idGenerationTimer;
private final Gauge cacheHitRate;
private final Gauge cacheMissRate;

public IdGeneratorMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;

// 初始化监控指标
this.idGenerationCounter = Counter.builder("id.generator.count")
.description("ID生成次数")
.register(meterRegistry);

this.idGenerationTimer = Timer.builder("id.generator.duration")
.description("ID生成耗时")
.register(meterRegistry);

this.cacheHitRate = Gauge.builder("id.generator.cache.hit.rate")
.description("缓存命中率")
.register(meterRegistry, this, IdGeneratorMetrics::getCacheHitRate);

this.cacheMissRate = Gauge.builder("id.generator.cache.miss.rate")
.description("缓存未命中率")
.register(meterRegistry, this, IdGeneratorMetrics::getCacheMissRate);
}

/**
* 记录ID生成
*/
public void recordIdGeneration(String businessType, long duration) {
idGenerationCounter.increment(Tags.of("business_type", businessType));
idGenerationTimer.record(duration, TimeUnit.MILLISECONDS);
}

/**
* 记录缓存命中
*/
public void recordCacheHit(String businessType) {
Counter.builder("id.generator.cache.hit")
.description("缓存命中次数")
.tag("business_type", businessType)
.register(meterRegistry)
.increment();
}

/**
* 记录缓存未命中
*/
public void recordCacheMiss(String businessType) {
Counter.builder("id.generator.cache.miss")
.description("缓存未命中次数")
.tag("business_type", businessType)
.register(meterRegistry)
.increment();
}

/**
* 获取缓存命中率
*/
private double getCacheHitRate() {
// 实现缓存命中率计算逻辑
return 0.95; // 示例值
}

/**
* 获取缓存未命中率
*/
private double getCacheMissRate() {
// 实现缓存未命中率计算逻辑
return 0.05; // 示例值
}
}

5.1.2 性能分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
* 性能分析服务
*/
@Service
public class PerformanceAnalysisService {

@Autowired
private IdGeneratorMetrics metrics;

@Autowired
private MeterRegistry meterRegistry;

/**
* 分析性能指标
*/
public PerformanceAnalysisResult analyzePerformance() {
PerformanceAnalysisResult result = new PerformanceAnalysisResult();

// 分析ID生成性能
analyzeIdGenerationPerformance(result);

// 分析缓存性能
analyzeCachePerformance(result);

// 分析数据库性能
analyzeDatabasePerformance(result);

return result;
}

/**
* 分析ID生成性能
*/
private void analyzeIdGenerationPerformance(PerformanceAnalysisResult result) {
// 获取ID生成计时器
Timer idGenerationTimer = meterRegistry.find("id.generator.duration").timer();
if (idGenerationTimer != null) {
result.setAvgIdGenerationTime(idGenerationTimer.mean(TimeUnit.MILLISECONDS));
result.setMaxIdGenerationTime(idGenerationTimer.max(TimeUnit.MILLISECONDS));
result.setP95IdGenerationTime(idGenerationTimer.percentile(0.95, TimeUnit.MILLISECONDS));
result.setP99IdGenerationTime(idGenerationTimer.percentile(0.99, TimeUnit.MILLISECONDS));
}

// 获取ID生成计数器
Counter idGenerationCounter = meterRegistry.find("id.generator.count").counter();
if (idGenerationCounter != null) {
result.setTotalIdGenerationCount(idGenerationCounter.count());
}
}

/**
* 分析缓存性能
*/
private void analyzeCachePerformance(PerformanceAnalysisResult result) {
// 获取缓存命中率
Gauge cacheHitRate = meterRegistry.find("id.generator.cache.hit.rate").gauge();
if (cacheHitRate != null) {
result.setCacheHitRate(cacheHitRate.value());
}

// 获取缓存未命中率
Gauge cacheMissRate = meterRegistry.find("id.generator.cache.miss.rate").gauge();
if (cacheMissRate != null) {
result.setCacheMissRate(cacheMissRate.value());
}
}

/**
* 分析数据库性能
*/
private void analyzeDatabasePerformance(PerformanceAnalysisResult result) {
// 实现数据库性能分析逻辑
result.setDatabaseConnectionCount(10);
result.setDatabaseQueryTime(50.0);
result.setDatabaseUpdateTime(30.0);
}
}

5.2 性能优化

5.2.1 批量优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
* 批量ID生成器
*/
@Service
public class BatchIdGenerator {

@Autowired
private IdGeneratorMapper idGeneratorMapper;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private final Map<String, BatchIdRange> batchCache = new ConcurrentHashMap<>();

/**
* 批量获取ID
*/
public List<Long> generateBatchIds(String businessType, int count) {
List<Long> ids = new ArrayList<>();

// 1. 尝试从批量缓存获取
BatchIdRange batchRange = batchCache.get(businessType);
if (batchRange != null && batchRange.hasNext(count)) {
return batchRange.next(count);
}

// 2. 从Redis批量缓存获取
batchRange = getFromRedisBatchCache(businessType);
if (batchRange != null && batchRange.hasNext(count)) {
batchCache.put(businessType, batchRange);
return batchRange.next(count);
}

// 3. 从数据库获取新的批量ID段
batchRange = getNewBatchIdRange(businessType, count);
batchCache.put(businessType, batchRange);

// 4. 异步更新Redis缓存
updateRedisBatchCache(businessType, batchRange);

return batchRange.next(count);
}

/**
* 获取新的批量ID段
*/
private BatchIdRange getNewBatchIdRange(String businessType, int count) {
// 计算需要的步长
int stepSize = Math.max(count * 2, 1000); // 至少1000,或者count的2倍

// 获取ID段
IdRange range = idGeneratorMapper.getNewIdRange(businessType);

return new BatchIdRange(range.getStart(), range.getEnd());
}

/**
* 从Redis批量缓存获取
*/
private BatchIdRange getFromRedisBatchCache(String businessType) {
String cacheKey = "batch_id_range:" + businessType;
return (BatchIdRange) redisTemplate.opsForValue().get(cacheKey);
}

/**
* 更新Redis批量缓存
*/
private void updateRedisBatchCache(String businessType, BatchIdRange batchRange) {
CompletableFuture.runAsync(() -> {
String cacheKey = "batch_id_range:" + businessType;
redisTemplate.opsForValue().set(cacheKey, batchRange, Duration.ofMinutes(30));
});
}

/**
* 批量ID段类
*/
public static class BatchIdRange {
private long start;
private long end;
private long current;

public BatchIdRange(long start, long end) {
this.start = start;
this.end = end;
this.current = start;
}

public boolean hasNext(int count) {
return current + count - 1 <= end;
}

public List<Long> next(int count) {
if (!hasNext(count)) {
throw new IllegalStateException("批量ID段不足");
}

List<Long> ids = new ArrayList<>();
for (int i = 0; i < count; i++) {
ids.add(current++);
}

return ids;
}

// getter和setter方法
public long getStart() { return start; }
public void setStart(long start) { this.start = start; }
public long getEnd() { return end; }
public void setEnd(long end) { this.end = end; }
public long getCurrent() { return current; }
public void setCurrent(long current) { this.current = current; }
}
}

5.2.2 异步优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/**
* 异步ID生成器
*/
@Service
public class AsyncIdGenerator {

@Autowired
private IdGeneratorMapper idGeneratorMapper;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private final ExecutorService asyncExecutor = Executors.newFixedThreadPool(10);

/**
* 异步获取ID
*/
public CompletableFuture<Long> generateIdAsync(String businessType) {
return CompletableFuture.supplyAsync(() -> {
return generateId(businessType);
}, asyncExecutor);
}

/**
* 异步批量获取ID
*/
public CompletableFuture<List<Long>> generateIdsAsync(String businessType, int count) {
return CompletableFuture.supplyAsync(() -> {
return generateIds(businessType, count);
}, asyncExecutor);
}

/**
* 异步预热ID段
*/
public CompletableFuture<Void> preloadIdRangeAsync(String businessType) {
return CompletableFuture.runAsync(() -> {
try {
// 获取ID段
IdRange range = getNewIdRange(businessType);

// 存储到Redis
String cacheKey = "id_range:" + businessType;
redisTemplate.opsForValue().set(cacheKey, range, Duration.ofMinutes(30));

log.info("异步预热ID段完成: {}", businessType);
} catch (Exception e) {
log.error("异步预热ID段失败: {}", businessType, e);
}
}, asyncExecutor);
}

/**
* 同步获取ID(内部方法)
*/
private Long generateId(String businessType) {
// 实现同步ID生成逻辑
return 1L; // 示例返回值
}

/**
* 同步批量获取ID(内部方法)
*/
private List<Long> generateIds(String businessType, int count) {
// 实现同步批量ID生成逻辑
return Collections.singletonList(1L); // 示例返回值
}

/**
* 获取新的ID段(内部方法)
*/
private IdRange getNewIdRange(String businessType) {
// 实现获取新ID段逻辑
return new IdRange(1, 1000); // 示例返回值
}
}

六、企业级部署方案

6.1 容器化部署

6.1.1 Docker配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Dockerfile
FROM openjdk:8-jdk-alpine

# 设置工作目录
WORKDIR /app

# 复制应用文件
COPY target/id-generator-*.jar app.jar

# 设置环境变量
ENV JAVA_OPTS="-Xms512m -Xmx1g -XX:+UseG1GC"

# 暴露端口
EXPOSE 8080

# 启动命令
CMD ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# docker-compose.yml
version: '3.8'
services:
id-generator:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- MYSQL_HOST=mysql
- REDIS_HOST=redis
depends_on:
- mysql
- redis
restart: unless-stopped

mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=root123
- MYSQL_DATABASE=id_generator
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./sql:/docker-entrypoint-initdb.d
restart: unless-stopped

redis:
image: redis:6.2-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: unless-stopped

volumes:
mysql_data:
redis_data:

6.1.2 Kubernetes部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# k8s-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: id-generator
spec:
replicas: 3
selector:
matchLabels:
app: id-generator
template:
metadata:
labels:
app: id-generator
spec:
containers:
- name: id-generator
image: id-generator:latest
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "k8s"
- name: MYSQL_HOST
value: "mysql-service"
- name: REDIS_HOST
value: "redis-service"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
name: id-generator-service
spec:
selector:
app: id-generator
ports:
- port: 8080
targetPort: 8080
type: ClusterIP

---
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
type: ClusterIP

---
apiVersion: v1
kind: Service
metadata:
name: redis-service
spec:
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
type: ClusterIP

6.2 监控告警

6.2.1 Prometheus配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# prometheus.yml
global:
scrape_interval: 15s

scrape_configs:
- job_name: 'id-generator'
static_configs:
- targets: ['id-generator-service:8080']
metrics_path: '/actuator/prometheus'
scrape_interval: 5s

rule_files:
- "id-generator-rules.yml"

alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# id-generator-rules.yml
groups:
- name: id-generator-alerts
rules:
- alert: HighIdGenerationLatency
expr: id_generator_duration_seconds{quantile="0.95"} > 1
for: 2m
labels:
severity: warning
annotations:
summary: "ID生成延迟过高"
description: "ID生成P95延迟超过1秒,当前值: {{ $value }}秒"

- alert: LowCacheHitRate
expr: id_generator_cache_hit_rate < 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "缓存命中率过低"
description: "缓存命中率低于80%,当前值: {{ $value }}%"

- alert: HighErrorRate
expr: rate(id_generator_errors_total[5m]) > 0.1
for: 2m
labels:
severity: critical
annotations:
summary: "ID生成错误率过高"
description: "ID生成错误率超过10%,当前值: {{ $value }}"

6.2.2 Grafana仪表板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
{
"dashboard": {
"title": "ID生成器监控仪表板",
"panels": [
{
"title": "ID生成QPS",
"type": "graph",
"targets": [
{
"expr": "rate(id_generator_count_total[1m])",
"legendFormat": "{{business_type}}"
}
]
},
{
"title": "ID生成延迟",
"type": "graph",
"targets": [
{
"expr": "id_generator_duration_seconds",
"legendFormat": "{{quantile}}"
}
]
},
{
"title": "缓存命中率",
"type": "singlestat",
"targets": [
{
"expr": "id_generator_cache_hit_rate",
"legendFormat": "命中率"
}
]
},
{
"title": "数据库连接数",
"type": "graph",
"targets": [
{
"expr": "hikaricp_connections_active",
"legendFormat": "活跃连接"
},
{
"expr": "hikaricp_connections_idle",
"legendFormat": "空闲连接"
}
]
}
]
}
}

6.3 运维管理

6.3.1 健康检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/**
* 健康检查服务
*/
@Component
public class HealthCheckService {

@Autowired
private DataSource dataSource;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private IdGeneratorMapper idGeneratorMapper;

/**
* 健康检查
*/
public HealthStatus checkHealth() {
HealthStatus status = new HealthStatus();

// 检查数据库
boolean dbHealthy = checkDatabaseHealth();
status.setDatabaseHealthy(dbHealthy);

// 检查Redis
boolean redisHealthy = checkRedisHealth();
status.setRedisHealthy(redisHealthy);

// 检查ID生成器
boolean idGeneratorHealthy = checkIdGeneratorHealth();
status.setIdGeneratorHealthy(idGeneratorHealthy);

// 总体健康状态
status.setOverallHealthy(dbHealthy && redisHealthy && idGeneratorHealthy);

return status;
}

/**
* 检查数据库健康状态
*/
private boolean checkDatabaseHealth() {
try (Connection connection = dataSource.getConnection()) {
return connection.isValid(5);
} catch (SQLException e) {
log.error("数据库健康检查失败", e);
return false;
}
}

/**
* 检查Redis健康状态
*/
private boolean checkRedisHealth() {
try {
String result = redisTemplate.getConnectionFactory().getConnection().ping();
return "PONG".equals(result);
} catch (Exception e) {
log.error("Redis健康检查失败", e);
return false;
}
}

/**
* 检查ID生成器健康状态
*/
private boolean checkIdGeneratorHealth() {
try {
// 尝试生成一个ID
Long id = idGeneratorMapper.getNewIdRange("health_check").next();
return id != null && id > 0;
} catch (Exception e) {
log.error("ID生成器健康检查失败", e);
return false;
}
}

/**
* 健康状态类
*/
public static class HealthStatus {
private boolean overallHealthy;
private boolean databaseHealthy;
private boolean redisHealthy;
private boolean idGeneratorHealthy;
private long checkTime = System.currentTimeMillis();

// getter和setter方法
public boolean isOverallHealthy() { return overallHealthy; }
public void setOverallHealthy(boolean overallHealthy) { this.overallHealthy = overallHealthy; }
public boolean isDatabaseHealthy() { return databaseHealthy; }
public void setDatabaseHealthy(boolean databaseHealthy) { this.databaseHealthy = databaseHealthy; }
public boolean isRedisHealthy() { return redisHealthy; }
public void setRedisHealthy(boolean redisHealthy) { this.redisHealthy = redisHealthy; }
public boolean isIdGeneratorHealthy() { return idGeneratorHealthy; }
public void setIdGeneratorHealthy(boolean idGeneratorHealthy) { this.idGeneratorHealthy = idGeneratorHealthy; }
public long getCheckTime() { return checkTime; }
public void setCheckTime(long checkTime) { this.checkTime = checkTime; }
}
}

6.3.2 运维脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/bin/bash
# 运维管理脚本

# 设置变量
SERVICE_NAME="id-generator"
HEALTH_CHECK_URL="http://localhost:8080/actuator/health"
LOG_FILE="/var/log/id-generator/ops.log"

# 函数:记录日志
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
}

# 函数:健康检查
health_check() {
local response=$(curl -s -o /dev/null -w "%{http_code}" $HEALTH_CHECK_URL)
if [ "$response" = "200" ]; then
log "健康检查通过"
return 0
else
log "健康检查失败,HTTP状态码: $response"
return 1
fi
}

# 函数:重启服务
restart_service() {
log "开始重启服务"

# 停止服务
systemctl stop $SERVICE_NAME

# 等待服务停止
sleep 10

# 启动服务
systemctl start $SERVICE_NAME

# 等待服务启动
sleep 30

# 检查服务状态
if health_check; then
log "服务重启成功"
return 0
else
log "服务重启失败"
return 1
fi
}

# 函数:清理缓存
clear_cache() {
log "开始清理缓存"

# 清理Redis缓存
redis-cli FLUSHDB

# 清理本地缓存
curl -X POST "$HEALTH_CHECK_URL/clear-cache"

log "缓存清理完成"
}

# 函数:性能测试
performance_test() {
log "开始性能测试"

# 使用ab进行性能测试
ab -n 10000 -c 100 http://localhost:8080/api/id/generate?businessType=test

log "性能测试完成"
}

# 主函数
main() {
case "$1" in
"health")
health_check
;;
"restart")
restart_service
;;
"clear-cache")
clear_cache
;;
"performance-test")
performance_test
;;
*)
echo "用法: $0 {health|restart|clear-cache|performance-test}"
exit 1
;;
esac
}

# 执行主函数
main "$@"

七、总结

基于MySQL实现高性能分布式ID生成器,通过合理的架构设计、性能优化和运维管理,可以满足企业级应用的需求。虽然MySQL在高并发场景下存在性能瓶颈,但通过分段、缓存、异步等优化策略,仍然可以实现高性能的分布式ID生成服务。

7.1 关键要点

  1. 架构设计:采用分段、缓存、异步等策略提高性能
  2. 并发控制:使用乐观锁、悲观锁、分布式锁保证数据一致性
  3. 性能优化:通过索引优化、连接池优化、批量操作提高性能
  4. 高可用:通过主从复制、集群部署、故障转移保证高可用
  5. 监控运维:建立完善的监控告警和运维管理体系

7.2 最佳实践

  1. 分段策略:使用分段表减少锁竞争,提高并发性能
  2. 缓存优化:使用多级缓存减少数据库访问,提高响应速度
  3. 批量操作:使用批量获取减少网络开销,提高吞吐量
  4. 异步处理:使用异步操作提高系统响应性和并发能力
  5. 监控告警:建立完善的监控体系,及时发现和处理问题

通过以上措施,可以构建一个高性能、高可用的MySQL分布式ID生成器,为分布式系统提供可靠的ID生成服务。