《深入理解Mybatis原理》Mybatis中的緩存實現原理

一級緩存實現

什么是一級緩存? 為什么使用一級緩存?

每當我們使用MyBatis開啟一次和數據庫的會話,MyBatis會創建出一個SqlSession對象表示一次數據庫會話。

在對數據庫的一次會話中,我們有可能會反復地執行完全相同的查詢語句,如果不采取一些措施的話,每一次查詢都會查詢一次數據庫,而我們在極短的時間內做了完全相同的查詢,那么它們的結果極有可能完全相同,由于查詢一次數據庫的代價很大,這有可能造成很大的資源浪費。

為了解決這一問題,減少資源的浪費,MyBatis會在表示會話的SqlSession對象中建立一個簡單的緩存,將每次查詢到的結果結果緩存起來,當下次查詢的時候,如果判斷先前有個完全一樣的查詢,會直接從緩存中直接將結果取出,返回給用戶,不需要再進行一次數據庫查詢了。

如下圖所示,MyBatis一次會話: 一個SqlSession對象中創建一個本地緩存(local cache),對于每一次查詢,都會嘗試根據查詢的條件去本地緩存中查找是否在緩存中,如果在緩存中,就直接從緩存中取出,然后返回給用戶;否則,從數據庫讀取數據,將查詢結果存入緩存并返回給用戶。

對于會話(Session)級別的數據緩存,我們稱之為一級數據緩存,簡稱一級緩存。

MyBatis中的一級緩存是怎樣組織的?

即SqlSession中的緩存是怎樣組織的?由于MyBatis使用SqlSession對象表示一次數據庫的會話,那么,對于會話級別的一級緩存也應該是在SqlSession中控制的。

實際上, MyBatis只是一個MyBatis對外的接口,SqlSession將它的工作交給了Executor執行器這個角色來完成,負責完成對數據庫的各種操作。當創建了一個SqlSession對象時,MyBatis會為這個SqlSession對象創建一個新的Executor執行器,而緩存信息就被維護在這個Executor執行器中,MyBatis將緩存和對緩存相關的操作封裝成了Cache接口中。SqlSession、Executor、Cache之間的關系如下列類圖所示:

如上述的類圖所示,Executor接口的實現類BaseExecutor中擁有一個Cache接口的實現類PerpetualCache,則對于BaseExecutor對象而言,它將使用PerpetualCache對象維護緩存。

綜上,SqlSession對象、Executor對象、Cache對象之間的關系如下圖所示:

由于Session級別的一級緩存實際上就是使用PerpetualCache維護的,那么PerpetualCache是怎樣實現的呢?

PerpetualCache實現原理其實很簡單,其內部就是通過一個簡單的HashMap<k,v>?來實現的,沒有其他的任何限制。如下是PerpetualCache的實現代碼:

package org.apache.ibatis.cache.impl;  import java.util.HashMap;  
import java.util.Map;  
import java.util.concurrent.locks.ReadWriteLock;  import org.apache.ibatis.cache.Cache;  
import org.apache.ibatis.cache.CacheException;  /** * 使用簡單的HashMap來維護緩存 * @author Clinton Begin */  
public class PerpetualCache implements Cache {  private String id;  private Map<Object, Object> cache = new HashMap<Object, Object>();  public PerpetualCache(String id) {  this.id = id;  }  public String getId() {  return id;  }  public int getSize() {  return cache.size();  }  public void putObject(Object key, Object value) {  cache.put(key, value);  }  public Object getObject(Object key) {  return cache.get(key);  }  public Object removeObject(Object key) {  return cache.remove(key);  }  public void clear() {  cache.clear();  }  public ReadWriteLock getReadWriteLock() {  return null;  }  public boolean equals(Object o) {  if (getId() == null) throw new CacheException("Cache instances require an ID.");  if (this == o) return true;  if (!(o instanceof Cache)) return false;  Cache otherCache = (Cache) o;  return getId().equals(otherCache.getId());  }  public int hashCode() {  if (getId() == null) throw new CacheException("Cache instances require an ID.");  return getId().hashCode();  }  } 

一級緩存的生命周期有多長?

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中;將查詢結果返回;

  • 結束。

Cache接口的設計以及CacheKey的定義

如下圖所示,MyBatis定義了一個org.apache.ibatis.cache.Cache接口作為其Cache提供者的SPI(Service Provider Interface) ,所有的MyBatis內部的Cache緩存,都應該實現這一接口。MyBatis定義了一個PerpetualCache實現類實現了Cache接口,實際上,在SqlSession對象里的Executor對象內維護的Cache類型實例對象,就是PerpetualCache子類創建的。

(MyBatis內部還有很多Cache接口的實現,一級緩存只會涉及到這一個PerpetualCache子類,Cache的其他實現將會放到二級緩存中介紹)。

我們知道,Cache最核心的實現其實就是一個Map,將本次查詢使用的特征值作為key,將查詢結果作為value存儲到Map中。現在最核心的問題出現了:怎樣來確定一次查詢的特征值?換句話說就是:怎樣判斷某兩次查詢是完全相同的查詢?也可以這樣說:如何確定Cache中的key值?

MyBatis認為,對于兩次查詢,如果以下條件都完全一樣,那么就認為它們是完全相同的兩次查詢:

  • 傳入的 statementId

  • 查詢時要求的結果集中的結果范圍 (結果的范圍通過rowBounds.offset和rowBounds.limit表示)

  • 這次查詢所產生的最終要傳遞給JDBC java.sql.Preparedstatement的Sql語句字符串(boundSql.getSql() )

  • 傳遞給java.sql.Statement要設置的參數值

現在分別解釋上述四個條件

  • 傳入的statementId,對于MyBatis而言,你要使用它,必須需要一個statementId,它代表著你將執行什么樣的Sql;

  • MyBatis自身提供的分頁功能是通過RowBounds來實現的,它通過rowBounds.offset和rowBounds.limit來過濾查詢出來的結果集,這種分頁功能是基于查詢結果的再過濾,而不是進行數據庫的物理分頁;

  • 由于MyBatis底層還是依賴于JDBC實現的,那么,對于兩次完全一模一樣的查詢,MyBatis要保證對于底層JDBC而言,也是完全一致的查詢才行。而對于JDBC而言,兩次查詢,只要傳入給JDBC的SQL語句完全一致,傳入的參數也完全一致,就認為是兩次查詢是完全一致的。

  • 上述的第3個條件正是要求保證傳遞給JDBC的SQL語句完全一致;第4條則是保證傳遞給JDBC的參數也完全一致;即3、4兩條MyBatis最本質的要求就是:調用JDBC的時候,傳入的SQL語句要完全相同,傳遞給JDBC的參數值也要完全相同。

綜上所述,CacheKey由以下條件決定:statementId + rowBounds + 傳遞給JDBC的SQL + 傳遞給JDBC的參數值

  • CacheKey的創建

對于每次的查詢請求,Executor都會根據傳遞的參數信息以及動態生成的SQL語句,將上面的條件根據一定的計算規則,創建一個對應的CacheKey對象。

我們知道創建CacheKey的目的,就兩個:

  • 根據CacheKey作為key,去Cache緩存中查找緩存結果;

  • 如果查找緩存命中失敗,則通過此CacheKey作為key,將從數據庫查詢到的結果作為value,組成key,value對存儲到Cache緩存中;

CacheKey的構建被放置到了Executor接口的實現類BaseExecutor中,定義如下:

/** * 所屬類:  org.apache.ibatis.executor.BaseExecutor * 功能   :   根據傳入信息構建CacheKey */  
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {  if (closed) throw new ExecutorException("Executor was closed.");  CacheKey cacheKey = new CacheKey();  //1.statementId  cacheKey.update(ms.getId());  //2. rowBounds.offset  cacheKey.update(rowBounds.getOffset());  //3. rowBounds.limit  cacheKey.update(rowBounds.getLimit());  //4. SQL語句  cacheKey.update(boundSql.getSql());  //5. 將每一個要傳遞給JDBC的參數值也更新到CacheKey中  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();  TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();  for (int i = 0; i < parameterMappings.size(); i++) { // mimic DefaultParameterHandler logic  ParameterMapping parameterMapping = parameterMappings.get(i);  if (parameterMapping.getMode() != ParameterMode.OUT) {  Object value;  String propertyName = parameterMapping.getProperty();  if (boundSql.hasAdditionalParameter(propertyName)) {  value = boundSql.getAdditionalParameter(propertyName);  } else if (parameterObject == null) {  value = null;  } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {  value = parameterObject;  } else {  MetaObject metaObject = configuration.newMetaObject(parameterObject);  value = metaObject.getValue(propertyName);  }  //將每一個要傳遞給JDBC的參數值也更新到CacheKey中  cacheKey.update(value);  }  }  return cacheKey;  
}

  • CacheKey的hashcode生成算法

剛才已經提到,Cache接口的實現,本質上是使用的HashMap<k,v>,而構建CacheKey的目的就是為了作為HashMap<k,v>中的key值。而HashMap是通過key值的hashcode 來組織和存儲的,那么,構建CacheKey的過程實際上就是構造其hashCode的過程。下面的代碼就是CacheKey的核心hashcode生成算法,感興趣的話可以看一下:

public void update(Object object) {  if (object != null && object.getClass().isArray()) {  int length = Array.getLength(object);  for (int i = 0; i < length; i++) {  Object element = Array.get(object, i);  doUpdate(element);  }  } else {  doUpdate(object);  }  
}  private void doUpdate(Object object) {  //1. 得到對象的hashcode;    int baseHashCode = object == null ? 1 : object.hashCode();  //對象計數遞增  count++;  checksum += baseHashCode;  //2. 對象的hashcode 擴大count倍  baseHashCode *= count;  //3. hashCode * 拓展因子(默認37)+拓展擴大后的對象hashCode值  hashcode = multiplier * hashcode + baseHashCode;  updateList.add(object);  
} 

MyBatis認為的完全相同的查詢,不是指使用sqlSession查詢時傳遞給算起來Session的所有參數值完完全全相同,你只要保證statementId,rowBounds,最后生成的SQL語句,以及這個SQL語句所需要的參數完全一致就可以了。

一級緩存的性能分析

  • MyBatis對會話(Session)級別的一級緩存設計的比較簡單,就簡單地使用了HashMap來維護,并沒有對HashMap的容量和大小進行限制

讀者有可能就覺得不妥了:如果我一直使用某一個SqlSession對象查詢數據,這樣會不會導致HashMap太大,而導致 java.lang.OutOfMemoryError錯誤啊? 讀者這么考慮也不無道理,不過MyBatis的確是這樣設計的。

MyBatis這樣設計也有它自己的理由:

  • 一般而言SqlSession的生存時間很短。一般情況下使用一個SqlSession對象執行的操作不會太多,執行完就會消亡;

  • 對于某一個SqlSession對象而言,只要執行update操作(update、insert、delete),都會將這個SqlSession對象中對應的一級緩存清空掉,所以一般情況下不會出現緩存過大,影響JVM內存空間的問題;

  • 可以手動地釋放掉SqlSession對象中的緩存。

  • 一級緩存是一個粗粒度的緩存,沒有更新緩存和緩存過期的概念

MyBatis的一級緩存就是使用了簡單的HashMap,MyBatis只負責將查詢數據庫的結果存儲到緩存中去, 不會去判斷緩存存放的時間是否過長、是否過期,因此也就沒有對緩存的結果進行更新這一說了。

根據一級緩存的特性,在使用的過程中,我認為應該注意:

  • 對于數據變化頻率很大,并且需要高時效準確性的數據要求,我們使用SqlSession查詢的時候,要控制好SqlSession的生存時間, SqlSession的生存時間越長,它其中緩存的數據有可能就越舊,從而造成和真實數據庫的誤差;同時對于這種情況,用戶也可以手動地適時清空SqlSession中的緩存;

  • 對于只執行、并且頻繁執行大范圍的select操作的SqlSession對象,SqlSession對象的生存時間不應過長。

二級緩存實現

MyBatis的二級緩存是Application級別的緩存,它可以提高對數據庫查詢的效率,以提高應用的性能。

MyBatis的緩存機制整體設計以及二級緩存的工作模式

如圖所示,當開一個會話時,一個SqlSession對象會使用一個Executor對象來完成會話操作,MyBatis的二級緩存機制的關鍵就是對這個Executor對象做文章。如果用戶配置了"cacheEnabled=true",那么MyBatis在為SqlSession對象創建Executor對象時,會對Executor對象加上一個裝飾者:CachingExecutor,這時SqlSession使用CachingExecutor對象來完成操作請求。CachingExecutor對于查詢請求,會先判斷該查詢請求在Application級別的二級緩存中是否有緩存結果,如果有查詢結果,則直接返回緩存結果;如果緩存中沒有,再交給真正的Executor對象來完成查詢操作,之后CachingExecutor會將真正Executor返回的查詢結果放置到緩存中,然后在返回給用戶。

CachingExecutor是Executor的裝飾者,以增強Executor的功能,使其具有緩存查詢的功能,這里用到了設計模式中的裝飾者模式,CachingExecutor和Executor的接口的關系如下類圖所示:

MyBatis二級緩存的劃分

MyBatis并不是簡單地對整個Application就只有一個Cache緩存對象,它將緩存劃分的更細,即是Mapper級別的,即每一個Mapper都可以擁有一個Cache對象,具體如下:

  • 為每一個Mapper分配一個Cache緩存對象(使用<cache>節點配置)

MyBatis將Application級別的二級緩存細分到Mapper級別,即對于每一個Mapper.xml,如果在其中使用了<cache>?節點,則MyBatis會為這個Mapper創建一個Cache緩存對象,如下圖所示:

注:上述的每一個Cache對象,都會有一個自己所屬的namespace命名空間,并且會將Mapper的 namespace作為它們的ID;

  • 多個Mapper共用一個Cache緩存對象(使用<cache-ref>節點配置)

如果你想讓多個Mapper公用一個Cache的話,你可以使用<cache-ref namespace="">節點,來指定你的這個Mapper使用到了哪一個Mapper的Cache緩存。

使用二級緩存,必須要具備的條件

MyBatis對二級緩存的支持粒度很細,它會指定某一條查詢語句是否使用二級緩存。

雖然在Mapper中配置了<cache>,并且為此Mapper分配了Cache對象,這并不表示我們使用Mapper中定義的查詢語句查到的結果都會放置到Cache對象之中,我們必須指定Mapper中的某條選擇語句是否支持緩存,即如下所示,在<select>?節點中配置useCache="true",Mapper才會對此Select的查詢支持緩存特性,否則,不會對此Select查詢,不會經過Cache緩存。如下所示,Select語句配置了useCache="true",則表明這條Select語句的查詢會使用二級緩存。

<select id="selectByMinSalary" resultMap="BaseResultMap" parameterType="java.util.Map" useCache="true">

總之,要想使某條Select查詢支持二級緩存,你需要保證:

  • MyBatis支持二級緩存的總開關:全局配置變量參數 cacheEnabled=true

  • 該select語句所在的Mapper,配置了<cache>?或<cached-ref>節點,并且有效

  • 該select語句的參數 useCache=true

一級緩存和二級緩存的使用順序

請注意,如果你的MyBatis使用了二級緩存,并且你的Mapper和select語句也配置使用了二級緩存,那么在執行select查詢的時候,MyBatis會先從二級緩存中取輸入,其次才是一級緩存,即MyBatis查詢數據的順序是:二級緩存 ———> 一級緩存 ——> 數據庫

二級緩存實現的選擇

MyBatis對二級緩存的設計非常靈活,它自己內部實現了一系列的Cache緩存實現類,并提供了各種緩存刷新策略如LRU,FIFO等等;另外,MyBatis還允許用戶自定義Cache接口實現,用戶是需要實現org.apache.ibatis.cache.Cache接口,然后將Cache實現類配置在<cache type="">節點的type屬性上即可;除此之外,MyBatis還支持跟第三方內存緩存庫如Memecached的集成,總之,使用MyBatis的二級緩存有三個選擇:

  • MyBatis自身提供的緩存實現;

  • 用戶自定義的Cache接口實現;

  • 跟第三方內存緩存庫的集成;

MyBatis自身提供的二級緩存的實現

MyBatis自身提供了豐富的,并且功能強大的二級緩存的實現,它擁有一系列的Cache接口裝飾者,可以滿足各種對緩存操作和更新的策略。

MyBatis定義了大量的Cache的裝飾器來增強Cache緩存的功能,如下類圖所示。

對于每個Cache而言,都有一個容量限制,MyBatis各供了各種策略來對Cache緩存的容量進行控制,以及對Cache中的數據進行刷新和置換。MyBatis主要提供了以下幾個刷新和置換策略:

  • LRU:(Least Recently Used),最近最少使用算法,即如果緩存中容量已經滿了,會將緩存中最近最少被使用的緩存記錄清除掉,然后添加新的記錄;

  • FIFO:(First in first out),先進先出算法,如果緩存中的容量已經滿了,那么會將最先進入緩存中的數據清除掉;

  • Scheduled:指定時間間隔清空算法,該算法會以指定的某一個時間間隔將Cache緩存中的數據清空;

如何細粒度地控制二級緩存

關于MyBatis的二級緩存的實際問題

現有AMapper.xml中定義了對數據庫表 ATable 的CRUD操作,BMapper定義了對數據庫表BTable的CRUD操作;

假設 MyBatis 的二級緩存開啟,并且 AMapper 中使用了二級緩存,AMapper對應的二級緩存為ACache;

除此之外,AMapper 中還定義了一個跟BTable有關的查詢語句,類似如下所述:

<select id="selectATableWithJoin" resultMap="BaseResultMap" useCache="true">  select * from ATable left join BTable on ....  
</select>

執行以下操作:

  • 執行AMapper中的"selectATableWithJoin" 操作,此時會將查詢到的結果放置到AMapper對應的二級緩存ACache中;

  • 執行BMapper中對BTable的更新操作(update、delete、insert)后,BTable的數據更新;

  • 再執行1完全相同的查詢,這時候會直接從AMapper二級緩存ACache中取值,將ACache中的值直接返回;

好,問題就出現在第3步上:

由于AMapper的“selectATableWithJoin” 對應的SQL語句需要和BTable進行join查找,而在第 2 步BTable的數據已經更新了,但是第 3 步查詢的值是第 1 步的緩存值,已經極有可能跟真實數據庫結果不一樣,即ACache中緩存數據過期了!

總結來看,就是:

對于某些使用了 join連接的查詢,如果其關聯的表數據發生了更新,join連接的查詢由于先前緩存的原因,導致查詢結果和真實數據不同步;

從MyBatis的角度來看,這個問題可以這樣表述:

對于某些表執行了更新(update、delete、insert)操作后,如何去清空跟這些表有關聯的查詢語句所造成的緩存

當前MyBatis二級緩存的工作機制

MyBatis二級緩存的一個重要特點:即松散的Cache緩存管理和維護

一個Mapper中定義的增刪改查操作只能影響到自己關聯的Cache對象。如上圖所示的Mapper namespace1中定義的若干CRUD語句,產生的緩存只會被放置到相應關聯的Cache1中,即Mapper namespace2,namespace3,namespace4 中的CRUD的語句不會影響到Cache1。

可以看出,Mapper之間的緩存關系比較松散,相互關聯的程度比較弱

現在再回到上面描述的問題,如果我們將AMapper和BMapper共用一個Cache對象,那么,當BMapper執行更新操作時,可以清空對應Cache中的所有的緩存數據,這樣的話,數據不是也可以保持最新嗎?

確實這個也是一種解決方案,不過,它會使緩存的使用效率變的很低!AMapper和BMapper的任意的更新操作都會將共用的Cache清空,會頻繁地清空Cache,導致Cache實際的命中率和使用率就變得很低了,所以這種策略實際情況下是不可取的。

最理想的解決方案就是:

對于某些表執行了更新(update、delete、insert)操作后,去清空跟這些指定的表有關聯的查詢語句所造成的緩存; 這樣,就是以很細的粒度管理MyBatis內部的緩存,使得緩存的使用率和準確率都能大大地提升。

文章轉載自:seven97_top

原文鏈接:《深入理解Mybatis原理》Mybatis中的緩存實現原理 - seven97_top - 博客園

體驗地址:引邁 - JNPF快速開發平臺_低代碼開發平臺_零代碼開發平臺_流程設計器_表單引擎_工作流引擎_軟件架構

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/66052.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/66052.shtml
英文地址,請注明出處:http://en.pswp.cn/web/66052.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

win32匯編環境,窗口程序中單選框的一般操作示例

;運行效果 ;win32匯編環境,窗口程序中單選框的一般操作示例 ;比如在窗口程序中生成單選框&#xff0c;默認哪項選中&#xff0c;判斷當前選中哪一項&#xff0c;讓哪項選中&#xff0c;得到選中項的名稱等 ;直接抄進RadAsm可編譯運行。重點部分加備注。 ;以下是ASM文件 ;>&g…

從transformer到informer

Transformer和Informer都是深度學習領域中的模型架構&#xff0c;它們主要用于處理序列數據&#xff0c;如自然語言處理&#xff08;NLP&#xff09;和時間序列預測任務。 **Transformer**&#xff1a; Transformer模型最初在2017年由Google的研究者提出&#xff0c;它在NLP領…

hive知識體系

hive知識體系 hive知識體系 鏈接: 1Hive概覽 鏈接: 2Hive表類型 鏈接: 3Hive數據抽樣 鏈接: 4Hive計算引擎 鏈接: 5Hive存儲與壓縮 鏈接: 6Hive Sql 大全 鏈接: 6Hive Sql 大全-Hive 函數 鏈接: 6Hive Sql 大全-窗口函數 鏈接: 7Hive執行計劃 鏈接: 8Hive SQL底層執行原理 鏈接…

優化 Azure Synapse Dedicated SQL Pool中的 SQL 執行性能的經驗方法

在 Azure Synapse Dedicated SQL Pool中優化 SQL 執行涉及了解底層體系結構&#xff08;例如分布和分區&#xff09;、查詢優化&#xff08;例如避免不必要的子查詢和聯接&#xff09;&#xff0c;以及利用具體化視圖和 PolyBase 等工具進行高效數據加載。 1.有效使用分布和分…

個人主頁搭建全流程(Nginx部署+SSL配置+DCDN加速)

前言 最近開始準備秋招&#xff0c;打算做一個個人主頁&#xff0c;以便在秋招市場上更有競爭力。 目前&#xff0c;現有的一些搭建主頁的博文教程存在以下一些問題&#xff1a; 使用Github Page進行部署&#xff0c;這在國內訪問容易受阻使用寶塔面板等框架&#xff0c;功能…

Spring MVC簡單數據綁定

【圖書介紹】《SpringSpring MVCMyBatis從零開始學&#xff08;視頻教學版&#xff09;&#xff08;第3版&#xff09;》_springspringmvcmybatis從零開始 代碼、課件、教學視頻與相關軟件包下載-CSDN博客 《SpringSpring MVCMyBatis從零開始學(視頻教學版)&#xff08;第3版&…

嵌入式系統Linux實時化(四)Xenomai應用開發測試

1、Xenomai 原生API 任務管理 Xenomai 本身提供的一系列多任務調度機制,主要有以下一些函數: int rt_task_create (RT_TASK task, const char name, int stksize, int prio, intmode) ; 任務的創建;int rt_task_start(RT_TASK task, void(entry)(void cookie), void cookie…

如何在Ubuntu上安裝和配置Git

版本控制系統&#xff08;VCS&#xff09;是軟件開發過程中不可或缺的工具之一&#xff0c;它幫助開發者跟蹤代碼變更、協作開發以及管理不同版本的項目。Git作為當前最流行的分布式版本控制系統&#xff0c;因其高效性和靈活性而廣受青睞。本文將指導你如何在Ubuntu操作系統上…

[python] bisect_right

等價于C中的upper_bound bisect_right 函數介紹 在Python的 bisect 模塊中&#xff0c; bisect_right &#xff08;別名 bisect &#xff09;用于在有序序列中查找插入點。插入點是在序列中插入元素后&#xff0c;序列仍保持有序的位置。 bisect_right 函數返回的插入點是在已…

Mac上安裝Label Studio

在Mac上安裝Anaconda并隨后安裝Label Studio&#xff0c;可以按照以下步驟進行&#xff1a; 1. 在Mac上安裝Anaconda 首先&#xff0c;你需要從Anaconda的官方網站下載適用于Mac的安裝程序。訪問Anaconda官網&#xff0c;點擊“Download Anaconda”按鈕&#xff0c;選擇適合M…

vscode開啟調試模式,結合Delve調試器調試golang項目詳細步驟

1.前期準備 (1).在vs code中的擴展程序中搜索并安裝Go擴展程序 (2).安裝 Delve 調試器 go install github.com/go-delve/delve/cmd/dlvlatest (3).打開vs code的命令面板&#xff0c;輸入Go: Install/Update Tools&#xff0c;并單擊該命令執行&#xff0c;安裝或更新Go語…

SQL面試題1:連續登陸問題

引言 場景介紹&#xff1a; 許多互聯網平臺為了提高用戶的參與度和忠誠度&#xff0c;會推出各種連續登錄獎勵機制。例如&#xff0c;游戲平臺會給連續登錄的玩家發放游戲道具、金幣等獎勵&#xff1b;學習類 APP 會為連續登錄學習的用戶提供積分&#xff0c;積分可兌換課程或…

爬山算法與模擬退火算法的全方面比較

一、基本概念與原理 1. 爬山算法 爬山算法是一種基于啟發式的局部搜索算法,通過不斷地向當前解的鄰域中搜索更優解來逼近全局最優解。它的核心思想是,從當前解出發,在鄰域內找到一個使目標函數值更大(或更小)的解作為新的當前解,直到找不到更優的解為止。 2.模擬退火算…

PostgreSQL 超級管理員詳解

1. 什么是 PostgreSQL 超級管理員 PostgreSQL 超級管理員&#xff08;superuser&#xff09;是擁有數據庫系統最高權限的用戶。他們可以執行任何數據庫操作&#xff0c;包括但不限于創建和刪除數據庫、用戶、表空間、模式等。超級管理員權限是 PostgreSQL 中權限的最高級別。 …

安裝本地測試安裝apache-doris

一、安裝前規劃 我的服務器是三臺麒麟服務器,2臺跑不起來,這是我本地的,內存分配的也不多。 fe192.168.1.13 主數據庫端口9030訪問 8Gbe192.168.1.13內存4G 硬盤50be192.168.1.14內存4G 硬盤50be192.168.1.12內存4G 硬盤5013同時安裝的fe和be 。 原理:192.168.1.13 服…

GPT(General Purpose Timer)定時器

基本概念&#xff1a; 在嵌入式系統中&#xff0c;General Purpose Timer&#xff08;GPT&#xff09;是一種非常重要的硬件組件&#xff0c;用于提供定時功能。 定義&#xff1a;通用定時器是一種能夠提供精確時間測量和控制功能的電子設備或電路模塊。它可以產生周期性的時…

集中式架構vs分布式架構

一、集中式架構 如何準確理解集中式架構 1. 集中式架構的定義 集中式架構是一種將系統的所有計算、存儲、數據處理和控制邏輯集中在一個或少數幾個節點上運行的架構模式。這些中央節點&#xff08;服務器或主機&#xff09;作為系統的核心&#xff0c;負責處理所有用戶請求和…

數據挖掘實訓:天氣數據分析與機器學習模型構建

隨著氣候變化對各行各業的影響日益加劇&#xff0c;精準的天氣預測已經變得尤為重要。降雨預測在日常生活中尤其關鍵&#xff0c;例如農業、交通和災害預警等領域。本文將通過機器學習方法&#xff0c;利用歷史天氣數據預測明天是否會下雨&#xff0c;具體內容包括數據預處理、…

kalilinux - 目錄掃描之dirsearch

情景導入 先簡單介紹一下dirsearch有啥用。 假如你現在訪問一個網站&#xff0c;例如https://www.example.com/ 它是一個電商平臺或者其他功能性質的平臺。 站在開發者的角度上思考&#xff0c;我們只指導https://www.example.com/ 但不知道它下面有什么文件&#xff0c;文…

關于 ThinkPHP 與 PostgreSQL 結合使用的一些要點

ThinkPHP 是一款流行的 PHP 開發框架&#xff0c;而 PostgreSQL 是功能強大的開源關系型數據庫。它們可以結合使用來開發各類應用&#xff0c;以下是關于 ThinkPHP 與 PostgreSQL 結合使用的一些要點&#xff1a; 配置數據庫連接 編輯配置文件&#xff1a;在 ThinkPHP 項目中&…