項目開發時,使用了兩套數據庫,開發環境和上線環境,數據庫表中大多採用了自增主鍵,
比方:
id int unsigned primary key auto_increment,
但往往會碰到一些問題。比方:
開發環境中,使用爬蟲抓取一些數據,建立索引。再把數據遷移到上線環境,會導致索引中的id和
上線環境數據庫中id對不上,所以決定使用字符串作為主鍵。
那么問題來了,怎樣生成唯一的序列號?
格式依照:yyyyMMdd+兩位業務碼+10位的自增序列。
比方20150101**99**0000000001。
思路:
獲得日期非常easy;
業務碼是調用服務傳入的參數;
使用Redis來實現10位的自增序列的保存和自增,使用serial.number:{日期}的格式來保存某一天的自增序列的值;
主要代碼例如以下:
public interface SerialNumberService {
/** * 序列號自增序列 */
String SERIAL_NUMBER = "serial.number:";
/** * 依據兩位業務碼字符串,生成一個流水號,格式依照:<br/> * yyyyMMdd{bizCode}{10位的自增序列號} * * @param bizCode * 兩位,00-99 * @return 20位的序列號 * @throws ServiceException */
String generate(String bizCode) throws ServiceException;
//事實上,應該對bizCode做白名單驗證,以免惡意偽造
default boolean isLegal(String bizCode) {
if (bizCode == null || bizCode.length() != 2) {
throw new RuntimeException("bizCode: " + bizCode + "異常");
}
if (Character.isDigit(bizCode.charAt(0))
&& Character.isDigit(bizCode.charAt(1)))
return true;
return false;
}
}
@Service
public class SerialNumberServiceImpl implements SerialNumberService {
@Resource
private RedisDao redisDao;
@Override
public String generate(String bizCode) throws ServiceException {
/** 檢查業務碼 */
boolean isLegal = isLegal(bizCode);
if (!isLegal) {
throw new ServiceException("bizCode參數不合法");
}
/** 獲取今天的日期:yyyyMMdd */
String date = TimeUtil.getToday();
/** 構造redis的key */
String key = SERIAL_NUMBER + date;
/** 自增 */
long sequence = redisDao.incr(key);
String seq = StringUtil.getSequence(sequence);
StringBuilder sb = new StringBuilder();
sb.append(date).append(bizCode).append(seq);
String serial = sb.toString();
return serial;
}
}
public class TimeUtil {
private TimeUtil() {
}
/** * 獲取今日日期 * * @return */
public static String getToday() {
return "20150101";
}
}
public class StringUtil {
private StringUtil() {
}
static final int DEFAULT_LENGTH = 10;
/** * 得到10位的序列號,長度不足10位,前面補0 * * @param seq * @return */
public static String getSequence(long seq) {
String str = String.valueOf(seq);
int len = str.length();
if (len >= DEFAULT_LENGTH) {// 取決於業務規模,應該不會到達10
return str;
}
int rest = DEFAULT_LENGTH - len;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < rest; i++) {
sb.append('0');
}
sb.append(str);
return sb.toString();
}
}
僅僅聲明了RedisDao接口,能夠使用Jedisclient來實現。
public interface RedisDao {
String get(String key);
/** * 自增,+1,返回添加后的值 * * @param key * @return */
long incr(String key);
}
這樣Redis里面就存儲了每天產生的最大序列號。
能夠依據日期、業務碼等統計相關信息。