成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久

您的位置:首頁技術(shù)文章
文章詳情頁

利用mysql實(shí)現(xiàn)的雪花算法案例

瀏覽:5日期:2023-10-12 16:19:04

一、為何要用雪花算法

1、問題產(chǎn)生的背景

現(xiàn)如今越來越多的公司都在用分布式、微服務(wù),那么對(duì)應(yīng)的就會(huì)針對(duì)不同的服務(wù)進(jìn)行數(shù)據(jù)庫拆分,然后當(dāng)數(shù)據(jù)量上來的時(shí)候也會(huì)進(jìn)行分表,那么隨之而來的就是分表以后id的問題。

例如之前單體項(xiàng)目中一個(gè)表中的數(shù)據(jù)主鍵id都是自增的,mysql是利用autoincrement來實(shí)現(xiàn)自增,而oracle是利用序列來實(shí)現(xiàn)的,但是當(dāng)單表數(shù)據(jù)量上來以后就要進(jìn)行水平分表,阿里java開發(fā)建議是單表大于500w的時(shí)候就要分表,但是具體還是得看業(yè)務(wù),如果索引用的號(hào)的話,單表千萬的數(shù)據(jù)也是可以的。水平分表就是將一張表的數(shù)據(jù)分成多張表,那么問題就來了如果還是按照以前的自增來做主鍵id,那么就會(huì)出現(xiàn)id重復(fù),這個(gè)時(shí)候就得考慮用什么方案來解決分布式id的問題了。

2、解決方案

2.1、數(shù)據(jù)庫表

可以在某個(gè)庫中專門維護(hù)一張表,然后每次無論哪個(gè)表需要自增id的時(shí)候都去查這個(gè)表的記錄,然后用for update鎖表,然后取到的值加一,然后返回以后把再把值記錄到表中,但是這個(gè)方法適合并發(fā)量比較小的項(xiàng)目,因此每次都得鎖表。

2.2、redis

因?yàn)閞edis是單線程的,可以在redis中維護(hù)一個(gè)鍵值對(duì),然后哪個(gè)表需要直接去redis中取值然后加一,但是這個(gè)跟上面一樣由于單線程都是對(duì)高并發(fā)的支持不高,只適合并發(fā)量小的項(xiàng)目。

2.3、uuid

可以使用uuid作為不重復(fù)主鍵id,但是uuid有個(gè)問題就是其是無序的字符串,如果使用uuid當(dāng)做主鍵,那么主鍵索引就會(huì)失效。

2.4、雪花算法

雪花算法是解決分布式id的一個(gè)高效的方案,大部分互聯(lián)網(wǎng)公司都在使用雪花算法,當(dāng)然還有公司自己實(shí)現(xiàn)其他的方案。

二、雪花算法

1、原理

利用mysql實(shí)現(xiàn)的雪花算法案例

雪花算法就是使用64位long類型的數(shù)據(jù)存儲(chǔ)id,最高位一位存儲(chǔ)0或者1,0代表整數(shù),1代表負(fù)數(shù),一般都是0,所以最高位不變,41位存儲(chǔ)毫秒級(jí)時(shí)間戳,10位存儲(chǔ)機(jī)器碼(包括5位datacenterId和5位workerId),12存儲(chǔ)序列號(hào)。這樣最大2的10次方的機(jī)器,也就是1024臺(tái)機(jī)器,最多每毫秒每臺(tái)機(jī)器產(chǎn)生2的12次方也就是4096個(gè)id。(下面有代碼實(shí)現(xiàn))

但是一般我們沒有那么多臺(tái)機(jī)器,所以我們也可以使用53位來存儲(chǔ)id。為什么要用53位?

因?yàn)槲覀儙缀醵际歉鷚eb頁面打交道,就需要跟js打交道,js支持最大的整型范圍為53位,超過這個(gè)范圍就會(huì)丟失精度,53之內(nèi)可以直接由js讀取,超過53位就需要轉(zhuǎn)換成字符串才能保證js處理正確。53存儲(chǔ)的話,32位存儲(chǔ)秒級(jí)時(shí)間戳,5位存儲(chǔ)機(jī)器碼,16位存儲(chǔ)序列化,這樣每臺(tái)機(jī)器每秒可以生產(chǎn)65536個(gè)不重復(fù)的id。

2、缺點(diǎn)

由于雪花算法嚴(yán)重依賴時(shí)間,所以當(dāng)發(fā)生服務(wù)器時(shí)鐘回?fù)艿膯栴}是會(huì)導(dǎo)致可能產(chǎn)生重復(fù)的id。當(dāng)然幾乎沒有公司會(huì)修改服務(wù)器時(shí)間,修改以后會(huì)導(dǎo)致各種問題,公司寧愿新加一臺(tái)服務(wù)器也不愿意修改服務(wù)器時(shí)間,但是不排除特殊情況。

如何解決時(shí)鐘回?fù)艿膯栴}?可以對(duì)序列化的初始值設(shè)置步長(zhǎng),每次觸發(fā)時(shí)鐘回?fù)苁录瑒t其初始步長(zhǎng)就加1w,可以在下面代碼的第85行來實(shí)現(xiàn),將sequence的初始值設(shè)置為10000。

三、代碼實(shí)現(xiàn)

64位的代碼實(shí)現(xiàn):

package com.yl.common;/** * Twitter_Snowflake<br> * SnowFlake的結(jié)構(gòu)如下(每部分用-分開):<br> * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br> * 1位標(biāo)識(shí),由于long基本類型在Java中是帶符號(hào)的,最高位是符號(hào)位,正數(shù)是0,負(fù)數(shù)是1,所以id一般是正數(shù),最高位是0<br> * 41位時(shí)間截(毫秒級(jí)),注意,41位時(shí)間截不是存儲(chǔ)當(dāng)前時(shí)間的時(shí)間截,而是存儲(chǔ)時(shí)間截的差值(當(dāng)前時(shí)間截 - 開始時(shí)間截) * 得到的值),這里的的開始時(shí)間截,一般是我們的id生成器開始使用的時(shí)間,由我們程序來指定的(如下下面程序IdWorker類的startTime屬性)。41位的時(shí)間截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br> * 10位的數(shù)據(jù)機(jī)器位,可以部署在1024個(gè)節(jié)點(diǎn),包括5位datacenterId和5位workerId<br> * 12位序列,毫秒內(nèi)的計(jì)數(shù),12位的計(jì)數(shù)順序號(hào)支持每個(gè)節(jié)點(diǎn)每毫秒(同一機(jī)器,同一時(shí)間截)產(chǎn)生4096個(gè)ID序號(hào)<br> * 加起來剛好64位,為一個(gè)Long型。<br> * SnowFlake的優(yōu)點(diǎn)是,整體上按照時(shí)間自增排序,并且整個(gè)分布式系統(tǒng)內(nèi)不會(huì)產(chǎn)生ID碰撞(由數(shù)據(jù)中心ID和機(jī)器ID作區(qū)分),并且效率較高,經(jīng)測(cè)試,SnowFlake每秒能夠產(chǎn)生26萬ID左右。 */public class SnowflakeIdWorker { // ==============================Fields=========================================== /** 開始時(shí)間截 (2020-01-01) */ private final long twepoch = 1577808000000L; /** 機(jī)器id所占的位數(shù) */ private final long workerIdBits = 5L; /** 數(shù)據(jù)標(biāo)識(shí)id所占的位數(shù) */ private final long datacenterIdBits = 5L; /** 支持的最大機(jī)器id,結(jié)果是31 (這個(gè)移位算法可以很快的計(jì)算出幾位二進(jìn)制數(shù)所能表示的最大十進(jìn)制數(shù)) */ private final long maxWorkerId = -1L ^ (-1L << workerIdBits); /** 支持的最大數(shù)據(jù)標(biāo)識(shí)id,結(jié)果是31 */ private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); /** 序列在id中占的位數(shù) */ private final long sequenceBits = 12L; /** 機(jī)器ID向左移12位 */ private final long workerIdShift = sequenceBits; /** 數(shù)據(jù)標(biāo)識(shí)id向左移17位(12+5) */ private final long datacenterIdShift = sequenceBits + workerIdBits; /** 時(shí)間截向左移22位(5+5+12) */ private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; /** 生成序列的掩碼,這里為4095 (0b111111111111=0xfff=4095) */ private final long sequenceMask = -1L ^ (-1L << sequenceBits); /** 工作機(jī)器ID(0~31) */ private long workerId; /** 數(shù)據(jù)中心ID(0~31) */ private long datacenterId; /** 毫秒內(nèi)序列(0~4095) */ private long sequence = 0L; /** 上次生成ID的時(shí)間截 */ private long lastTimestamp = -1L; //==============================Constructors===================================== /** * 構(gòu)造函數(shù) * @param workerId 工作ID (0~31) * @param datacenterId 數(shù)據(jù)中心ID (0~31) */ public SnowflakeIdWorker(long workerId, long datacenterId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format('worker Id can’t be greater than %d or less than 0', maxWorkerId)); } if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException(String.format('datacenter Id can’t be greater than %d or less than 0', maxDatacenterId)); } this.workerId = workerId; this.datacenterId = datacenterId; } // ==============================Methods========================================== /** * 獲得下一個(gè)ID (該方法是線程安全的) * @return SnowflakeId */ public synchronized long nextId() { long timestamp = timeGen(); //如果當(dāng)前時(shí)間小于上一次ID生成的時(shí)間戳,說明系統(tǒng)時(shí)鐘回退過這個(gè)時(shí)候應(yīng)當(dāng)拋出異常 if (timestamp < lastTimestamp) { throw new RuntimeException( String.format('Clock moved backwards. Refusing to generate id for %d milliseconds', lastTimestamp - timestamp)); } //如果是同一時(shí)間生成的,則進(jìn)行毫秒內(nèi)序列 if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; //毫秒內(nèi)序列溢出 if (sequence == 0) { //阻塞到下一個(gè)毫秒,獲得新的時(shí)間戳 timestamp = tilNextMillis(lastTimestamp); } } //時(shí)間戳改變,毫秒內(nèi)序列重置 else { sequence = 0L; } //上次生成ID的時(shí)間截 lastTimestamp = timestamp; //移位并通過或運(yùn)算拼到一起組成64位的ID return ((timestamp - twepoch) << timestampLeftShift) // | (datacenterId << datacenterIdShift) // | (workerId << workerIdShift) // | sequence; } /** * 阻塞到下一個(gè)毫秒,直到獲得新的時(shí)間戳 * @param lastTimestamp 上次生成ID的時(shí)間截 * @return 當(dāng)前時(shí)間戳 */ protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } /** * 返回以毫秒為單位的當(dāng)前時(shí)間 * @return 當(dāng)前時(shí)間(毫秒) */ protected long timeGen() { return System.currentTimeMillis(); } //==============================Test============================================= /** 測(cè)試 */ public static void main(String[] args) { SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0); for (int i = 0; i < 100; i++) { long id = idWorker.nextId(); System.out.println(id); } }}

補(bǔ)充知識(shí):雪花算法實(shí)現(xiàn)分布式自增長(zhǎng)ID

我就廢話不多說了,大家還是直接看代碼吧~

/** * <p>名稱:IdWorker.java</p> * <p>描述:分布式自增長(zhǎng)ID</p> * <pre> * Twitter的 Snowflake JAVA實(shí)現(xiàn)方案 * </pre> * 核心代碼為其IdWorker這個(gè)類實(shí)現(xiàn),其原理結(jié)構(gòu)如下,我分別用一個(gè)0表示一位,用—分割開部分的作用: * 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000 * 在上面的字符串中,第一位為未使用(實(shí)際上也可作為long的符號(hào)位),接下來的41位為毫秒級(jí)時(shí)間, * 然后5位datacenter標(biāo)識(shí)位,5位機(jī)器ID(并不算標(biāo)識(shí)符,實(shí)際是為線程標(biāo)識(shí)), * 然后12位該毫秒內(nèi)的當(dāng)前毫秒內(nèi)的計(jì)數(shù),加起來剛好64位,為一個(gè)Long型。 * 這樣的好處是,整體上按照時(shí)間自增排序,并且整個(gè)分布式系統(tǒng)內(nèi)不會(huì)產(chǎn)生ID碰撞(由datacenter和機(jī)器ID作區(qū)分), * 并且效率較高,經(jīng)測(cè)試,snowflake每秒能夠產(chǎn)生26萬ID左右,完全滿足需要。 * <p> * 64位ID (42(毫秒)+5(機(jī)器ID)+5(業(yè)務(wù)編碼)+12(重復(fù)累加)) * * @author Polim */public class IdWorker { // 時(shí)間起始標(biāo)記點(diǎn),作為基準(zhǔn),一般取系統(tǒng)的最近時(shí)間(一旦確定不能變動(dòng)) private final static long twepoch = 1288834974657L; // 機(jī)器標(biāo)識(shí)位數(shù) private final static long workerIdBits = 5L; // 數(shù)據(jù)中心標(biāo)識(shí)位數(shù) private final static long datacenterIdBits = 5L; // 機(jī)器ID最大值 private final static long maxWorkerId = -1L ^ (-1L << workerIdBits); // 數(shù)據(jù)中心ID最大值 private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 毫秒內(nèi)自增位 private final static long sequenceBits = 12L; // 機(jī)器ID偏左移12位 private final static long workerIdShift = sequenceBits; // 數(shù)據(jù)中心ID左移17位 private final static long datacenterIdShift = sequenceBits + workerIdBits; // 時(shí)間毫秒左移22位 private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; private final static long sequenceMask = -1L ^ (-1L << sequenceBits); /* 上次生產(chǎn)id時(shí)間戳 */ private static long lastTimestamp = -1L; // 0,并發(fā)控制 private long sequence = 0L; private final long workerId; // 數(shù)據(jù)標(biāo)識(shí)id部分 private final long datacenterId; public IdWorker(){ this.datacenterId = getDatacenterId(maxDatacenterId); this.workerId = getMaxWorkerId(datacenterId, maxWorkerId); } /** * @param workerId * 工作機(jī)器ID * @param datacenterId * 序列號(hào) */ public IdWorker(long workerId, long datacenterId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format('worker Id can’t be greater than %d or less than 0', maxWorkerId)); } if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException(String.format('datacenter Id can’t be greater than %d or less than 0', maxDatacenterId)); } this.workerId = workerId; this.datacenterId = datacenterId; } /** * 獲取下一個(gè)ID * * @return */ public synchronized long nextId() { long timestamp = timeGen(); if (timestamp < lastTimestamp) { throw new RuntimeException(String.format('Clock moved backwards. Refusing to generate id for %d milliseconds', lastTimestamp - timestamp)); } if (lastTimestamp == timestamp) { // 當(dāng)前毫秒內(nèi),則+1 sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { // 當(dāng)前毫秒內(nèi)計(jì)數(shù)滿了,則等待下一秒 timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; // ID偏移組合生成最終的ID,并返回ID long nextId = ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; return nextId; } private long tilNextMillis(final long lastTimestamp) { long timestamp = this.timeGen(); while (timestamp <= lastTimestamp) { timestamp = this.timeGen(); } return timestamp; } private long timeGen() { return System.currentTimeMillis(); } /** * <p> * 獲取 maxWorkerId * </p> */ protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) { StringBuffer mpid = new StringBuffer(); mpid.append(datacenterId); String name = ManagementFactory.getRuntimeMXBean().getName(); if (!name.isEmpty()) { /* * GET jvmPid */ mpid.append(name.split('@')[0]); } /* * MAC + PID 的 hashcode 獲取16個(gè)低位 */ return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1); } /** * <p> * 數(shù)據(jù)標(biāo)識(shí)id部分 * </p> */ protected static long getDatacenterId(long maxDatacenterId) { long id = 0L; try { InetAddress ip = InetAddress.getLocalHost(); NetworkInterface network = NetworkInterface.getByInetAddress(ip); if (network == null) { id = 1L; } else { byte[] mac = network.getHardwareAddress(); id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6; id = id % (maxDatacenterId + 1); } } catch (Exception e) { System.out.println(' getDatacenterId: ' + e.getMessage()); } return id; }}

以上這篇利用mysql實(shí)現(xiàn)的雪花算法案例就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持好吧啦網(wǎng)。

標(biāo)簽: MySQL 數(shù)據(jù)庫
相關(guān)文章:
成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久
欧美一区二区精品在线| 99视频国产精品| gogogo免费视频观看亚洲一| 日本高清免费不卡视频| 亚洲第四色夜色| 国产视频精品网| 亚洲婷婷综合久久一本伊一区| 91一区二区三区在线观看| 日韩欧美一二三| 国产黑丝在线一区二区三区| 欧美日韩国产综合视频在线观看| 日韩高清欧美激情| 久久只有精品| 日韩精品电影在线观看| 国产亚洲精品v| 亚洲乱码国产乱码精品精98午夜 | 国产精品麻豆久久久| 欧美在线网站| 国产日韩欧美精品综合| 欧美在线免费一级片| 国产清纯白嫩初高生在线观看91 | av影院午夜一区| 欧美tickling挠脚心丨vk| 国产 欧美在线| 精品国产伦一区二区三区观看体验 | 欧美日韩久久一区二区| 精品午夜一区二区三区在线观看 | 在线视频你懂得一区| 久久99国产精品免费网站| 91精品一区二区三区久久久久久| 国产成都精品91一区二区三| 欧美电影免费观看高清完整版在线 | 美女图片一区二区| 亚洲aⅴ怡春院| 在线欧美一区二区| 国产在线播放一区三区四| 欧美精品亚洲一区二区在线播放| 国产宾馆实践打屁股91| 久久免费的精品国产v∧| 99久久精品国产麻豆演员表| 国产视频一区不卡| 亚洲日本无吗高清不卡| 天堂av在线一区| 欧美日韩国产经典色站一区二区三区| 国产成人精品www牛牛影视| 国产情人综合久久777777| 极品中文字幕一区| 亚洲1区2区3区视频| 欧美人xxxx| 91小宝寻花一区二区三区| 国产精品私房写真福利视频| 国产精品视频| 狠狠色综合色综合网络| www一区二区| 国产综合婷婷| 五月天亚洲婷婷| 欧美一级在线观看| 欧美婷婷久久| 亚洲高清三级视频| 欧美乱妇20p| 91麻豆国产精品久久| 亚洲免费av高清| 欧美日韩一区精品| 97久久精品人人做人人爽| 亚洲久草在线视频| 欧美日韩国产精品成人| 91免费在线视频观看| 一区二区三区在线视频免费| 欧美无人高清视频在线观看| 成人黄色av电影| 亚洲日本免费电影| 欧美午夜片在线观看| 波多野结衣一区二区三区| 亚洲女性喷水在线观看一区| 欧美日韩国产影片| 国产精品mv在线观看| 日本亚洲电影天堂| 欧美日韩电影一区| 国产一区二区三区自拍| 婷婷综合久久一区二区三区| 日韩一区二区麻豆国产| 伊伊综合在线| 精品一区二区三区在线播放| 国产色产综合色产在线视频| 老鸭窝毛片一区二区三区| 高清不卡在线观看| 一区二区三区免费看视频| 欧美一区二区视频在线观看 | 欧美99久久| 天天综合网天天综合色| 久久综合中文字幕| 色哟哟精品一区| 欧美日韩一视频区二区| 麻豆91在线观看| 国产精品乱人伦中文| 欧美精品v日韩精品v韩国精品v| 亚洲福利专区| 国产成人精品综合在线观看| 一区二区三区在线免费| 91麻豆精品国产91久久久| 日韩一级在线| 北条麻妃国产九九精品视频| 视频一区二区三区入口| 欧美国产精品久久| 欧美人狂配大交3d怪物一区| 亚洲精品日本| 波多野结衣在线一区| 午夜电影网亚洲视频| 久久久精品免费观看| 日本乱人伦aⅴ精品| 欧美婷婷久久| 国产成人啪免费观看软件| 亚洲伊人伊色伊影伊综合网| 精品粉嫩aⅴ一区二区三区四区| 久久国产精品99国产| 午夜精品免费| 国产酒店精品激情| 午夜精品免费在线观看| 中文字幕国产一区| 在线播放国产精品二区一二区四区| 亚洲日本视频| 99re66热这里只有精品3直播| 美女视频黄 久久| 亚洲同性gay激情无套| 精品国产sm最大网站免费看| 在线观看日韩毛片| 99在线精品视频在线观看| 成人黄色av网站在线| 美女一区二区三区| 亚洲综合清纯丝袜自拍| 欧美韩日一区二区三区| 91麻豆精品国产91久久久更新时间| 久久aⅴ国产紧身牛仔裤| 欧美日韩一区在线观看视频| 成人午夜电影小说| 美女视频黄久久| 午夜影视日本亚洲欧洲精品| 国产精品电影院| 久久久不卡网国产精品二区| 欧美精品欧美精品系列| 色婷婷av一区二区三区大白胸| 亚洲国产日韩综合一区| 97精品视频在线观看自产线路二| 久久se精品一区二区| 天天亚洲美女在线视频| 亚洲欧美日韩小说| 欧美国产激情一区二区三区蜜月| 日韩三级在线观看| 欧美人成免费网站| 在线观看91精品国产入口| 亚洲二区在线视频| 亚洲蜜臀av乱码久久精品| 国产精品三级电影| 久久夜色精品国产欧美乱极品| 91精品久久久久久久99蜜桃| 欧美三级日韩在线| 色呦呦网站一区| 亚洲一区二区三区在线观看视频| 亚洲午夜伦理| 国产在线视频欧美一区二区三区| 欧美在线高清| av成人动漫在线观看| 高清久久久久久| 国产成人综合网站| 国产综合一区二区| 国产中文字幕精品| 韩国一区二区三区| 经典三级视频一区| 精品一区二区免费视频| 韩国精品久久久| 激情六月婷婷综合| 狠狠色丁香久久婷婷综合_中| 久久精品国产精品青草| 久久精品国产色蜜蜜麻豆| 麻豆精品一二三| 国内精品国产成人国产三级粉色| 麻豆91在线看| 韩国女主播成人在线观看| 韩国精品免费视频| 国产精品一区二区久久不卡| 国产福利一区二区三区视频| 国产成人在线网站| 成人av网址在线| 97久久精品人人爽人人爽蜜臀| 91免费精品国自产拍在线不卡 | 久久久久久久久久久久久久一区| 国产伦精品一区二区三区高清版 | 国产精品久久久久久模特| 亚洲一区二区在线看| 色就色 综合激情| 欧洲一区二区三区在线| 欧美群妇大交群的观看方式| 91精品国产一区二区三区香蕉| 精品奇米国产一区二区三区| 久久精品水蜜桃av综合天堂| 国产精品久久精品日日| 亚洲另类中文字| 日日摸夜夜添夜夜添国产精品| 黄色日韩网站视频| 成人高清视频免费观看|