您的位置:首页 > 业界新闻

互联网分库分表主键的生成-一个小思路

2016-12-26 00:00 218 查看
摘要: 分库分表主键

几乎所有的大型项目都涉及到分库分表(使用关系型数据库),为了应对递增的数据增长采用分库分表的策略,分库分表后面临的首要问题就是主键的生成。主键的关系涉及几个重要的因素:

1,如果只是做数据存储,没有其他的意义,这样的主键设计会很简单,面临的是后期查询的问题,需要选择是选择什么样的数据类型来存储主键,比如uuid的varchar,序列的bigint等,又或者拼接的结果最终以varchar来存储,考虑的要点是需要怎么最好的使用索引性能来设计实现快速的查询。

2,确保整个系统的分库分表的主键数据唯一,可以采用主键生成器的方式来确保主键唯一,该方案也有很多的方式实现,最常见的就是我们使用mysql的一个表来控制主键的生成,你可以使用序列也可以使用其他的,该方案的缺点是高并发的时候性能会压在该数据库服务器,也可以采用不同的步长增长使用不同的数据库。还有一种是动态的生成Twitter的snowflake算法,该算法已经有很多实现方法,中心思想是一样的,可以上网搜索很多的实现方法。我这里介绍的是基于redis来实现的一个小方案。

3,基于redis实现分布式主键的策略。

redis首先是支持主从复制的,可以确保高可用的,采用服务器的双redis和keepalive实现灾难自动转移。根据情况可以分成不同的主键成成类型采用负载均衡的方法,平摊服务器的压力。并且redis本身是支持事物的,顺便再讲解一下,在分布式系统中使用的分布式锁安全的一种,这里只是小提一下,具体的可以参考redis的官方文档。具体的实现方式有很多种,这里使用一种简单的方式来实现:

public class SequenceNum {

//关于redis的使用
private static JedisPool jedisPool;
private static Map<String,Integer> dbMap = new ConcurrentHashMap<String,Integer>();

//单例的安全实现如下
private SequenceNum() {
//spring整整合redis的链接工程模式
JedisConnectionFactory jedisConnectionFactory = (JedisConnectionFactory) SpringUtil.getBean("jedisConnectionFactory");
jedisPool = new JedisPool(jedisConnectionFactory.getPoolConfig(),
jedisConnectionFactory.getHostName(),jedisConnectionFactory.getPort());
dbMap.put("default", jedisConnectionFactory.getDatabase());
System.out.print("init one");
}
private static class SequenceHelper {
private static SequenceNum instance = new SequenceNum();
}
public static SequenceNum getInstance() {
return SequenceHelper.instance;
}

//下面是需要进行内部缓存处理的数据
private static Map<String, ConcurrentLinkedQueue<Long>> sequenceMap = new ConcurrentHashMap<String, ConcurrentLinkedQueue<Long>>();
private static final long SEQUENCE_NUM = 50;

//获得分布式序列的方法
public Long getSequenceNum(String sequence) {
Long sequenceNum = -1l;

ConcurrentLinkedQueue<Long> sequenceQuene = sequenceMap.get(sequence);

if (null != sequenceQuene) {
if (sequenceQuene.isEmpty()) {
getAndSetQuene(sequence, sequenceQuene);
}
} else {
sequenceQuene = new ConcurrentLinkedQueue<Long>();
sequenceMap.put(sequence, sequenceQuene);
getAndSetQuene(sequence, sequenceQuene);
}
sequenceNum = sequenceQuene.poll();
return sequenceNum;
}

/**
* 重构本地缓存的数据
* @param sequence
* @param sequenceQuene
*/
private synchronize
3ff8
d void getAndSetQuene(String sequence, ConcurrentLinkedQueue<Long> sequenceQuene) {
if (!sequenceQuene.isEmpty()) return;
try {
Jedis jedis = getJedis(sequence);

byte [] keys = sequence.getBytes("utf-8");
//使用redis的事物管理
Transaction trans =jedis.multi();
//该redis的健值自增1,是本次的开始位置
Response<Long> start = trans.incr(keys);
//redis的最后序列,可以使用区间的方式获得本地的缓存序列
Response<Long> end = trans.incrBy(keys, SEQUENCE_NUM);
//提交事务,保证本次的操作是一致性的,当然也可以采用redis的管道来实现
trans.exec();

closeRedis(jedis);
for (long i = start.get(); i <= end.get(); i++) {
sequenceQuene.add(i);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//释放redis的连接源
private void closeRedis(Jedis jedis) {
jedisPool.returnResourceObject(jedis);
}
//获得redis的操作源
private Jedis getJedis(String sequence) {
Jedis jedis = jedis = jedisPool.getResource();
jedis.select(dbMap.containsKey(sequence) ? dbMap.get(sequence) : dbMap.get("default"));
return jedis;
}

}


该方法只是一个抛砖引玉,你可以采用其他的方式来实现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  分布式主键生成