1. 程式人生 > 實用技巧 >Kubernetes 叢集安全機制詳解(轉)

Kubernetes 叢集安全機制詳解(轉)

一級快取

每當我們使用MyBatis開啟一次和資料庫的會話,MyBatis會創建出一個SqlSession物件表示一次資料庫會話。這個SqlSession物件會建立一個本地快取(local cache),對於每一次查詢,都會嘗試根據查詢的條件去本地快取中查詢是否在快取中,如果在快取中,就直接從快取中取出,然後返回給使用者;否則,從資料庫讀取資料,將查詢結果存入快取並返回給使用者。SqlSession中執行了任何一個update操作(update()、delete()、insert()) ,都會清空PerpetualCache物件的資料,

一級快取的生命週期有多長?

  1. MyBatis在開啟一個數據庫會話時,會 建立一個新的SqlSession物件,SqlSession物件中會有一個新的Executor物件,Executor物件中持有一個新的PerpetualCache物件;當會話結束時,SqlSession物件及其內部的Executor物件還有PerpetualCache物件也一併釋放掉。
  2. 如果SqlSession呼叫了close()方法,會釋放掉一級快取PerpetualCache物件,一級快取將不可用;
  3. 如果SqlSession呼叫了clearCache(),會清空PerpetualCache物件中的資料,但是該物件仍可使用;
  4. SqlSession中執行了任何一個update操作(update()、delete()、insert()) ,都會清空PerpetualCache物件的資料,但是該物件可以繼續使用;

SqlSession 一級快取的工作流程:

  1. 對於某個查詢,根據statementId,params,rowBounds來構建一個key值,根據這個key值去快取Cache中取出對應的key值儲存的快取結果
  2. 判斷從Cache中根據特定的key值取的資料資料是否為空,即是否命中;
  3. 如果命中,則直接將快取結果返回;
  4. 如果沒命中:
    1. 去資料庫中查詢資料,得到查詢結果;
    2. 將key和查詢到的結果分別作為key,value對儲存到Cache中;
    3. 將查詢結果返回;

一級快取的不足:

  使用一級快取的時候,因為快取不能跨會話共享,不同的會話之間對於相同的資料可能有不一樣的快取。在有多個會話或者分散式環境下,會存在髒資料的問題。如果要解決這個問題,就要用到二級快取。MyBatis 一級快取(MyBaits 稱其為 Local Cache)無法關閉,但是有兩種級別可選:

  1. session 級別的快取,在同一個 sqlSession 內,對同樣的查詢將不再查詢資料庫,直接從快取中。
  2. statement 級別的快取,避坑: 為了避免這個問題,可以將一級快取的級別設為 statement 級別的,這樣每次查詢結束都會清掉一級快取。
    來源:https://www.cnblogs.com/wuzhenzhao/p/11103043.html

二級快取:

二級快取是用來解決一級快取不能跨會話共享的問題的,範圍是namespace 級別的,可以被多個SqlSession 共享(只要是同一個接口裡面的相同方法,都可以共享),生命週期和應用同步。如果你的MyBatis使用了二級快取,並且你的Mapper和select語句也配置使用了二級快取,那麼在執行select查詢的時候,MyBatis會先從二級快取中取輸入,其次才是一級快取,即MyBatis查詢資料的順序是:二級快取 —> 一級快取 —> 資料庫。
實際上MyBatis 用了一個裝飾器的類來維護,就是CachingExecutor。如果啟用了二級快取,MyBatis 在建立Executor 物件的時候會對Executor 進行裝飾。CachingExecutor 對於查詢請求,會判斷二級快取是否有快取結果,如果有就直接返回,如果沒有委派交給真正的查詢器Executor 實現類,比如SimpleExecutor 來執行查詢,再走到一級快取的流程。最後會把結果快取起來,並且返回給使用者。

開啟二級快取的方法

第一步:配置 mybatis.configuration.cache-enabled=true,只要沒有顯式地設定cacheEnabled=false,都會用CachingExecutor 裝飾基本的執行器。

第二步:在Mapper.xml 中配置<cache/>標籤:

<cache type="org.apache.ibatis.cache.impl.PerpetualCache"
    size="1024" eviction="LRU" flushInterval="120000" readOnly="false"/>

基本上就是這樣。這個簡單語句的效果如下:

  • 對映語句檔案中的所有 select 語句的結果將會被快取。
  • 對映語句檔案中的所有 insert、update 和 delete 語句會重新整理快取。
  • 快取會使用最近最少使用演算法(LRU, Least Recently Used)演算法來清除不需要的快取。
  • 快取不會定時進行重新整理(也就是說,沒有重新整理間隔)。
  • 快取會儲存列表或物件(無論查詢方法返回哪種)的 1024 個引用。
  • 快取會被視為讀/寫快取,這意味著獲取到的物件並不是共享的,可以安全地被呼叫者修改,而不干擾其他呼叫者或執行緒所做的潛在修改。
readOnly(只讀)屬性可以被設定為 true 或 false。只讀的快取會給所有呼叫者返回快取物件的相同例項。 因此這些物件不能被修改。這就提供了可觀的效能提升。而可讀寫的快取會(通過序列化)返回快取物件的拷貝。 速度上會慢一些,但是更安全,因此預設值是 false。

Mapper.xml 配置了<cache>之後,select()會被快取。update()、delete()、insert()會重新整理快取。:如果cacheEnabled=true,Mapper.xml 沒有配置標籤,還有二級快取嗎?(沒有)還會出現CachingExecutor 包裝物件嗎?(會)

  只要cacheEnabled=true 基本執行器就會被裝飾。有沒有配置<cache>,決定了在啟動的時候會不會建立這個mapper 的Cache 物件,只是最終會影響到CachingExecutorquery 方法裡面的判斷。如果某些查詢方法對資料的實時性要求很高,不需要二級快取,怎麼辦?我們可以在單個Statement ID 上顯式關閉二級快取(預設是true):

<select id="selectBlog" resultMap="BaseResultMap" useCache="false">

二級快取驗證(驗證二級快取需要先開啟二級快取)

1、事務不提交,二級快取不存在

System.out.println(mapper1.selectBlogById(1002));
// 事務不提交的情況下,二級快取不會寫入
// session1.commit();
System.out.println(mapper2.selectBlogById(1002));
為什麼事務不提交,二級快取不生效?因為二級快取使用TransactionalCacheManager(TCM)來管理,最後又呼叫了TransactionalCache 的getObject()、putObject 和commit()方法,TransactionalCache裡面又持有了真正的Cache 物件,比如是經過層層裝飾的PerpetualCache。在putObject 的時候,只是新增到了entriesToAddOnCommit 裡面,只有它的commit()方法被呼叫的時候才會呼叫flushPendingEntries()真正寫入快取。它就是在DefaultSqlSession 呼叫commit()的時候被呼叫的。

2、使用不同的session 和mapper,驗證二級快取可以跨session 存在取消以上commit()的註釋

3、在其他的session 中執行增刪改操作,驗證快取會被重新整理

System.out.println(mapper1.selectBlogById(1002));
//主鍵自增返回測試
Blog blog3 = new Blog();
blog3.setBid(1002);
blog3.setName("mybatis快取機制");
mapper1.updateBlog(blog3);
session1.commit();
System.out.println(mapper2.selectBlogById(1002));

什麼時候開啟二級快取?

一級快取預設是開啟的,二級快取需要配置才可以開啟。那麼我們必須思考一個問題,在什麼情況下才有必要去開啟二級快取?

  1. 因為所有的增刪改都會重新整理二級快取,導致二級快取失效,所以適合在查詢為主的應用中使用,比如歷史交易、歷史訂單的查詢。否則快取就失去了意義。
  2. 如果多個namespace 中有針對於同一個表的操作,比如Blog 表,如果在一個namespace 中重新整理了快取,另一個namespace 中沒有重新整理,就會出現讀到髒資料的情況。所以,推薦在一個Mapper 裡面只操作單表的情況使用。
如果要讓多個namespace 共享一個二級快取,應該怎麼做?跨namespace 的快取共享的問題,可以使用<cache-ref>來解決:
<cache-ref namespace="com.wuzz.crud.dao.DepartmentMapper" />

cache-ref 代表引用別的名稱空間的Cache 配置,兩個名稱空間的操作使用的是同一個Cache。在關聯的表比較少,或者按照業務可以對錶進行分組的時候可以使用。

  注意:在這種情況下,多個Mapper 的操作都會引起快取重新整理,快取的意義已經不大了.

第三方快取做二級快取

  除了MyBatis 自帶的二級快取之外,我們也可以通過實現Cache 介面來自定義二級快取。MyBatis 官方提供了一些第三方快取整合方式,比如ehcache 和redis:https://github.com/mybatis/redis-cache ,這裡就不過多介紹了。當然,我們也可以使用獨立的快取服務,不使用MyBatis 自帶的二級快取。

除了上述自定義快取的方式,你也可以通過實現你自己的快取,或為其他第三方快取方案建立介面卡,來完全覆蓋快取行為。

<cache type="com.domain.something.MyCustomCache"/>

  這個示例展示瞭如何使用一個自定義的快取實現。type 屬性指定的類必須實現 org.mybatis.cache.Cache 介面,且提供一個接受 String 引數作為 id 的構造器。 這個介面是 MyBatis 框架中許多複雜的介面之一,但是行為卻非常簡單。

public interface Cache {
      String getId();
      int getSize();
      void putObject(Object key, Object value);
      Object getObject(Object key);
      boolean hasKey(Object key);
      Object removeObject(Object key);
      void clear();
}

為了對你的快取進行配置,只需要簡單地在你的快取實現中新增公有的 JavaBean 屬性,然後通過 cache 元素傳遞屬性值,例如,下面的例子將在你的快取實現上呼叫一個名為setCacheFile(String file)的方法:

<cache type="com.domain.something.MyCustomCache">
  <property name="cacheFile" value="/tmp/my-custom-cache.tmp"/>
</cache>

  你可以使用所有簡單型別作為 JavaBean 屬性的型別,MyBatis 會進行轉換。 你也可以使用佔位符(如${cache.file}),以便替換成在配置檔案屬性中定義的值。從版本 3.4.2 開始,MyBatis 已經支援在所有屬性設定完畢之後,呼叫一個初始化方法。 如果想要使用這個特性,請在你的自定義快取類裡實現org.apache.ibatis.builder.InitializingObject

口。

public interface InitializingObject {
  void initialize() throws Exception;
}

  請注意,快取的配置和快取例項會被繫結到 SQL 對映檔案的名稱空間中。 因此,同一名稱空間中的所有語句和快取將通過名稱空間繫結在一起。 每條語句可以自定義與快取互動的方式,或將它們完全排除於快取之外,這可以通過在每條語句上使用兩個簡單屬性來達成。 預設情況下,語句會這樣來配置:

<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>

  鑑於這是預設行為,顯然你永遠不應該以這樣的方式顯式配置一條語句。但如果你想改變預設的行為,只需要設定 flushCache 和 useCache 屬性。比如,某些情況下你可能希望特定 select 語句的結果排除於快取之外,或希望一條 select 語句清空快取。類似地,你可能希望某些 update 語句執行時不要重新整理快取。

參考:

mybatis快取機制