1. 程式人生 > 資訊 >榮耀推出多主攝融合計算攝影,趙明稱拍照效果相比蘋果 iPhone 13 系列更優

榮耀推出多主攝融合計算攝影,趙明稱拍照效果相比蘋果 iPhone 13 系列更優

簡介

什麼是快取

  • 存在記憶體中的臨時資料。
  • 將使用者經常查詢的資料放在快取(記憶體)中,使用者去查詢資料就不用從磁碟上(關係型資料庫資料檔案)查詢,轉從快取中查詢,從而提高查詢效率,解決了高併發系統的效能問題。

為什麼使用快取

  • 減少和資料庫的互動次數,減少系統開銷,提高系統效率。

什麼樣的資料適合使用快取

  • 經常查詢並且不經常改變的資料。

Mybatis快取

  • MyBatis 包含一個非常強大的查詢快取特性,它可以非常方便地定製和配置快取。

  • MyBatis 系統中預設定義了兩級快取:一級快取二級快取

    • 預設情況下,只有一級快取開啟。( SqlSession 級別的快取,也稱為本地快取)
    • 二級快取需要手動開啟和配置,他是基於 namespace 級別的快取。
    • 為了提高擴充套件性,MyBatis 定義了快取介面 Cache。我們可以通過實現 Cache 介面來自定義二級快取。

一級快取

一級快取也叫本地快取:

  • 與資料庫同一次會話期間查詢到的資料會放在本地快取中。
  • 以後如果需要獲取相同的資料,直接從快取中拿,沒必須再去查詢資料庫;

測試

先在mybatis中加入日誌,方便測試結果。

@Test
public void oneCache() {
    SqlSession sqlSession = MybatisUtils.getSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User user1 = userMapper.selectOne(1);
    System.out.println(user1);
    User user2 = userMapper.selectOne(1);
    System.out.println(user2);
    System.out.println(user1 == user2);
    sqlSession.close();
}

列印結果

Opening JDBC Connection
Created connection 1806431167.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6babf3bf]
==>  Preparing: select id, name, pwd from user where id = ?
==> Parameters: 1(Integer)
<==    Columns: id, name, pwd
<==        Row: 1, 狂神, 123456
<==      Total: 1
User(id=1, name=狂神, pwd=123456)
User(id=1, name=狂神, pwd=123456)
true

可以看出,只進行了一次資料庫查詢,第二次查詢沒走資料庫,直接取的快取資料,所以兩個物件相等。

一級快取失效的四種情況

一級快取是SqlSession級別的快取,是一直開啟的,我們關閉不了它。

要想讓一級快取失效,只能是不使用當前的一級快取,效果就是,還需要再向資料庫中發起一次查詢請求。

sqlSession 不同

@Test
public void oneCache() {
    SqlSession sqlSession1 = MybatisUtils.getSession();
    SqlSession sqlSession2 = MybatisUtils.getSession();
    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
    User user1 = userMapper1.selectOne(1);
    System.out.println(user1);
    User user2 = userMapper2.selectOne(1);
    System.out.println(user2);
    System.out.println(user1 == user2);
    sqlSession1.close();
    sqlSession2.close();
}

列印結果

Opening JDBC Connection
Created connection 1806431167.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6babf3bf]
==>  Preparing: select id, name, pwd from user where id = ?
==> Parameters: 1(Integer)
<==    Columns: id, name, pwd
<==        Row: 1, 狂神, 123456
<==      Total: 1
User(id=1, name=狂神, pwd=123456)
Opening JDBC Connection
Created connection 1128948651.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@434a63ab]
==>  Preparing: select id, name, pwd from user where id = ?
==> Parameters: 1(Integer)
<==    Columns: id, name, pwd
<==        Row: 1, 狂神, 123456
<==      Total: 1
User(id=1, name=狂神, pwd=123456)
false

可以很明顯的看出,進行了兩次查詢,所以這裡沒有使用到快取。

由此可以得出結論:每個sqlSession中的快取相互獨立。

sqlSession 相同,查詢條件不同

@Test
public void oneCache() {
    SqlSession sqlSession = MybatisUtils.getSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User user1 = userMapper.selectOne(1);
    System.out.println(user1);
    User user2 = userMapper.selectOne(2);
    System.out.println(user2);
    System.out.println(user1 == user2);
    sqlSession.close();
}

列印結果

Opening JDBC Connection
Created connection 1806431167.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6babf3bf]
==>  Preparing: select id, name, pwd from user where id = ?
==> Parameters: 1(Integer)
<==    Columns: id, name, pwd
<==        Row: 1, 狂神, 123456
<==      Total: 1
User(id=1, name=狂神, pwd=123456)
==>  Preparing: select id, name, pwd from user where id = ?
==> Parameters: 2(Integer)
<==    Columns: id, name, pwd
<==        Row: 2, 張三, abcdef
<==      Total: 1
User(id=2, name=張三, pwd=abcdef)
false

發現進行了兩次查詢。

很容易理解,因為當前快取中,不存在這個資料。

sqlSession 相同,兩次查詢之間執行了增刪改操作

測試

@Test
public void oneCache() {
    SqlSession sqlSession = MybatisUtils.getSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User user1 = userMapper.selectOne(1);
    System.out.println(user1);

    userMapper.delete(14);

    User user2 = userMapper.selectOne(1);
    System.out.println(user2);
    System.out.println(user1 == user2);
    sqlSession.close();
}

列印結果:在中間執行了刪除操作後,第二次查詢也執行了。

結論:因為增刪改操作可能會對當前資料產生影響,為嚴謹考慮,會再執行一次查詢操作。

sqlSession 相同,手動清除一級快取

@Test
public void oneCache() {
    SqlSession sqlSession = MybatisUtils.getSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User user1 = userMapper.selectOne(1);
    System.out.println(user1);

    sqlSession.clearCache();

    User user2 = userMapper.selectOne(1);
    System.out.println(user2);
    System.out.println(user1 == user2);
    sqlSession.close();
}

列印結果:執行了兩次查詢,快取失效。

結論:一級快取就是一個 map,清除了之後就沒有了,自然會再進行一次查詢。

二級快取

  • 二級快取也叫全域性快取,一級快取作用域太低了,所以誕生了二級快取。

  • 基於 namespace 級別的快取,一個名稱空間,對應一個二級快取。

  • 工作機制:

    • 一個會話查詢一條資料,這個資料就會被放在當前會話的一級快取中。
    • 如果當前會話關閉了,這個會話對應的一級快取就沒了。
    • 我們想要的是,會話關閉了,一級快取中的資料被儲存到二級快取中。
    • 新的會話查詢資訊,就可以從二級快取中獲取內容。
    • 不同的 mapper 查出的資料會放在自己對應的快取( map )中。

使用步驟

  1. 開啟全域性快取 【mybatis-config.xml】
<setting name="cacheEnabled" value="true"/>
  1. 去每個 mapper.xml 中配置使用二級快取,這個配置非常簡單。
<cache/>

這裡也可以自定義配置,比如:

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

這個更高階的配置建立了一個 FIFO 快取,每隔 60 秒重新整理,最多可以儲存結果物件或列表的 512 個引用,而且返回的物件被認為是隻讀的,因此對它們進行修改可能會在不同執行緒中的呼叫者產生衝突。

eviction(清除策略)

可用的清除策略有:

  • LRU – 最近最少使用:移除最長時間不被使用的物件。
  • FIFO – 先進先出:按物件進入快取的順序來移除它們。
  • SOFT – 軟引用:基於垃圾回收器狀態和軟引用規則移除物件。
  • WEAK – 弱引用:更積極地基於垃圾收集器狀態和弱引用規則移除物件。

預設的清除策略是 LRU。

flushInterval(重新整理間隔)

  • 可以被設定為任意的正整數,設定的值應該是一個以毫秒為單位的合理時間量。

  • 預設情況是不設定,也就是沒有重新整理間隔,快取僅僅會在呼叫語句時重新整理。

size(引用數目)

  • 可以被設定為任意正整數,要注意欲快取物件的大小和執行環境中可用的記憶體資源。

  • 預設值是 1024。

readOnly(只讀)

  • 可以被設定為 true 或 false。

  • 只讀的快取會給所有呼叫者返回快取物件的相同例項,因此這些物件不能被修改。這就提供了可觀的效能提升。

  • 可讀寫的快取會(通過序列化)返回快取物件的拷貝。 速度上會慢一些,但是更安全,因此預設值是 false。

二級快取是事務性的。這意味著,當 SqlSession 完成並提交時,或是完成並回滾,但沒有執行 flushCache=true 的 insert/delete/update 語句時,快取會獲得更新。

  1. 程式碼測試
  • 所有的實體類先實現序列化介面
  • 測試程式碼
@Test
public void TwoCache(){
    SqlSession sqlSession1 = MybatisUtils.getSession();
    SqlSession sqlSession2 = MybatisUtils.getSession();

    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

    User user1 = userMapper1.selectOne(1);
    System.out.println(user1);
    sqlSession1.close();

    User user2 = userMapper2.selectOne(1);
    System.out.println(user2);
    System.out.println(user1 == user2);
    sqlSession2.close();
}

列印結果

Opening JDBC Connection
Created connection 1048855692.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3e84448c]
==>  Preparing: select id, name, pwd from user where id = ?
==> Parameters: 1(Integer)
<==    Columns: id, name, pwd
<==        Row: 1, 狂神, 123456
<==      Total: 1
User(id=1, name=狂神, pwd=123456)
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3e84448c]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3e84448c]
Returned connection 1048855692 to pool.
As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66
Cache Hit Ratio [cn.sail.mapper.UserMapper]: 0.5
User(id=1, name=狂神, pwd=123456)
false

由此可以看出,雖然前一個連線被關閉了,但由於開啟了二級快取,在查詢同一個 Mapper 的情況下,第二次建立的連線也使用了前一次連線快取中的資料,但此時物件不一樣了。

結論

  • 只要開啟了二級快取,我們在同一個 Mapper 中的查詢,可以在二級快取中拿到資料。
  • 查出的資料都會被預設先放在一級快取中。
  • 只有會話提交或者關閉以後,一級快取中的資料才會轉到二級快取中。

快取原理圖

EhCache

第三方快取實現:EhCache

Ehcache 是一種廣泛使用的 java 分散式快取,用於通用快取。

使用步驟

  1. 要引入依賴的jar包。
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
   <groupId>org.mybatis.caches</groupId>
   <artifactId>mybatis-ehcache</artifactId>
   <version>1.1.0</version>
</dependency>
  1. 在mapper.xml中使用對應的快取即可
<mapper namespace="org.acme.FooMapper">
   <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
</mapper>
  1. 編寫ehcache.xml檔案,如果在載入時未找到/ehcache.xml資源或出現問題,則將使用預設配置。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
        updateCheck="false">
   <!--
      diskStore:為快取路徑,ehcache分為記憶體和磁碟兩級,此屬性定義磁碟的快取位置。引數解釋如下:
      user.home – 使用者主目錄
      user.dir – 使用者當前工作目錄
      java.io.tmpdir – 預設臨時檔案路徑
    -->
   <diskStore path="./tmpdir/Tmp_EhCache"/>
   
   <defaultCache
           eternal="false"
           maxElementsInMemory="10000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="259200"
           memoryStoreEvictionPolicy="LRU"/>

   <cache
           name="cloud_user"
           eternal="false"
           maxElementsInMemory="5000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           memoryStoreEvictionPolicy="LRU"/>
   <!--
      defaultCache:預設快取策略,當ehcache找不到定義的快取時,則使用這個快取策略。只能定義一個。
    -->
   <!--
     name:快取名稱。
     maxElementsInMemory:快取最大數目
     maxElementsOnDisk:硬碟最大快取個數。
     eternal:物件是否永久有效,一但設定了,timeout將不起作用。
     overflowToDisk:是否儲存到磁碟,當系統當機時
     timeToIdleSeconds:設定物件在失效前的允許閒置時間(單位:秒)。僅當eternal=false物件不是永久有效時使用,可選屬性,預設值是0,也就是可閒置時間無窮大。
     timeToLiveSeconds:設定物件在失效前允許存活時間(單位:秒)。最大時間介於建立時間和失效時間之間。僅當eternal=false物件不是永久有效時使用,預設是0.,也就是物件存活時間無窮大。
     diskPersistent:是否快取虛擬機器重啟期資料 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
     diskSpoolBufferSizeMB:這個引數設定DiskStore(磁碟快取)的快取區大小。預設是30MB。每個Cache都應該有自己的一個緩衝區。
     diskExpiryThreadIntervalSeconds:磁碟失效執行緒執行時間間隔,預設是120秒。
     memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理記憶體。預設策略是LRU(最近最少使用)。你可以設定為FIFO(先進先出)或是LFU(較少使用)。
     clearOnFlush:記憶體數量最大時是否清除。
     memoryStoreEvictionPolicy:可選策略有:LRU(最近最少使用,預設策略)、FIFO(先進先出)、LFU(最少訪問次數)。
     FIFO,first in first out,這個是大家最熟的,先進先出。
     LFU, Less Frequently Used,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面所講,快取的元素有一個hit屬性,hit值最小的將會被清出快取。
     LRU,Least Recently Used,最近最少使用的,快取的元素有一個時間戳,當快取容量滿了,而又需要騰出地方來快取新的元素的時候,那麼現有快取元素中時間戳離當前時間最遠的元素將被清出快取。
  -->

</ehcache>