目錄
背景
因爲業務數量較大,單表影響查詢性能,採用了單庫分表解決。引入了sharding-jdbc插件進行分表下的數據庫操作。生產環境都是分佈式部署服務的,有兩臺機器。最近有併發量高的業務場景,偶爾會出現生成主鍵重複的問題 Duplicate entry '******************' for key 'PRIMARY'。
問題排查
主鍵是sharding-jdbc生成的,使用默認的雪花算法,是可以保證分佈式主鍵唯一的。所以就很奇怪爲什麼主鍵會重複,又仔細的查看了官方文檔,發現了問題。由於是分佈式部署,沒有指定不同進程的workId(默認是0),導致兩臺機器的workId相同,致使10bit工作進程位相同,所以兩個寫入請求同時打到兩臺機器上時,就可能會出現主鍵衝突。
下面簡單解釋一下:
符號位固定是0
時間戳位:生產服務器的時間是同步過的,所以同一時間寫入數據,時間戳位是相同的
工作進程位:默認是0,分佈式部署的話,不同進程要自己標記workId來實現區分,由於之前沒有區分,導致工位進程位也相同
序列號位:序列號位是在同一進程中,保證id順序的。有可能兩個進程的請求的序列號是相同的,這時就會出現兩臺機器生成的id完全相同。
解決
定位了原因,之後就是給兩臺機器指定不同的workId就好。考慮到配置的通用性,使用hostname做區分。
生成workId
public class SnowflakeIdWorker {
public static Long getWorkId() {
String hostAddress = System.getenv("HOSTNAME");
log.info("============= hostAddress: {} =============", hostAddress);
int[] ints = org.apache.commons.lang3.StringUtils.toCodePoints(hostAddress);
int sums = 0;
int[] var3 = ints;
int var4 = ints.length;
for(int var5 = 0; var5 < var4; ++var5) {
int b = var3[var5];
sums += b;
}
long workId = (long)(sums % 32);
log.info("============== workId:{} =============", workId);
return workId;
}
}
配置workId
import com.yst.b2b.wallet.utils.SnowflakeIdWorker;
import io.shardingsphere.core.keygen.DefaultKeyGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
/**
* sharding-jdbc 使用雪花算法生成主鍵時,workId配置
*
* @yx8102 2020/4/24
*/
@Configuration
@Slf4j
public class WorkIdConfig {
static {
long workId = 0;
try {
workId = SnowflakeIdWorker.getWorkId();
} catch (Exception e) {
log.error("生成workId發生異常.", e);
}
DefaultKeyGenerator.setWorkerId(workId);
}
}
後續:其他生成workId的方法