幾乎全部的大型項目都涉及到分庫分表(使用關係型數據庫),爲了應對遞增的數據增加採用分庫分表的策略,分庫分表後面臨的首要問題就是主鍵的生成。主鍵的關係涉及幾個重要的因素:java
1,若是隻是作數據存儲,沒有其餘的意義,這樣的主鍵設計會很簡單,面臨的是後期查詢的問題,須要選擇是選擇什麼樣的數據類型來存儲主鍵,好比uuid的varchar,序列的bigint等,又或者拼接的結果最終以varchar來存儲,考慮的要點是須要怎麼最好的使用索引性能來設計實現快速的查詢。mysql
2,確保整個系統的分庫分表的主鍵數據惟一,能夠採用主鍵生成器的方式來確保主鍵惟一,該方案也有不少的方式實現,最多見的就是咱們使用mysql的一個表來控制主鍵的生成,你可使用序列也可使用其餘的,該方案的缺點是高併發的時候性能會壓在該數據庫服務器,也能夠採用不一樣的步長增加使用不一樣的數據庫。還有一種是動態的生成Twitter的snowflake算法,該算法已經有不少實現方法,中心思想是同樣的,能夠上網搜索不少的實現方法。我這裏介紹的是基於redis來實現的一個小方案。redis
3,基於redis實現分佈式主鍵的策略。算法
redis首先是支持主從複製的,能夠確保高可用的,採用服務器的雙redis和keepalive實現災難自動轉移。根據狀況能夠分紅不一樣的主鍵成成類型採用負載均衡的方法,平攤服務器的壓力。而且redis自己是支持事物的,順便再講解一下,在分佈式系統中使用的分佈式鎖安全的一種,這裏只是小提一下,具體的能夠參考redis的官方文檔。具體的實現方式有不少種,這裏使用一種簡單的方式來實現:spring
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 synchronized 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; } }
該方法只是一個拋磚引玉,你能夠採用其餘的方式來實現。sql