非自增編號字段,避免生成重複編號(以pdfNo編號爲例)java
有個場景,用戶查詢延誤航班信息,而後生產一個編號,默認第一個編號是1000001,其後新增的編號默認自增長1。每次有人來查延誤信息,若是延誤信息存在,則取查詢數據庫pdfNo字段,查詢最大的編號,而後+1後,再插入一條新的延誤記錄。這樣會形成多人同時查詢,並生成延誤記錄是,pdfNo的編號會重複現象。redis
通過分析,俺們組長說,有2中多種解決方案,一種是分佈式鎖方案,一種是insert into select from方案,一種是RedisLock方案。數據庫
本人愚笨,說下insert into select from, 和 RedisLock方案app
insert into select from:dom
在入庫的時候,查詢下最大pdfNo,而後加一入庫分佈式
方法一: INSERT INTO Websites (name, country) SELECT app_name, country FROM apps WHERE id=1;
二: 表結構如上圖 insert into test_aaa (title, pdfno) select "hello", max(pdfno) +1 as pdfmax from test_aaa;
RedisLock方法ide
VariableKeyLock.java
import java.util.concurrent.locks.Lock; /** * 可變key鎖 * * @author zhangyang-b */ public interface VariableKeyLock extends Lock { boolean tryLock(String key); void lock(String key); void unlock(String key); }
RedisLock.javalua
public class RedisLock implements VariableKeyLock { public static final String LOCK = "LOCK"; @Autowired private RedisConnectionFactory factory; private ThreadLocal<String> localValue = new ThreadLocal<String>(); /** * 解鎖lua腳本 */ private static final String UNLOCK_LUA = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end"; @Override public void lock() { if (!tryLock()) { try { Thread.sleep(new Random().nextInt(10) + 1); } catch (InterruptedException e) { log.error(e.getMessage(), e); } lock(); } } @Override public void lock(String key) { if (!tryLock(key)) { try { Thread.sleep(new Random().nextInt(10) + 1); } catch (InterruptedException e) { log.error(e.getMessage(), e); } lock(key); } } @Override public boolean tryLock() { RedisConnection connection = null; try { connection = factory.getConnection(); Jedis jedis = (Jedis)connection.getNativeConnection(); String value = UUID.randomUUID().toString(); localValue.set(value); String ret = jedis.set(LOCK, value, "NX", "PX", 10000); return ret != null && "OK".equals(ret); } catch (Exception e) { log.error(e.getMessage(), e); } finally { if (connection != null) { connection.close(); } } return false; } @Override public boolean tryLock(String key) { RedisConnection connection = null; try { connection = factory.getConnection(); Jedis jedis = (Jedis)connection.getNativeConnection(); String value = UUID.randomUUID().toString(); localValue.set(value); String ret = jedis.set(key, value, "NX", "PX", 10000); return ret != null && "OK".equals(ret); } catch (Exception e) { log.error(e.getMessage(), e); } finally { if (connection != null) { connection.close(); } } return false; } @Override public void unlock() { String script = UNLOCK_LUA; RedisConnection connection = null; try { connection = factory.getConnection(); Object jedis = connection.getNativeConnection(); if (jedis instanceof Jedis) { ((Jedis)jedis).eval(script, Arrays.asList(LOCK), Arrays.asList(localValue.get())); } else if (jedis instanceof JedisCluster) { ((JedisCluster)jedis).eval(script, Arrays.asList(LOCK), Arrays.asList(localValue.get())); } } catch (Exception e) { log.error(e.getMessage(), e); } finally { if (connection != null) { connection.close(); } } } @Override public void unlock(String key) { String script = UNLOCK_LUA; RedisConnection connection = null; try { connection = factory.getConnection(); Object jedis = connection.getNativeConnection(); if (jedis instanceof Jedis) { ((Jedis)jedis).eval(script, Arrays.asList(key), Arrays.asList(localValue.get())); } else if (jedis instanceof JedisCluster) { ((JedisCluster)jedis).eval(script, Arrays.asList(key), Arrays.asList(localValue.get())); } } catch (Exception e) { log.error(e.getMessage(), e); } finally { if (connection != null) { connection.close(); } } } }
用法:
redisLock.lock(key名); try{ int maxPdfNo = 0; try{ maxPdfNo = flightDelayPdfJService.queryMaxPdfNo(); }catch (Exception e){ e.printStackTrace(); } if(maxPdfNo > 0){ return maxPdfNo + 1; }else{ return 1000001; } }finally { redisLock.unlock(key名); }