背景:筆者負責公司内部單點登入系統(inpass)已兩年有餘,登入服務接入逐漸增多,登入日志逐漸增多。筆者對登入日志也進行了幾輪改造和優化,其中涉及到了一次簡單的分表操作,特此記錄下來。
改造之前的狀況(2018.7月調研)
inpass日志表中id采用工具類(SnowflakeIdWorker)自動生成id,但是存在高并發下id重複問題,資料庫插入存在主鍵沖突,導緻業務方調用出現異常。同時原有資料庫表login_log中資料量已達400w ,統計最近4天的登入日志,平均每天16000條,且有逐漸增多的趨勢,是以有必要進行優化。當時調研了三種方案,如下:
方案1:
将id生成器工具類替換為使用共享服務部提供的ID生成器服務,其他不變。優點:可快速優化高并發下id重複問題,代碼量比較少缺點:需要增加依賴服務,無法對現有表資料進行拆分優化。
方案2:
重新建構日志表(loginlognew),表字段不變,id改為資料庫自增方式。優點:
- 不依賴外部id唯一工具,
-
可拆分登入日志,減少原有表資料逐漸增大的問題。
缺點:
需要建立表。
方案3:
删除目前日志表(login_log)中3個月之前的資料 方案1.優點:不需要建立表,缺點:線上删除大資料量可能會鎖表,同時會丢失大量日志資料。綜合考慮,方案2為優。那上面的方案是否是一勞永逸的呢?答案是否定的。
我們設想一下,現在的日均日志量已經不是16000 ,而是18000 了,假如以一年為機關,16000*365 = 584W條。18000*365 = 657W條。資料庫是MySQL的,場景是插入多,查詢少,比例與插入量差不多,但是如果要查的話,那看下面的sql:select count(*) from login_log where login_time >='2018-07-24 00:00:00' and login_time <='2018-07-24 23:59:59';這個sql至少執行了3.8秒。應該是可以優化的。
要查該表的背景
- 某員工違規操作,要查日志,什麼時間登入的,登入到哪個IP位址。
-
某員工最近一個月通過inpass登入系統的日志。
這種場景非常少,但是在單表這麼多資料量的情況下一條sql執行下來估計也需要不少時間。
可以借用分表的設計想法,(分庫就不用了哈,沒那麼多複雜的需求),以年為機關,預先設計3張表loginlog2018(已初始化到線上),loginlog2019(已初始化到線上),loginlog2020(未初始化到線上).假設每張表存儲不超過1KW條資料,以年為機關記,即可滿足未來3年内的日志增長量。同時滿足所有日志可查。
按上述方案執行之後,到現在已經往loginlog2018表寫入了480w登入日志資料了,其實已經到了需要寫新表的時候了,此時也接到了需要對inpass進行内審安全合規性的改造,使用者需要對登入日志進行查詢。新的需求需要對登入過程中的所有操作進行記錄,包括修改密碼等環節。而原來的登入日志記錄僅僅記錄登入成功的場景,是以如果再寫入資料庫就不合适了。
此時需要進行新的改造,由于inpass隻是提供單點登入和修改密碼的功能,沒有查詢界面,如果給使用者新開清單查詢界面也不合适。經過和上司溝通,有如下操作和方案
- 将登入日志以埋點的方式接入公司的日志平台進行上報。
- inpass服務端使用線程池異步寫日志(不阻塞登入主流程)
- 由于接入日志平台之後日志平台可以提供查詢界面,是以inpass的改造僅僅在用日志埋點的接入,但是為了保障整個接入過程順利還進行了雙寫的操作。
- 就是一方面将登入日志上報到日志平台,一方面寫入資料庫,此時需要建立表,因為新增了幾個記錄項,不适合在原有表中擴充,新表為loginlog2020.
-
第4步的操作也為了保障在接入過程中資料可以備援保留,如果日志平台确實能滿足使用者查詢需求,我們可以很快下掉寫資料庫的邏輯。
架構設計@工程設計@服務穩定性之路