Kubernetes 叢集安全機制詳解(轉)
一級快取
每當我們使用MyBatis開啟一次和資料庫的會話,MyBatis會創建出一個SqlSession物件表示一次資料庫會話。這個SqlSession物件會建立一個本地快取(local cache),對於每一次查詢,都會嘗試根據查詢的條件去本地快取中查詢是否在快取中,如果在快取中,就直接從快取中取出,然後返回給使用者;否則,從資料庫讀取資料,將查詢結果存入快取並返回給使用者。SqlSession中執行了任何一個update操作(update()、delete()、insert()) ,都會清空PerpetualCache物件的資料,
一級快取的生命週期有多長?
- MyBatis在開啟一個數據庫會話時,會 建立一個新的SqlSession物件,SqlSession物件中會有一個新的Executor物件,Executor物件中持有一個新的PerpetualCache物件;當會話結束時,SqlSession物件及其內部的Executor物件還有PerpetualCache物件也一併釋放掉。
- 如果SqlSession呼叫了close()方法,會釋放掉一級快取PerpetualCache物件,一級快取將不可用;
- 如果SqlSession呼叫了clearCache(),會清空PerpetualCache物件中的資料,但是該物件仍可使用;
- SqlSession中執行了任何一個update操作(update()、delete()、insert()) ,都會清空PerpetualCache物件的資料,但是該物件可以繼續使用;
SqlSession 一級快取的工作流程:
- 對於某個查詢,根據statementId,params,rowBounds來構建一個key值,根據這個key值去快取Cache中取出對應的key值儲存的快取結果
- 判斷從Cache中根據特定的key值取的資料資料是否為空,即是否命中;
- 如果命中,則直接將快取結果返回;
- 如果沒命中:
- 去資料庫中查詢資料,得到查詢結果;
- 將key和查詢到的結果分別作為key,value對儲存到Cache中;
- 將查詢結果返回;
一級快取的不足:
使用一級快取的時候,因為快取不能跨會話共享,不同的會話之間對於相同的資料可能有不一樣的快取。在有多個會話或者分散式環境下,會存在髒資料的問題。如果要解決這個問題,就要用到二級快取。MyBatis 一級快取(MyBaits 稱其為 Local Cache)無法關閉,但是有兩種級別可選:
- session 級別的快取,在同一個 sqlSession 內,對同樣的查詢將不再查詢資料庫,直接從快取中。
- 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 個引用。
- 快取會被視為讀/寫快取,這意味著獲取到的物件並不是共享的,可以安全地被呼叫者修改,而不干擾其他呼叫者或執行緒所做的潛在修改。
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));
什麼時候開啟二級快取?
一級快取預設是開啟的,二級快取需要配置才可以開啟。那麼我們必須思考一個問題,在什麼情況下才有必要去開啟二級快取?
- 因為所有的增刪改都會重新整理二級快取,導致二級快取失效,所以適合在查詢為主的應用中使用,比如歷史交易、歷史訂單的查詢。否則快取就失去了意義。
- 如果多個namespace 中有針對於同一個表的操作,比如Blog 表,如果在一個namespace 中重新整理了快取,另一個namespace 中沒有重新整理,就會出現讀到髒資料的情況。所以,推薦在一個Mapper 裡面只操作單表的情況使用。
<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 語句執行時不要重新整理快取。