本文目录导读:

在Java分布式系统中进行随机搜索或随机数据访问,核心挑战在于跨节点的随机性、一致性和性能,以下是几种常见的实现策略和代码示例,从简单到复杂。
核心概念
- 随机性:需要真随机或伪随机数生成器
- 分布式一致性:多节点都能独立生成相同或不同的随机结果
- 负载均衡:随机访问不应导致某些节点过载
- 数据分片:数据分布在多个节点上
实现策略
基于一致性哈希的随机路由
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.nio.ByteBuffer;
import java.util.*;
public class ConsistentHashRandomSearch<T> {
private final TreeMap<Long, T> ring = new TreeMap<>();
private final int numberOfReplicas;
private final MessageDigest md;
public ConsistentHashRandomSearch(int numberOfReplicas) throws NoSuchAlgorithmException {
this.numberOfReplicas = numberOfReplicas;
this.md = MessageDigest.getInstance("MD5");
}
public void addNode(T node) {
for (int i = 0; i < numberOfReplicas; i++) {
long hash = hash(node.toString() + ":" + i);
ring.put(hash, node);
}
}
public T getRandomNode() {
// 生成随机哈希值作为搜索起点
long randomHash = hash(UUID.randomUUID().toString());
Map.Entry<Long, T> entry = ring.ceilingEntry(randomHash);
if (entry == null) {
entry = ring.firstEntry();
}
return entry.getValue();
}
private long hash(String key) {
md.update(key.getBytes());
byte[] digest = md.digest();
return ByteBuffer.wrap(digest).getLong() & Long.MAX_VALUE;
}
}
使用场景:适合需要将请求均匀分布到不同数据分片的场景。
基于分片的随机采样
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
public class ShardedRandomSearch<T> {
private final Map<Integer, List<T>> shards = new ConcurrentHashMap<>();
private final Random random = new Random();
private final int totalShards;
public ShardedRandomSearch(int totalShards) {
this.totalShards = totalShards;
for (int i = 0; i < totalShards; i++) {
shards.put(i, new CopyOnWriteArrayList<>());
}
}
// 添加数据到随机分片
public void addData(T data) {
int shardId = random.nextInt(totalShards);
shards.get(shardId).add(data);
}
// 随机搜索:先随机选分片,再从分片中随机选
public T randomSearch() {
if (shards.isEmpty()) return null;
// 1. 随机选择一个分片
int shardId = random.nextInt(totalShards);
List<T> shard = shards.get(shardId);
// 2. 从分片中随机选一个元素
if (shard.isEmpty()) {
// 如果该分片为空,尝试其他分片
for (int i = 0; i < totalShards; i++) {
shardId = (shardId + 1) % totalShards;
shard = shards.get(shardId);
if (!shard.isEmpty()) break;
}
}
return shard.get(random.nextInt(shard.size()));
}
// 批量随机搜索(不重复)
public List<T> batchRandomSearch(int count) {
List<T> results = new ArrayList<>();
Set<Integer> usedIndices = new HashSet<>();
while (results.size() < count) {
T item = randomSearch();
if (item != null && !usedIndices.contains(item.hashCode())) {
results.add(item);
usedIndices.add(item.hashCode());
}
}
return results;
}
}
分布式缓存随机访问
import redis.clients.jedis.*;
import java.util.*;
public class RedisRandomSearch {
private final JedisCluster jedisCluster;
private final Random random = new Random();
private final String[] nodes; // 所有Redis节点
public RedisRandomSearch(Set<HostAndPort> clusterNodes) {
this.jedisCluster = new JedisCluster(clusterNodes);
this.nodes = clusterNodes.stream()
.map(n -> n.getHost() + ":" + n.getPort())
.toArray(String[]::new);
}
// 存储数据时生成随机键
public void storeData(String key, String value) {
jedisCluster.set(key, value);
}
// 随机获取一条数据
public Map.Entry<String, String> randomGet() {
// 1. 随机选择一个节点
String node = nodes[random.nextInt(nodes.length)];
// 2. 使用 RANDOMKEY 命令
try (Jedis jedis = new Jedis(node)) {
String randomKey = jedis.randomKey();
if (randomKey != null) {
String value = jedisCluster.get(randomKey);
return new AbstractMap.SimpleEntry<>(randomKey, value);
}
}
return null;
}
// 扫描随机数据(大数据量)
public List<Map.Entry<String, String>> scanRandomData(int count) {
List<Map.Entry<String, String>> results = new ArrayList<>();
String cursor = "0";
ScanParams params = new ScanParams().count(100);
while (results.size() < count) {
ScanResult<String> scanResult = jedisCluster.scan(cursor, params);
for (String key : scanResult.getResult()) {
if (results.size() >= count) break;
String value = jedisCluster.get(key);
results.add(new AbstractMap.SimpleEntry<>(key, value));
}
cursor = scanResult.getCursor();
if (cursor.equals("0")) break;
}
return results;
}
}
基于权重的不均匀随机搜索
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
public class WeightedRandomSearch {
private final NavigableMap<Double, String> weightedMap = new TreeMap<>();
private double totalWeight = 0;
// 添加带权重的数据
public void addItem(String item, double weight) {
totalWeight += weight;
weightedMap.put(totalWeight, item);
}
// 根据权重随机选择
public String weightedRandom() {
double random = ThreadLocalRandom.current().nextDouble() * totalWeight;
Map.Entry<Double, String> entry = weightedMap.ceilingEntry(random);
return entry != null ? entry.getValue() : weightedMap.firstEntry().getValue();
}
// 分布式版本:从不同节点按权重获取
public static class DistributedWeightedSearch {
private final Map<String, WeightedRandomSearch> nodeSearches = new HashMap<>();
private final Map<String, Double> nodeWeights = new HashMap<>();
public void addNode(String nodeId, double weight) {
nodeSearches.put(nodeId, new WeightedRandomSearch());
nodeWeights.put(nodeId, weight);
}
public String distributedRandom() {
// 先随机选节点
double totalNodeWeight = nodeWeights.values().stream().mapToDouble(Double::doubleValue).sum();
double random = ThreadLocalRandom.current().nextDouble() * totalNodeWeight;
String selectedNode = null;
double cumulative = 0;
for (Map.Entry<String, Double> entry : nodeWeights.entrySet()) {
cumulative += entry.getValue();
if (random <= cumulative) {
selectedNode = entry.getKey();
break;
}
}
// 从选中节点随机获取
if (selectedNode != null) {
WeightedRandomSearch search = nodeSearches.get(selectedNode);
return search != null ? search.weightedRandom() : null;
}
return null;
}
}
}
基于流式处理的随机抽样
import java.util.*;
import java.util.concurrent.*;
public class ReservoirSampling<T> {
private final List<T> reservoir = new ArrayList<>();
private int count = 0;
private final int sampleSize;
public ReservoirSampling(int sampleSize) {
this.sampleSize = sampleSize;
}
// 蓄水池抽样算法
public void add(T item) {
count++;
if (count <= sampleSize) {
reservoir.add(item);
} else {
int index = ThreadLocalRandom.current().nextInt(count);
if (index < sampleSize) {
reservoir.set(index, item);
}
}
}
public List<T> getSample() {
return new ArrayList<>(reservoir);
}
// 分布式蓄水池抽样
public static class DistributedReservoir<T> {
private final Map<String, ReservoirSampling<T>> nodeSamples = new ConcurrentHashMap<>();
public void addItem(String node, T item) {
nodeSamples.computeIfAbsent(node, k -> new ReservoirSampling<>(100));
nodeSamples.get(node).add(item);
}
public List<T> getDistributedSample() {
// 从每个节点收集样本
List<T> combinedSample = new ArrayList<>();
for (ReservoirSampling<T> sampler : nodeSamples.values()) {
combinedSample.addAll(sampler.getSample());
}
// 对合并后的样本进行二次抽样
ReservoirSampling<T> finalSampler = new ReservoirSampling<>(100);
for (T item : combinedSample) {
finalSampler.add(item);
}
return finalSampler.getSample();
}
}
}
性能优化建议
-
预计算随机序列:
public class PrecomputedRandomSequence { private final List<Long> randomSequence; private int index = 0; public PrecomputedRandomSequence(int size) { randomSequence = new ArrayList<>(size); Random rnd = new Random(); for (int i = 0; i < size; i++) { randomSequence.add(rnd.nextLong()); } } public long nextRandom() { long value = randomSequence.get(index); index = (index + 1) % randomSequence.size(); return value; } } -
使用线程安全的随机数生成器:
// 推荐使用 ThreadLocalRandom 而非 Random ThreadLocalRandom.current().nextInt();
-
连接池管理: 对于数据库/缓存访问,务必使用连接池来减少开销。
完整示例:分布式随机用户推荐
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;
public class DistributedRandomRecommender {
private final Map<String, List<String>> userDatabase = new ConcurrentHashMap<>();
private final Random random = ThreadLocalRandom.current();
// 模拟分布式环境中的随机推荐
public List<String> getRandomRecommendations(String userId, int count) {
// 1. 从不同节点获取候选集
List<String> candidates = new ArrayList<>();
for (String node : userDatabase.keySet()) {
List<String> nodeData = userDatabase.get(node);
if (nodeData.size() > 0) {
// 从每个节点随机取一个子集
int sampleSize = Math.min(count, nodeData.size() / 10);
List<String> sample = new ArrayList<>(nodeData);
Collections.shuffle(sample, random);
candidates.addAll(sample.subList(0, sampleSize));
}
}
// 2. 对合并的候选集进行随机排序和选择
Collections.shuffle(candidates, random);
return candidates.stream()
.limit(count)
.collect(Collectors.toList());
}
// 基于用户历史行为加权随机
public List<String> getWeightedRecommendations(String userId, Map<String, Double> userHistory) {
WeightedRandomSearch weightedSearch = new WeightedRandomSearch();
// 根据用户历史行为计算权重
for (Map.Entry<String, Double> history : userHistory.entrySet()) {
weightedSearch.addItem(history.getKey(), history.getValue());
}
List<String> recommendations = new ArrayList<>();
for (int i = 0; i < 10; i++) {
String item = weightedSearch.weightedRandom();
if (item != null) {
recommendations.add(item);
}
}
return recommendations;
}
}
选择哪种策略取决于:
- 数据规模:小数据用简单随机,大数据用分片随机
- 分布均匀性要求:一致性哈希适合均匀分布,蓄水池抽样适合流式数据
- 性能要求:预计算序列提高性能,加权随机适合业务场景
- 一致性要求:强一致性用分布式锁,最终一致性用乐观锁
实际生产环境建议结合多种策略,并进行充分的压测和调优。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播