分佈式Id生成功能的編寫及優化

項目須要一個分佈式Id生成的功能,原本以爲蠻簡單的東西,就主動接下來了。結果寫的時候才發現,想要寫好並不容易。web

先說下思路,其實很簡單。咱們最經常使用的id生成,就是數據庫的自增主鍵,由於每次都要操做庫,因此效率不高。很天然的就想着每操做一次庫,能夠設置多個Id。因而能夠在程序中緩存一個計數器,當計數值達到設置的值後,在讀庫,更新id。這樣來減小操做庫的次數,提升效率。數據庫

另外,操做庫是一個相對來講耗時的操做,因此它能夠異步來作。這樣就須要在程序中在緩存一個計數器,作二級緩存了,爲了不過多的異步操做,此處可使用線程池。緩存

以下是個人表結構:tomcat

key_name:用於記錄某個應用或某個表生成id的標識併發

start_id:用於每次記錄生成id的起始值框架

step:用於設置,內存中計數的上限,簡稱步長。異步

當時老大讓把這個功能作成一個jar包,給每一個項目用。因此它是分佈式的,爲了防止不一樣的機器在對start_id更改衝突,因此start_id要加樂觀鎖。分佈式

由於程序中的計數器num,是多個線程共享的,因此操做num的方法,也是要加鎖的。性能

可是程序中鎖的粒度很難控制,最終我把鎖加到了方法上,這讓我很鬱悶,由於在入口方法上加鎖它就至關於單線程了。對鎖優化,讓我花費了很常一段時間,最終仍是放棄了。測試

簡單的記錄下。

首先想到的是用讀寫鎖,ReentrantReadWriteLock ,可是對它深刻研究了下,發現讀寫鎖並不適用個人場景。由於每次生成id,會先讀num,而後用startId+num,而後修改num,這三個操做必須是原子的。而讀寫鎖的使用場景,是讀與寫互斥,讀與讀不互斥。

而後我想到的是使用threadLocal,用空間換時間。只能說本身基礎不紮實吧,研究了下,發現也不行。我設想經過threadLocal來設置num,可是這樣,線程並無真正的共享num,生成的id,確定是會重複的。

再而後,就是用線程池,可是感受有些複雜,並且我使用的是Spring框架,個人service是單例的,它就已經至關於一個單線程的線程池了。

好吧,說下效率。公司的測試團隊有點low,我把它打包成web項目,用3臺tomcat壓測,大概3000併發,qps只有12000左右。由於tomcat自己也有性能瓶頸,經過本地測試,不依賴tomcat,qps大概是8W,徹底能夠知足公司的需求。

感受仍是基礎太差,只是仍是要常總結

相關文章
相關標籤/搜索