2020/1/31

Redis資料庫+分佈鎖 - 基於Redis資料庫實現分佈鎖(二)

  • 資料來源
  • 基於Redis資料庫實現分佈鎖
    • 基於分佈式環境才有分佈鎖之問題
    • 需要以原子操作
    • 無法使用JVM級別的鎖
    • 將併發進行串行化(多路復用)操作,缺點就是無法支持高併發
    • 可使用ZK鎖以決定Redis主從資料庫的資料一致性
  • 情境如下
    • 庫存數量10
    • 下單後庫存-1
    • 使用jmeter壓測
    • 單例模式 (僅一個tomcat處理)
  • 演變1
    • 邏輯 : 單純將庫存-1
    • 缺點 : 扣減錯誤 , 單機環境也可使用 sychronize鎖解決 , 但分布式環境併發仍會扣減錯誤

  • 演變2
    • 邏輯 : 使用redis setnx() or incr() 決定線呈是否取得鎖=1 , 結束後還原=0
    • 缺點 : 部分線程雖優先順序較高 , 但無法取得鎖 ,造成後續扣減無法完成
    • 缺點 : 如果程式執行發生異常或web應用掛機 , 鎖無法取消

  • 演變3
    • 邏輯 : 將鎖設置超時時間
    • 缺點 : 後面線程將前面線程的鎖解除
    • 缺點 : 超時時間不好預估

  • 演變4
    • 邏輯 : 使用redission框架 ,  設置後台子線程 , 監測主線程是否還在執行, 如果還在執行則延遲超時時間 , 其他線程沒有取得鎖則循環嘗試加鎖
    • 缺點 : 自旋處理有其缺點及適用場景


  • Redission框架
    • 可立即使用在分布式鎖環境
    • 悲觀鎖 : 僅有取得鎖的線程可持續加鎖 , 其他線程要取得鎖必須等待 , 有性能問題
    • 實現方法


2020/1/30

多執行續處理


  • 參考網址
  • 多執行續演變
    • <= JDK 1.4
      • synchronize
      • volatile
      • 不變性
    • = JDK 1.5
      • Thread Pool
      • AIO
      • Excutor
      • Concurrent包
  • 創建執行續的4種方式
    • Thread
    • Runnable
    • Callable
      • 可以有返回值 , 由FutureTask支持接收結果 , get()->阻塞
    • ThreadPool
      • 如果沒有執行shutdown() , 執行續會持續進行
      • 使用runnable
      • 使用callable
      • 使用調度線程池
  • 多執行續的 可見性、原子性、有序性問題
    • 可見性 : 基於Java內存模型 , 每一個線程皆會從主存存取共用變數值至工作內存中進行運算 , 導致A線程已修改數據,B線程仍使用舊資料進行運算  , 因此有 Volatile修飾字的解決辦法
    • 原子性 : 多個線程存取同一共用變數 , 同時進行"修改"所導致的問題 (Concurrent包)
    • 有序性 : JVM因考量效能 , 在解析代碼會將沒有順序關係的變數進行順序調度 , 導致非預期結果
  • Volatile 參考
    • 保證共享(主)內存刷新
    • 符合有序性、可見性
    • 不符合原子性
  • Concurrent包
    • 用於處理多執行續的
    • 1.8之後符合CAS(Compare&Swap)演算法(原子性) , 使用native method硬體對併發的支持
      • CAS原理
        • 內存值V
        • 預估值A
        • 更新值B
        • If V==A , V=B
      • 屬於一種樂觀鎖機制
    • 含原子操作用的Atomic api
    • 常見之Class , 介於Collection之非執行續安全類別~執行續安全類別之間
      • ConcurrentHashMap
        • 默認分成16段 , 分段加鎖
      • ConcurrentSkipListMap
      • ConcurrentSkipListSet
      • CopyOnWriteArrayList
      • CopyOnWriteArraySet
  • 常見多執行續"鎖"之種類介紹
    • 悲觀鎖
      • 當對象被線程鎖上時,其餘線程必須等待鎖釋放才可存取
      • 例如 synchronize
      • 缺點沒控制好性能通常較差
    • 樂觀鎖
      • 當未完成操作時 , 進行重試
      • 例如 CAS進行硬件級別鎖
      • 缺點ABA問題 , 循環開銷大 , 一次只能保證一個共享變數(建議使用synchronize)
    • 閉鎖操作
      • CountDownLatch用於等待多線程操作完成
      • 當計數器=0時代表開鎖
    • 同步鎖
      • synchronize區塊(隱式鎖)
      • synchronize方法(隱式鎖)
      • Lock(顯示鎖、樂觀鎖)
        • 使用lock() 、 unlock()定義同步區塊
        • 搭配condition生成await()、signal()、signalAll()
        • Object原生的wait()如同await()建議放在while回圈內,防止虛假喚醒
      • 補充
        • synchronize屬於對象鎖 , 影響單一對象 , 如果另外生成對象則不影響 
        • static synchronize屬於類別鎖 , 影響全部對象 
        • synchronize與static synchronize兩者為獨立運行 , 各別控制運行
    • 讀寫鎖
      • 寫寫/讀寫兩種線程必須互斥 , 讀寫分離
      • 一個 readLock() , 一個 WriteLock()
  • Fork/Join框架




2020/1/29

MySQL SQL優化

    1. 開啟慢查詢日誌 , 觀察一天或重現事故
    2. Explain + 慢查詢SQL
    3. Show Profile分析SQL生命週期
    4. DB參數優化
  • 慢查詢日誌
    • 使用慢查詢日誌 , 紀錄查詢效能差的SQL
    • 當查詢時間超過long_query_time時寫入日誌,默認時間為10秒
    • 開啟慢查詢日誌 , 設置後須重新連線DB

    • 永久配置
    • 查看慢查詢累積筆數
    • 慢查詢分析日誌 mysqldumpslow
      • 參數
      • 範例
  • Explain + 慢查詢SQL
    • 使用Explain查看執行計畫 , 優化SQL
    • SQL優化策略 (Index索引為其重點)
      • 查詢執行計畫 : explain select ............最重要之4個欄位
        • Id : 執行順序 , id越大優先執行 , id值相等則上方列優先
        • type : 查詢等級System>const>eq_ref>ref>range>index>all
          • 大多是ref
          • 至少要range
          • all為全表掃描
        • rows : 預估要查詢的列數 , 越少越好
        • extra : 其他index>filesort>temporary
          • 至少要index
      • SQL優化策略
        • Index優化策略
          • select和where欄位皆可使用
          • MySQL CRUD皆會做查詢的動作, 因此Index索引決定效能
            • MyIsam引擎將資料與索引拆分成兩份檔案InnoDB為同一份檔案查詢效能較好
            • 建議PK使用數值或自動產生流水號查詢效能較文字高
            • 建議一定要加PK , 否則默認由DB自己判斷生成RowId
          • MySQL 預設使用 B+Tree資料結構儲存索引
            • 根據官方分析過,預設高度為3
            • 完成資料data存在於最後一層索引上 , 與其他資料結構僅儲存磁碟位置不同
            • Mysql另外有一種Hash資料結構, 較適合等值where條件
          • 組合索引必須依序讀取/設置
          • range索引(>= , > , < , <=)導致後續的索引失效 , 故可將此欄位放置在最後面
          • 欄位不使用函數、計算
          • 當使用left join時 , index加在右表 , 反之亦然
          • 少使用select * , 使用覆蓋索引(欄位順序個數跟Index完全相同)效能佳
          • 少用 OR
          • Varchar必用單引號 , 以免索引失效
          • 不使用 >< ,  != , 以免索引失效
          • 不使用 is null , is not null , 以免索引失效,  使用 ''代替null欄位
          • Like必須使用 'X%'以免索引失效 , 否則可用覆蓋索引彌補
        • 查詢優化策略
          • join buffer大小可以視情況調整
          • 小表(A)驅動大表(B)查詢 , B in (A) 或 A exist (B)
        • Order By優化策略
          • 可搭配Index左前戳開頭規則 , 建立組合索引
          • 不可使用 select *
          • 所有排序欄位必須一致遞升或遞減
          • sort_buffer_size不足會導致多次I/O臨時表多路合併 (這是每個進程都有一份)
          • max_length_for_sort_data不足會導致臨時表與多路合併
          • 當max_length_for_sort_data設置過大會導致sort_buffer_size不足
          • 當查詢筆數<max_length_for_sort_data且排序欄位並非text|blob , 使用單路排序
        • Group By優化策略
          • 實質上是先排序後分組
          • Order By索引規則相同
          • 當無法使用索引 , 則增加sort_buffer_size和max_length_for_sort_data
        • Order By優化策略
    • Show Profile分析SQL生命週期
      • 分析SQL執行細節和生命週期 , 查出問題點
      • 開啟profile功能
      • 查詢SQL執行明細過程
      • 可查詢欄位 , 以及Query Id
      • 診斷方式
        •  Status欄位不得出現之訊息 , 將嚴重影響效能

    JVM構造(上)


    • 參考資料
    • JVM
      • Heap堆為JVM主要調優對象 , Full GC會造成STW(stop-the-world),因此Full GC越少越好
        • minor gc : 僅年輕代GC
        • full gc : 年輕代+老年代全局GC
      • Heap預設分配
        • 老年代 2/3
        • 年輕代 1/3
        • Eden 8/10*1/3
        • from 1/10*1/3
        • to 1/10*1/3
      • 倖存下來的對象 , 在from/to間搬移次數達15次,即可放置於老年代 , 如from/to空間不足則立即放置於老年代
      • 可用 ...jdk/bin/jvisualvm 圖形化介面查看記憶體狀態
        • 可另外裝 visual gc
      • 調優步驟
        • 開啟打印 gc log (tomcat加在 JAVA_OPTS裡)
          • -XX:+PrintGCDetails
          • -XX:+PrintGCTimeStamps
          • -XX:+PrintGCDateStamps
          • -Xloggc:/gc.log
    • JMM(Java內存模型)
      • Java內存模型是基於CPU模型之設計
      • Java內存模型
        • 每項線程從主內存取得資料副本以便運算,也因此造成多執行續在運算共享變量資料不一致性的問題 , 因此演變至Volatile補足一致性(可見性)問題
      • 字節碼
        • # javap -c (簡易)
        • # javap -v (明細)
        • Java內存模型的共8種原子操作,取得CPU使用權時才可操作

      • JMM演變
        • 演變1 : 透過總線加鎖
          • 對主存變量Lock/Unlock
          • 須等線程使用完,其他線程必須等待
          • 缺點性能太低
        • 演變2 : MESI一致性協議
          • 當其中一個線程變更主存的共享變量值 , 透過總線嗅探(監聽)機制通知其他線程
          • 缺點多線程在經過總線前會有時間差,導致資料不一致
        • 演變3 : Volatile
          • 對總線Lock , 共享變量修改完畢後總線Unlock
          • Volatile符合併發的有序性、可見性  
          • Volatile不符合併發的原子性

    高併發簡介


    • 蔡學鏞文章
    • 高併發設計重點
      • 高速 : 減少I/O,尤其硬體I/O
      • 大量 : 緩衝區
    • 高速
      • 記憶體運算技術 , 缺點即斷電後無法持久化
      • Event Sourcing儲存
        • 紀錄從帳戶開戶以來的所有操作,必須把操作記錄都累計起來,才會得到當前的帳戶狀態
        • 直接紀錄到事件日誌的尾端,不需要搜尋、定位、調整索引等附帶的行為
        • 使用快照, 存放當前用戶狀態
        • 無法像RDBMS那樣做很複雜的各種關聯查詢
    • 大量
      • 緩衝空間可以採用經典的環狀結構
      • 部署多套一樣的系統,同時接收相同的業務請求,多套系統要把請求的次序統一起來,但只有其中一套真正會進行計算
    • 參考範圍
      • JMM模型
      • SQL優化
      • JVM優化
      • 分布式環境設計
        • Tomcat應用水平擴充 , 設置分流增加流量處理
        • 將串行鎖變成並行鎖 , 將大鎖切成小鎖 , 增加流量處理


    • 架構思維順序
      • 業務
      • 技術
      • 組織
    • 架構12原則 (principles)
      • 架構設計的規範、原則
      • 架構原則1不要使用Stored Procedure,因為這會讓業務邏輯難以維護
      • 架構原則2一個系統內部可以包含儲存和程式碼,但系統間不能共用資料庫。不管寫入或讀出由單一系統控制,系統之間的依賴只透過API
      • 架構原則3邏輯容易變動的程式碼必須剝離成另一個系統,容易變動者(例如應用系統)可以依賴較不容易變動者(例如平台系統)
      • 架構原則4任何系統都不能依賴容易變動的系統
      • 架構原則5被調用方必須提供清晰、文件化的API
      • 架構原則6使用者界面要被剝離出來,且使用者界面內盡量不要有邏輯
      • 架構原則7調用外部廠商的系統時必須只依賴SPI(Service Provider Interface,服務提供者界面),不依賴具體的系統。
      • 架構原則8業務邏輯的程式碼必須區分服務(service)和物件(object),服務沒有狀態,物件有狀態,服務操作物件,物件的狀態記錄在資料庫(和外部系統)
      • 架構原則9服務不能直接讀寫資料(和外部系統)
      • 架構原則10物件狀態的保存方式必須做出隔離,也就是提供資料隔離層
      • 架構原則 11充血模型才是好的物件模型。且設計模型時,要考慮是否有強一致性的要求
      • 架構原則 12禁止循環依賴

    2020/1/21

    效能寫法小記錄


    • 減少重複呼叫
      • for (int i = 0, int length = list.size(); i < length; i++)
    • 執行續同步使用StringBuilder,並給定適當大小
      • StringBuilder() // 預設分配16個字元的空間
      • StringBuilder(int size) // 預設分配size個字元的空間
      • StringBuilder(String str)  // 預設分配16個字元+str.length()個字元空間
      • 當StringBuilder達到最大容量的時候,它會將自身容量增加到當前的2倍再加2,無論何時只要StringBuilder達到它的最大容量,它就不得不建立一個新的字元陣列然後將舊的字元陣列內容拷貝到新字元陣列中,這是十分耗費效能的一個操作
    • HashMap這種是以陣列+連結串列實現的集合,別把初始大小和你估計的大小設定得一樣,因為一個table上只連線一個物件的可能性幾乎為0。初始大小建議設定為2的N次冪,如果能估計到有2000個元素,設定成new HashMap(128)、new HashMap(256)都可以。
    • 當複製大量資料時,使用System.arraycopy()命令 .
    • 移位運算
      • << X   *2^X
      • >> X   /2^X
    • 減少物件引用次數
    • 儘可能使用array,無法確定陣列大小時才使用ArrayList
    • ArrayList 的Interface RandomAccess , 官方建議使用for 

       for (int i=0, n=list.size(); i < n; i++)
               list.get(i);
      如果是有順序性的 sequential 
           for (Iterator i=list.iterator(); i.hasNext(); )
               i.next();
    • 程式執行過程中避免使用反射
      • 反射是Java提供給使用者一個很強大的功能,功能強大往往意味著效率不高。不建議在程式執行過程中使用尤其是頻繁使用反射機制,特別是Method的invoke方法,如果確實有必要,一種建議性的做法是將那些需要通過反射載入的類在專案啟動的時候通過反射例項化出一個物件並放入記憶體,使用者只關心和對端互動的時候獲取最快的響應速度,並不關心對端的專案啟動花多久時間。

















    MariaDB指令



      • 備份&還原1
        • # mysqldump -u root -p --all-databases > backup.dump
        • # mysql -u root -p < backup.dump
      • 備份&還原2
        • # mysql -u root -p
        • # source back.sql
      • 將mysql資料轉移到mariadb後
        • # mysql_upgrade --verbose
      • mariadb 一般還原
        • # mysql_upgrade -u root -p
      • 當前版本訊息
        • # \s
      • 用戶識別 , mariadb會優先使用'user'@'192.168.1.1'
        • 'user'@'192.168.1.1'
        • 'user'@'%'
      • 賦予權限
        • # grant [select,insert....all] on *.* to 'user'@'%' identified by 'password' with grant option;
        • identified  by  =>定義受訪密碼
        • with grant option => 為其他客戶授權的權限 

      Redis資料庫+分佈鎖 - 簡介(一)


      • Redis資料庫特性
        • 單線程操作
        • 非阻塞I/O多路復用機制
        • 所有操作都是原子性
        • key-value nosql資料庫
        • 數據類型
          • String(字符串)
          • List(列表)
          • Hash(字典)
          • Set(集合)
          • Sorted Set(有序集合)
          • HyperLogLog(基数统计用)

        • 資料可存於內存中,速度非常快。官方提供的數據表明,在一個普通的linux機器上,Redis讀寫速度分別達到81000/s和110000/s , 
        • 持久化 , 異步保存到磁盤上 , 保存數據到磁盤可以避免斷電時導致一段時間內的數據丟失(memcacache不能持久化,mongo是部分在內存)
          • 內存存儲
          • 磁盤存儲
          • log文件
        • 主-從復制
          • 無論是第一次同步建立的連接還是連接斷開後的重新連接,master都會將數據庫快照保存到文件中,同時master主進程會開始收集新的寫命令並緩存起來。
          • 後臺進程完成寫文件後,master就發送文件給slave,slave將文件保存到磁盤上,然後加載到內存恢復數據庫快照到slave上。
          • 接著master就會把緩存的命令轉發給slave。而且後續master收到的寫命令都會通過開始建立的連接發送給slave。
        • Sharding數據分布到多個Redis實例中
      • 文件格式
        • 全量數據將數據結果儲存
        • 增量請求將數據序列化為操作請求,用於讀取文件進行replay得到數據
      • 持久化方式
        • Snapshotting(RDB)(默認) 
          • 在n秒內如果超過m個key被修改就自動做快照
        • Append-only file (AOF)
          • 將每一個收到的寫命令都通過fsync函數 , 每X秒強制write函數追加到文件中,當redis重啟時會通過重新執行文件中保存的寫命令來在內存中重建整個數據庫的內容。 
      • Redis應用場景
        • 高命中率的資料
        • 用List取最新的X筆資料
        • 用Sorted Set進行排行榜
        • 用Sorted Set進行過期數據處理
        • 用set重複更新資料
        • 用Pub/Sub系統可以構建實時的消息系統
        • 用list可以構建隊列系統
        • 用Sorted Sets範圍查找
          • 例如服務端對於客戶端IP不同的版本區間會做些不同的配置
        • 用set做交集,並集,差集
      • Redis實用命令
        • Keys * : 返回所有的key,* 可使用正則表達式查詢
        • Type key :返回key的類型(string ,zset ,list)
        • Select 1 : 選擇第一個數據庫;默認0-15個數據庫;默認是第0個數據庫
        • Dbsize : 當前數據庫中的key的個數
        • Monitor : 監控收到的請求
      • 分佈鎖演化
        • 基於Redis資料庫實現分佈鎖
        • 基於Redisson框架實現分佈鎖
        • Redis資料庫主從架構鎖失效問題
        • 高併發分布式鎖如何實現

      2020/1/20

      Shiro Framework - 緩存管理(十)


      • 資料來源1
      • 資料來源2
      • 資料來源3
      • 緩存應用
        • 如果繼承shiro的realm類,幾乎都已經繼承CacheManagerAware接口並已有緩存機制
        • 任何實現了CacheManagerAware 的Shiro 組件將會自動地接收一個配置好的CacheManager,該CacheManager 能夠用來獲取Cache 實例
      • 如果在SecurityManager上設置了CacheManger,它反過來也會將它設置到實現了CacheManagerAware的各種不同的Realm上。
      • 設定實現


      • 設定中斷點 , 登錄後重複訪問 "/hello" , 第一次會進入中斷點 , 第二次後不會 , 即完成緩存

      Shiro Framework - "RememberMe"管理(九)


      • 資料來源
      • Shiro提供記住我功能
        • 一般會將Cookie寫到客戶端並保存
        • 瀏覽器重新打開仍能記住身分,但對於敏感網頁不得保留
      • 實現效果
        • 因為要使用瀏覽器 , 登入改用 http get method
        • 建立訪問權限與method


        • 登錄

        • 關閉瀏覽器 測試user訪問權限


        • 關閉瀏覽器 測試athuc訪問權限

      • 增加10秒後到期

      2020/1/19

      Shiro Framework - 會話管理(八)

      • 資料來源
      • Shrio提供的會話管理功能 
        • 類似 http session
        • 支持JavaSE、JavaEE
        • 事件監聽
        • 持久化
        • 容器無關的集群
        • 時效性
        • SSO單點登入
        • web不管在controller、service...等皆可取得session
      • session監聽

      • session狀態
      • session控制

      • 測試在service取得session , 交由spring管理
        • 增加service程式
        • 取值 "testsession"
        • 修改controller
        • 登入時塞值 "testsession"
        • 呼叫/hello 取值
        • 測試結果




      test2