99偷拍视频精品区一区二,口述久久久久久久久久久久,国产精品夫妇激情啪发布,成人永久免费网站在线观看,国产精品高清免费在线,青青草在线观看视频观看,久久久久久国产一区,天天婷婷久久18禁,日韩动漫av在线播放直播

SpringBoot2.0中HikariCP數(shù)據(jù)庫(kù)連接池的原理是什么-創(chuàng)新互聯(lián)

SpringBoot2.0 中 HikariCP 數(shù)據(jù)庫(kù)連接池的原理是什么?相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。

創(chuàng)新互聯(lián)建站專注于企業(yè)全網(wǎng)整合營(yíng)銷推廣、網(wǎng)站重做改版、沐川網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5技術(shù)成都商城網(wǎng)站開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為沐川等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。

一、什么是數(shù)據(jù)庫(kù)連接池


在講解HiKariCP之前,我們先簡(jiǎn)單介紹下什么是數(shù)據(jù)庫(kù)連接池(Database Connection Pooling),以及為什么要有數(shù)據(jù)庫(kù)連接池。

從根本上而言,數(shù)據(jù)庫(kù)連接池和我們常用的線程池一樣,都屬于池化資源,它在程序初始化時(shí)創(chuàng)建一定數(shù)量的數(shù)據(jù)庫(kù)連接對(duì)象并將其保存在一塊內(nèi)存區(qū)中。它允許應(yīng)用程序重復(fù)使用一個(gè)現(xiàn)有的數(shù)據(jù)庫(kù)連接,當(dāng)需要執(zhí)行 SQL 時(shí),我們是直接從連接池中獲取一個(gè)連接,而不是重新建立一個(gè)數(shù)據(jù)庫(kù)連接,當(dāng) SQL 執(zhí)行完,也并不是將數(shù)據(jù)庫(kù)連接真的關(guān)掉,而是將其歸還到數(shù)據(jù)庫(kù)連接池中。我們可以通過(guò)配置連接池的參數(shù)來(lái)控制連接池中的初始連接數(shù)、最小連接、較大連接、較大空閑時(shí)間等參數(shù),來(lái)保證訪問(wèn)數(shù)據(jù)庫(kù)的數(shù)量在一定可控制的范圍類,防止系統(tǒng)崩潰,同時(shí)保證用戶良好的體驗(yàn)。數(shù)據(jù)庫(kù)連接池示意圖如下所示:

SpringBoot2.0 中 HikariCP 數(shù)據(jù)庫(kù)連接池的原理是什么

因此使用數(shù)據(jù)庫(kù)連接池的核心作用,就是避免數(shù)據(jù)庫(kù)連接頻繁創(chuàng)建和銷毀,節(jié)省系統(tǒng)開銷。因?yàn)閿?shù)據(jù)庫(kù)連接是有限且代價(jià)昂貴,創(chuàng)建和釋放數(shù)據(jù)庫(kù)連接都非常耗時(shí),頻繁地進(jìn)行這樣的操作將占用大量的性能開銷,進(jìn)而導(dǎo)致網(wǎng)站的響應(yīng)速度下降,甚至引起服務(wù)器崩潰。

二、常見(jiàn)數(shù)據(jù)庫(kù)連接池對(duì)比分析

這里詳細(xì)總結(jié)了常見(jiàn)數(shù)據(jù)庫(kù)連接池的各項(xiàng)功能比較,我們重點(diǎn)分析下當(dāng)前主流的阿里巴巴Druid與HikariCP,HikariCP在性能上是完全優(yōu)于Druid連接池的。而Druid的性能稍微差點(diǎn)是由于鎖機(jī)制的不同,并且Druid提供更豐富的功能,包括監(jiān)控、sql攔截與解析等功能,兩者的側(cè)重點(diǎn)不一樣,HikariCP追求極致的高性能。

SpringBoot2.0 中 HikariCP 數(shù)據(jù)庫(kù)連接池的原理是什么

下面是官網(wǎng)提供的性能對(duì)比圖,在性能上面這五種數(shù)據(jù)庫(kù)連接池的排序如下:HikariCP>druid>tomcat-jdbc>dbcp>c3p0:

SpringBoot2.0 中 HikariCP 數(shù)據(jù)庫(kù)連接池的原理是什么

三、HikariCP 數(shù)據(jù)庫(kù)連接池簡(jiǎn)介

HikariCP 號(hào)稱是史上性能好的數(shù)據(jù)庫(kù)連接池,SpringBoot 2.0將它設(shè)置為默認(rèn)的數(shù)據(jù)源連接池。Hikari相比起其它連接池的性能高了非常多,那么,這是怎么做到的呢?通過(guò)查看HikariCP官網(wǎng)介紹,對(duì)于HikariCP所做優(yōu)化總結(jié)如下:

1. 字節(jié)碼精簡(jiǎn) :優(yōu)化代碼,編譯后的字節(jié)碼量極少,使得CPU緩存可以加載更多的程序代碼;

HikariCP在優(yōu)化并精簡(jiǎn)字節(jié)碼上也下了功夫,使用第三方的Java字節(jié)碼修改類庫(kù)Javassist來(lái)生成委托實(shí)現(xiàn)動(dòng)態(tài)代理.動(dòng)態(tài)代理的實(shí)現(xiàn)在ProxyFactory類,速度更快,相比于JDK Proxy生成的字節(jié)碼更少,精簡(jiǎn)了很多不必要的字節(jié)碼。

2. 優(yōu)化代理和攔截器:減少代碼,例如HikariCP的Statement proxy只有100行代碼,只有BoneCP的十分之一;

3. 自定義數(shù)組類型(FastStatementList)代替ArrayList:避免ArrayList每次get()都要進(jìn)行range check,避免調(diào)用remove()時(shí)的從頭到尾的掃描(由于連接的特點(diǎn)是后獲取連接的先釋放);

4. 自定義集合類型(ConcurrentBag):提高并發(fā)讀寫的效率;

5. 其他針對(duì)BoneCP缺陷的優(yōu)化,比如對(duì)于耗時(shí)超過(guò)一個(gè)CPU時(shí)間片的方法調(diào)用的研究。

當(dāng)然作為一個(gè)數(shù)據(jù)庫(kù)連接池,不能說(shuō)快就會(huì)被消費(fèi)者所推崇,它還具有非常好的健壯性及穩(wěn)定性。HikariCP從15年推出以來(lái),已經(jīng)經(jīng)受了廣大應(yīng)用市場(chǎng)的考驗(yàn),并且成功地被SpringBoot2.0作為默認(rèn)數(shù)據(jù)庫(kù)連接池進(jìn)行推廣,在可靠性上面是值得信任的。其次借助于其代碼量少,占用cpu和內(nèi)存量小的優(yōu)點(diǎn),使得它的執(zhí)行率非常高。最后,Spring配置HikariCP和druid基本沒(méi)什么區(qū)別,遷移過(guò)來(lái)非常方便,這些都是為什么HikariCP目前如此受歡迎的原因。

字節(jié)碼精簡(jiǎn)、優(yōu)化代理和攔截器、自定義數(shù)組類型。

四、HikariCP 核心源碼解析

4.1 FastList 是如何優(yōu)化性能問(wèn)題的

首先我們來(lái)看一下執(zhí)行數(shù)據(jù)庫(kù)操作規(guī)范化的操作步驟:

  • 通過(guò)數(shù)據(jù)源獲取一個(gè)數(shù)據(jù)庫(kù)連接;

  • 創(chuàng)建 Statement;

  • 執(zhí)行 SQL;

  • 通過(guò) ResultSet 獲取 SQL 執(zhí)行結(jié)果;

  • 釋放 ResultSet;

  • 釋放 Statement;

  • 釋放數(shù)據(jù)庫(kù)連接。

當(dāng)前所有數(shù)據(jù)庫(kù)連接池都是嚴(yán)格地根據(jù)這個(gè)順序來(lái)進(jìn)行數(shù)據(jù)庫(kù)操作的,為了防止最后的釋放操作,各類數(shù)據(jù)庫(kù)連接池都會(huì)把創(chuàng)建的 Statement 保存在數(shù)組 ArrayList 里,來(lái)保證當(dāng)關(guān)閉連接的時(shí)候,可以依次將數(shù)組中的所有 Statement 關(guān)閉。HiKariCP 在處理這一步驟中,認(rèn)為 ArrayList 的某些方法操作存在優(yōu)化空間,因此對(duì)List接口的精簡(jiǎn)實(shí)現(xiàn),針對(duì)List接口中核心的幾個(gè)方法進(jìn)行優(yōu)化,其他部分與ArrayList基本一致 。

首先是get()方法,ArrayList每次調(diào)用get()方法時(shí)都會(huì)進(jìn)行rangeCheck檢查索引是否越界,F(xiàn)astList的實(shí)現(xiàn)中去除了這一檢查,是因?yàn)閿?shù)據(jù)庫(kù)連接池滿足索引的合法性,能保證不會(huì)越界,此時(shí)rangeCheck就屬于無(wú)效的計(jì)算開銷,所以不用每次都進(jìn)行越界檢查。省去頻繁的無(wú)效操作,可以明顯地減少性能消耗。

FastList get()操作

public T get(int index)
{
  // ArrayList 在此多了范圍檢測(cè) rangeCheck(index);
  return elementData[index];
}

其次是remove方法,當(dāng)通過(guò) conn.createStatement() 創(chuàng)建一個(gè) Statement 時(shí),需要調(diào)用 ArrayList 的 add() 方法加入到 ArrayList 中,這個(gè)是沒(méi)有問(wèn)題的;但是當(dāng)通過(guò) stmt.close() 關(guān)閉 Statement 的時(shí)候,需要調(diào)用 ArrayList 的 remove() 方法來(lái)將其從 ArrayList 中刪除,而ArrayList的remove(Object)方法是從頭開始遍歷數(shù)組,而FastList是從數(shù)組的尾部開始遍歷,因此更為高效。假設(shè)一個(gè) Connection 依次創(chuàng)建 6 個(gè) Statement,分別是 S1、S2、S3、S4、S5、S6,而關(guān)閉 Statement 的順序一般都是逆序的,從S6 到 S1,而 ArrayList 的 remove(Object o) 方法是順序遍歷查找,逆序刪除而順序查找,這樣的查找效率就太慢了。因此FastList對(duì)其進(jìn)行優(yōu)化,改成了逆序查找。如下代碼為FastList 實(shí)現(xiàn)的數(shù)據(jù)移除操作,相比于ArrayList的 remove()代碼, FastList 去除了檢查范圍 和 從頭到尾遍歷檢查元素的步驟,其性能更快。

SpringBoot2.0 中 HikariCP 數(shù)據(jù)庫(kù)連接池的原理是什么

FastList 刪除操作

public boolean remove(Object element)
{
  // 刪除操作使用逆序查找
  for (int index = size - 1; index >= 0; index--) {
   if (element == elementData[index]) {
     final int numMoved = size - index - 1;
     // 如果角標(biāo)不是最后一個(gè),復(fù)制一個(gè)新的數(shù)組結(jié)構(gòu)
     if (numMoved > 0) {
      System.arraycopy(elementData, index + 1, elementData, index, numMoved);
     }
     //如果角標(biāo)是最后面的 直接初始化為null
     elementData[--size] = null;
     return true;
   }
  }
  return false;
}

通過(guò)上述源碼分析,F(xiàn)astList 的優(yōu)化點(diǎn)還是很簡(jiǎn)單的。相比ArrayList僅僅是去掉了rage檢查,擴(kuò)容優(yōu)化等細(xì)節(jié)處,刪除時(shí)數(shù)組從后往前遍歷查找元素等微小的調(diào)整,從而追求性能極致。當(dāng)然FastList 對(duì)于 ArrayList 的優(yōu)化,我們不能說(shuō)ArrayList不好。所謂定位不同、追求不同,ArrayList作為通用容器,更追求安全、穩(wěn)定,操作前rangeCheck檢查,對(duì)非法請(qǐng)求直接拋出異常,更符合 fail-fast(快速失敗)機(jī)制,而FastList追求的是性能極致。

下面我們?cè)賮?lái)聊聊 HiKariCP 中的另外一個(gè)數(shù)據(jù)結(jié)構(gòu) ConcurrentBag,看看它又是如何提升性能的。

4.2 ConcurrentBag 實(shí)現(xiàn)原理分析   

當(dāng)前主流數(shù)據(jù)庫(kù)連接池實(shí)現(xiàn)方式,大都用兩個(gè)阻塞隊(duì)列來(lái)實(shí)現(xiàn)。一個(gè)用于保存空閑數(shù)據(jù)庫(kù)連接的隊(duì)列 idle,另一個(gè)用于保存忙碌數(shù)據(jù)庫(kù)連接的隊(duì)列 busy;獲取連接時(shí)將空閑的數(shù)據(jù)庫(kù)連接從 idle 隊(duì)列移動(dòng)到 busy 隊(duì)列,而關(guān)閉連接時(shí)將數(shù)據(jù)庫(kù)連接從 busy 移動(dòng)到 idle。這種方案將并發(fā)問(wèn)題委托給了阻塞隊(duì)列,實(shí)現(xiàn)簡(jiǎn)單,但是性能并不是很理想。因?yàn)?Java SDK 中的阻塞隊(duì)列是用鎖實(shí)現(xiàn)的,而高并發(fā)場(chǎng)景下鎖的爭(zhēng)用對(duì)性能影響很大。

HiKariCP 并沒(méi)有使用 Java SDK 中的阻塞隊(duì)列,而是自己實(shí)現(xiàn)了一個(gè)叫做 ConcurrentBag 的并發(fā)容器,在連接池(多線程數(shù)據(jù)交互)的實(shí)現(xiàn)上具有比LinkedBlockingQueue和LinkedTransferQueue更優(yōu)越的性能。

ConcurrentBag 中最關(guān)鍵的屬性有 4 個(gè),分別是:用于存儲(chǔ)所有的數(shù)據(jù)庫(kù)連接的共享隊(duì)列 sharedList、線程本地存儲(chǔ) threadList、等待數(shù)據(jù)庫(kù)連接的線程數(shù) waiters 以及分配數(shù)據(jù)庫(kù)連接的工具 handoffQueue。其中,handoffQueue 用的是 Java SDK 提供的 SynchronousQueue,SynchronousQueue 主要用于線程之間傳遞數(shù)據(jù)。

ConcurrentBag 中的關(guān)鍵屬性

// 存放共享元素,用于存儲(chǔ)所有的數(shù)據(jù)庫(kù)連接
private final CopyOnWriteArrayList<T> sharedList;
// 在 ThreadLocal 緩存線程本地的數(shù)據(jù)庫(kù)連接,避免線程爭(zhēng)用
private final ThreadLocal<List<Object>> threadList;
// 等待數(shù)據(jù)庫(kù)連接的線程數(shù)
private final AtomicInteger waiters;
// 接力隊(duì)列,用來(lái)分配數(shù)據(jù)庫(kù)連接
private final SynchronousQueue<T> handoffQueue;

ConcurrentBag 保證了全部的資源均只能通過(guò) add() 方法進(jìn)行添加,當(dāng)線程池創(chuàng)建了一個(gè)數(shù)據(jù)庫(kù)連接時(shí),通過(guò)調(diào)用 ConcurrentBag 的 add() 方法加入到 ConcurrentBag 中,并通過(guò) remove() 方法進(jìn)行移出。下面是 add() 方法和 remove() 方法的具體實(shí)現(xiàn),添加時(shí)實(shí)現(xiàn)了將這個(gè)連接加入到共享隊(duì)列 sharedList 中,如果此時(shí)有線程在等待數(shù)據(jù)庫(kù)連接,那么就通過(guò) handoffQueue 將這個(gè)連接分配給等待的線程。

ConcurrentBag 的 add() 與 remove() 方法


public void add(final T bagEntry)
{
  if (closed) {
   LOGGER.info("ConcurrentBag has been closed, ignoring add()");
   throw new IllegalStateException("ConcurrentBag has been closed, ignoring add()");
  }
  // 新添加的資源優(yōu)先放入sharedList
  sharedList.add(bagEntry);
 
  // 當(dāng)有等待資源的線程時(shí),將資源交到等待線程 handoffQueue 后才返回
  while (waiters.get() > 0 && bagEntry.getState() == STATE_NOT_IN_USE && !handoffQueue.offer(bagEntry)) {
   yield();
  }
}
public boolean remove(final T bagEntry)
{
  // 如果資源正在使用且無(wú)法進(jìn)行狀態(tài)切換,則返回失敗
  if (!bagEntry.compareAndSet(STATE_IN_USE, STATE_REMOVED) && !bagEntry.compareAndSet(STATE_RESERVED, STATE_REMOVED) && !closed) {
   LOGGER.warn("Attempt to remove an object from the bag that was not borrowed or reserved: {}", bagEntry);
   return false;
  }
  // 從sharedList中移出
  final boolean removed = sharedList.remove(bagEntry);
  if (!removed && !closed) {
   LOGGER.warn("Attempt to remove an object from the bag that does not exist: {}", bagEntry);
  }
  return removed;
}

同時(shí)ConcurrentBag通過(guò)提供的 borrow() 方法來(lái)獲取一個(gè)空閑的數(shù)據(jù)庫(kù)連接,并通過(guò)requite()方法進(jìn)行資源回收,borrow() 的主要邏輯是:

  • 查看線程本地存儲(chǔ) threadList 中是否有空閑連接,如果有,則返回一個(gè)空閑的連接;

  • 如果線程本地存儲(chǔ)中無(wú)空閑連接,則從共享隊(duì)列 sharedList 中獲取;

  • 如果共享隊(duì)列中也沒(méi)有空閑的連接,則請(qǐng)求線程需要等待。

ConcurrentBag 的 borrow() 與 requite() 方法

// 該方法會(huì)從連接池中獲取連接, 如果沒(méi)有連接可用, 會(huì)一直等待timeout超時(shí)
public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException
{
  // 首先查看線程本地資源threadList是否有空閑連接
  final List<Object> list = threadList.get();
  // 從后往前反向遍歷是有好處的, 因?yàn)樽詈笠淮问褂玫倪B接, 空閑的可能性比較大, 之前的連接可能會(huì)被其他線程提前借走了
  for (int i = list.size() - 1; i >= 0; i--) {
   final Object entry = list.remove(i);
   @SuppressWarnings("unchecked")
   final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry;
   // 線程本地存儲(chǔ)中的連接也可以被竊取, 所以需要用CAS方法防止重復(fù)分配
   if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
     return bagEntry;
   }
  }
  // 當(dāng)無(wú)可用本地化資源時(shí),遍歷全部資源,查看可用資源,并用CAS方法防止資源被重復(fù)分配
  final int waiting = waiters.incrementAndGet();
  try {
   for (T bagEntry : sharedList) {
     if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
      // 因?yàn)榭赡堋皳屪摺绷似渌€程的資源,因此提醒包裹進(jìn)行資源添加
      if (waiting > 1) {
        listener.addBagItem(waiting - 1);
      }
      return bagEntry;
     }
   }
 
   listener.addBagItem(waiting);
   timeout = timeUnit.toNanos(timeout);
   do {
     final long start = currentTime();
     // 當(dāng)現(xiàn)有全部資源都在使用中時(shí),等待一個(gè)被釋放的資源或者一個(gè)新資源
     final T bagEntry = handoffQueue.poll(timeout, NANOSECONDS);
     if (bagEntry == null || bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
      return bagEntry;
     }
     timeout -= elapsedNanos(start);
   } while (timeout > 10_000);
   return null;
  }
  finally {
   waiters.decrementAndGet();
  }
}
 
public void requite(final T bagEntry)
{
  // 將資源狀態(tài)轉(zhuǎn)為未在使用
  bagEntry.setState(STATE_NOT_IN_USE);
  // 判斷是否存在等待線程,若存在,則直接轉(zhuǎn)手資源
  for (int i = 0; waiters.get() > 0; i++) {
   if (bagEntry.getState() != STATE_NOT_IN_USE || handoffQueue.offer(bagEntry)) {
     return;
   }
   else if ((i & 0xff) == 0xff) {
     parkNanos(MICROSECONDS.toNanos(10));
   }
   else {
     yield();
   }
  }
  // 否則,進(jìn)行資源本地化處理
  final List<Object> threadLocalList = threadList.get();
  if (threadLocalList.size() < 50) {
   threadLocalList.add(weakThreadLocals ? new WeakReference<>(bagEntry) : bagEntry);
  }
}

borrow() 方法可以說(shuō)是整個(gè) HikariCP 中最核心的方法,它是我們從連接池中獲取連接的時(shí)候最終會(huì)調(diào)用到的方法。需要注意的是 borrow() 方法只提供對(duì)象引用,不移除對(duì)象,因此使用時(shí)必須通過(guò) requite() 方法進(jìn)行放回,否則容易導(dǎo)致內(nèi)存泄露。requite() 方法首先將數(shù)據(jù)庫(kù)連接狀態(tài)改為未使用,之后查看是否存在等待線程,如果有則分配給等待線程;否則將該數(shù)據(jù)庫(kù)連接保存到線程本地存儲(chǔ)里。

ConcurrentBag 實(shí)現(xiàn)采用了queue-stealing的機(jī)制獲取元素:首先嘗試從ThreadLocal中獲取屬于當(dāng)前線程的元素來(lái)避免鎖競(jìng)爭(zhēng),如果沒(méi)有可用元素則再次從共享的CopyOnWriteArrayList中獲取。此外,ThreadLocal和CopyOnWriteArrayList在ConcurrentBag中都是成員變量,線程間不共享,避免了偽共享(false sharing)的發(fā)生。同時(shí)因?yàn)榫€程本地存儲(chǔ)中的連接是可以被其他線程竊取的,在共享隊(duì)列中獲取空閑連接,所以需要用 CAS 方法防止重復(fù)分配。

看完上述內(nèi)容,你們掌握SpringBoot2.0 中 HikariCP 數(shù)據(jù)庫(kù)連接池的原理是什么的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!

本文名稱:SpringBoot2.0中HikariCP數(shù)據(jù)庫(kù)連接池的原理是什么-創(chuàng)新互聯(lián)
瀏覽路徑:http://www.yijiale78.com/article20/ccdjjo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供云服務(wù)器虛擬主機(jī)品牌網(wǎng)站制作商城網(wǎng)站網(wǎng)站維護(hù)品牌網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

小程序開發(fā)