有很多教程是關于Go的sql.DB類型和如何使用它來執(zhí)行SQL數(shù)據(jù)庫查詢的。但大多數(shù)內(nèi)容都沒有講述 SetMaxOpenConns() , SetMaxIdleConns() 和 SetConnMaxLifetime()方法, 您可以使用它們來配置sql.DB的行為并改變其性能。
創(chuàng)新互聯(lián)公司從2013年創(chuàng)立,是專業(yè)互聯(lián)網(wǎng)技術服務公司,擁有項目成都做網(wǎng)站、網(wǎng)站設計網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元烏達做網(wǎng)站,已為上家服務,為烏達各地企業(yè)和個人服務,聯(lián)系電話:18982081108
轉(zhuǎn)自:
整理:go語言中文文檔:
在本文我將詳細解釋這些設置的作用,并說明它們所能產(chǎn)生的(積極和消極)影響。
一個sql.DB對象就是一個數(shù)據(jù)庫連接池,它包含“正在用”和“空閑的”連接。一個正在用的連接指的是,你正用它來執(zhí)行數(shù)據(jù)庫任務,例如執(zhí)行SQL語句或行查詢。當任務完成連接就是空閑的。
當您創(chuàng)建sql.DB執(zhí)行數(shù)據(jù)庫任務時,它將首先檢查連接池中是否有可用的空閑連接。如果有可用的連接,那么Go將重用現(xiàn)有連接,并在執(zhí)行任務期間將其標記為正在使用。如果池中沒有空閑連接,而您需要一個空閑連接,那么Go將創(chuàng)建一個新的連接。
默認情況下,在同一時間打開連接的數(shù)量是沒有限制(包含使用中+空閑)。但你可以通過SetMaxOpenConns()方法實現(xiàn)自定義限制,如下所示:
在這個示例代碼中,連接池現(xiàn)在有5個并發(fā)打開的連接數(shù)。如果所有5個連接都已經(jīng)被標記為正在使用,并且需要另一個新的連接,那么應用程序?qū)⒈黄鹊却?,直?個連接中的一個被釋放并變?yōu)榭臻e。
為了說明更改MaxOpenConns的影響,我運行了一個基準測試,將最大打開連接數(shù)設置為1、2、5、10和無限?;鶞蕼y試在PostgreSQL數(shù)據(jù)庫上執(zhí)行并行的INSERT語句,您可以在這里找到代碼。測試結(jié)果:
對于這個基準測試,我們可以看到,允許打開的連接越多,在數(shù)據(jù)庫上執(zhí)行INSERT操作所花費的時間就越少(打開的連接數(shù)為1時,執(zhí)行速度3129633ns/op,而無限連接:531030ns/op——大約快了6倍)。這是因為允許打開的連接越多,可以并發(fā)執(zhí)行的數(shù)據(jù)庫查詢就越多。
默認情況下,sql.DB允許連接池中最多保留2個空閑連接。你可以通過SetMaxIdleConns()方法改變它,如下所示:
從理論上講,允許池中有更多的空閑連接將提高性能,因為這樣就不太可能從頭開始建立新連接——因此有助于提升數(shù)據(jù)庫性能。
讓我們來看看相同的基準測試,最大空閑連接設置為none, 1,2,5和10:
當MaxIdleConns設置為none時,必須為每個INSERT從頭創(chuàng)建一個新的連接,我們可以從基準測試中看到,平均運行時和內(nèi)存使用量相對較高。
只允許保留和重用一個空閑連接對基準測試影響特別明顯——它將平均運行時間減少了大約8倍,內(nèi)存使用量減少了大約20倍。繼續(xù)增加空閑連接池的大小會使性能變得更好,盡管改進并不明顯。
那么,您應該維護一個大的空閑連接池嗎?答案取決于應用程序。重要的是要意識到保持空閑連接是有代價的—它占用了可以用于應用程序和數(shù)據(jù)庫的內(nèi)存。
還有一種可能是,如果一個連接空閑時間太長,那么它可能會變得不可用。例如,MySQL的wait_timeout設置將自動關閉任何8小時(默認)內(nèi)未使用的連接。
當發(fā)生這種情況時,sql.DB會優(yōu)雅地處理它。壞連接將自動重試兩次,然后放棄,此時Go將該連接從連接池中刪除,并創(chuàng)建一個新的連接。因此,將MaxIdleConns設置得太大可能會導致連接變得不可用,與空閑連接池更小(使用更頻繁的連接更少)相比,會占有更多的資源。所以,如果你很可能很快就會再次使用,你只需保持一個空閑的連接。
最后要指出的是,MaxIdleConns應該總是小于或等于MaxOpenConns。Go強制執(zhí)行此操作,并在必要時自動減少MaxIdleConns。
現(xiàn)在讓我們看看SetConnMaxLifetime()方法,它設置連接可重用的最大時間長度。如果您的SQL數(shù)據(jù)庫也實現(xiàn)了最大連接生命周期,或者—例如—您希望方便地在負載均衡器后交換數(shù)據(jù)庫,那么這將非常有用。
你可以這樣使用它:
在這個例子中,所有的連接都將在創(chuàng)建后1小時“過期”,并且在過期后無法重用。但注意:
從理論上講,ConnMaxLifetime越短,連接過期的頻率就越高——因此,需要從頭創(chuàng)建連接的頻率就越高。為了說明這一點,我運行了將ConnMaxLifetime設置為100ms、200ms、500ms、1000ms和無限(永遠重用)的基準測試,默認設置為無限打開連接和2個空閑連接。這些時間段顯然比您在大多數(shù)應用程序中使用的時間要短得多,但它們有助于很好地說明行為。
在這些特定的基準測試中,我們可以看到,與無限生存期相比,在100ms生存期時內(nèi)存使用量增加了3倍以上,而且每個INSERT的平均運行時也稍微長一些。
如果您在代碼中設置了ConnMaxLifetime,那么一定要記住連接將過期(隨后重新創(chuàng)建)的頻率。例如,如果您總共有100個連接,而ConnMaxLifetime為1分鐘,那么您的應用程序可能每秒鐘殺死和重新創(chuàng)建1.67個連接(平均值)。您不希望這個頻率太大,最終會阻礙性能,而不是提高性能。
最后,如果不說明超過數(shù)據(jù)庫連接數(shù)量的硬限制將會發(fā)生什么,那么本文就不完整了。 為了說明這一點,我將修改postgresql.conf文件,這樣總共只允許5個連接(默認是100個)…
然后在無限連接的情況下重新運行基準測試……
一旦達到5個連接的硬限制,數(shù)據(jù)庫驅(qū)動程序(pq)立即返回一個太多客戶端連接的錯誤消息,而無法完成INSERT。為了防止這個錯誤,我們需要將sql.DB中打開連接的最大總數(shù)(正在使用的+空閑的)設置為低于5。像這樣:
現(xiàn)在,sql.DB在任何時候最多只能創(chuàng)建3個連接,基準測試運行時應該不會出現(xiàn)任何錯誤。但是這樣做需要注意:當達到開放連接數(shù)限制,并且所有連接都在使用時,應用程序需要執(zhí)行的任何新的數(shù)據(jù)庫任務都將被迫等待,直到連接標記為空閑。例如,在web應用程序的上下文中,用戶的HTTP請求看起來會“掛起”,甚至在等待數(shù)據(jù)庫任務運行時可能會超時。
為了減輕這種情況,你應該始終在一個上下文中傳遞。在調(diào)用數(shù)據(jù)庫時,啟用上下文的方法(如ExecContext()),使用固定的、快速的超時上下文對象。
總結(jié)
1、根據(jù)經(jīng)驗,應該顯式設置MaxOpenConns值。這應該小于數(shù)據(jù)庫和基礎設施對連接數(shù)量的硬性限制。
2、一般來說,更高的MaxOpenConns和MaxIdleConns值將帶來更好的性能。但你應該注意到效果是遞減的,連接池空閑連接太多(連接沒有被重用,最終會變壞)實際上會導致性能下降。
3、為了降低上面第2點帶來的風險,您可能需要設置一個相對較短的ConnMaxLifetime。但你也不希望它太短,導致連接被殺死或不必要地頻繁重建。
4、MaxIdleConns應該總是小于或等于MaxOpenConns。
對于中小型web應用程序,我通常使用以下設置作為起點,然后根據(jù)實際吞吐量水平的負載測試結(jié)果進行優(yōu)化。
我建議最好是從基礎入手,而不是一開始就進行可視化編程。雖然如今國內(nèi)絕大多數(shù)pc都是使用的windows,但是畢竟這知識這個世界的冰山一角。扎實的基礎自然會更有用處。編程其實重要的是程序思維,然后是算法和數(shù)據(jù)結(jié)構(gòu)。這些都是超出語言的,就是說不管是學c學java學delphi還是別的什么,這一部分都是一致的。因此培養(yǎng)這部分的知識可以說是一本萬利的事情。初學肯定是通過語言熟悉思想熟悉算法和數(shù)據(jù)結(jié)構(gòu),到一定的時候就是純粹的思想和算法數(shù)據(jù)結(jié)構(gòu)的學習,便已經(jīng)脫離程序語言了。經(jīng)歷過這些階段,換一種語言不過是重新了解一下描述的方式,就像你了解了中文思維,山東話和四川話的差別就不會太大;了解了拉丁語的思維,整個語系的語言都不過是簡簡單單的記憶工作,應用就好。入門的語言,理論上是怎么方便學哪個,看那個順眼學哪個。當然這里面還是有不同的推薦的。一般來說我比較推薦pascal、c/c++、java。并不是因為這三個東西很通用很有前途,而是它們實在是嚴整而有規(guī)則(c/c++還顯得稍微的寬松了一點),而嚴謹?shù)恼Z法要求和明確的概念區(qū)分是有利于編程思維的形成和算法數(shù)據(jù)結(jié)構(gòu)的學習的。同樣的因為這個理由我不推薦vb,而并不是因為它功能不強大(事實上vb在windows環(huán)境中是相當牛的語言)另外一個建議是,如果學c,不要一開始就用vc。ms提供的很多東西很方便,有很多很簡單的實現(xiàn)方法,但是它們不標準。vc與ansi
c標準是有很大的差距的。首先一個不遵循標準的c/c++程序是不通用的,換個編譯器說不定就不被承認了。所以我非常推崇gcc,理由之一是它完全符合
ansi
c標準,無論它的c還是c++編譯器都很嚴整,功能上一點也不缺乏(有人說gcc不能做圖形界面的程序,這一點完全錯誤,到處都有的qt庫和gtk庫都能做出很好的界面),另外一個理由便是它免費,畢竟稍微大一點的軟件企業(yè)就不會屈從與微軟的編譯器和平臺,而一個免費的c編譯器無疑可以創(chuàng)造更多的利益;就算要轉(zhuǎn)vc,標準的c程序也是幾乎不要作任何改動的。當然,這一切的前提是,你真的很想很好的學編程,做一個這方面的精英。如果只不過是興趣,或者只是想拿一個ms的工程師認證然后在國內(nèi)企業(yè)找份諸如設計vf、vb程序之類的工作,那完全可以忽略我上面的話,去找個認證培訓班,認認真真聽聽課,好好完成練習,從vb或者vc入手,考好認證是很不會太難的。畢竟現(xiàn)在很多很好的大學里都從來不缺乏計算機的課程,不會缺少算法或者編譯原理的課程,不會沒有計算機科學的研究院,而那里面出來的人一般都具備了很好的基礎知識,會更加容易成為前面所說的精英。
開始本文之前,我們看一段Go連接數(shù)據(jù)庫的代碼:
本文內(nèi)容我們將解釋連接池背后是如何工作的,并 探索 如何配置數(shù)據(jù)庫能改變或優(yōu)化其性能。
轉(zhuǎn)自:
整理:地鼠文檔:
那么sql.DB連接池是如何工作的呢?
需要理解的最重要一點是,sql.DB池包含兩種類型的連接——“正在使用”連接和“空閑”連接。當您使用連接執(zhí)行數(shù)據(jù)庫任務(例如執(zhí)行SQL語句或查詢行)時,該連接被標記為正在使用,任務完成后,該連接被標記為空閑。
當您使用Go執(zhí)行數(shù)據(jù)庫操作時,它將首先檢查池中是否有可用的空閑連接。如果有可用的連接,那么Go將重用這個現(xiàn)有連接,并在任務期間將其標記為正在使用。如果在您需要空閑連接時池中沒有空閑連接,那么Go將創(chuàng)建一個新的連接。
當Go重用池中的空閑連接時,與該連接有關的任何問題都會被優(yōu)雅地處理。異常連接將在放棄之前自動重試兩次,這時Go將從池中刪除異常連接并創(chuàng)建一個新的連接來執(zhí)行該任務。
連接池有四個方法,我們可以使用它們來配置連接池的行為。讓我們一個一個地來討論。
SetMaxOpenConns()方法允許您設置池中“打開”連接(使用中+空閑連接)數(shù)量的上限。默認情況下,打開的連接數(shù)是無限的。
一般來說,MaxOpenConns設置得越大,可以并發(fā)執(zhí)行的數(shù)據(jù)庫查詢就越多,連接池本身成為應用程序中的瓶頸的風險就越低。
但讓它無限并不是最好的選擇。默認情況下,PostgreSQL最多100個打開連接的硬限制,如果達到這個限制的話,它將導致pq驅(qū)動返回”sorry, too many clients already”錯誤。
為了避免這個錯誤,將池中打開的連接數(shù)量限制在100以下是有意義的,可以為其他需要使用PostgreSQL的應用程序或會話留下足夠的空間。
設置MaxOpenConns限制的另一個好處是,它充當一個非?;镜南蘖髌?,防止數(shù)據(jù)庫同時被大量任務壓垮。
但設定上限有一個重要的警告。如果達到MaxOpenConns限制,并且所有連接都在使用中,那么任何新的數(shù)據(jù)庫任務將被迫等待,直到有連接空閑。在我們的API上下文中,用戶的HTTP請求可能在等待空閑連接時無限期地“掛起”。因此,為了緩解這種情況,使用上下文為數(shù)據(jù)庫任務設置超時是很重要的。我們將在書的后面解釋如何處理。
SetMaxIdleConns()方法的作用是:設置池中空閑連接數(shù)的上限。缺省情況下,最大空閑連接數(shù)為2。
理論上,在池中允許更多的空閑連接將增加性能。因為它減少了從頭建立新連接發(fā)生概率—,因此有助于節(jié)省資源。
但要意識到保持空閑連接是有代價的。它占用了本來可以用于應用程序和數(shù)據(jù)庫的內(nèi)存,而且如果一個連接空閑時間過長,它也可能變得不可用。例如,默認情況下MySQL會自動關閉任何8小時未使用的連接。
因此,與使用更小的空閑連接池相比,將MaxIdleConns設置得過高可能會導致更多的連接變得不可用,浪費資源。因此保持適量的空閑連接是必要的。理想情況下,你只希望保持一個連接空閑,可以快速使用。
另一件要指出的事情是MaxIdleConns值應該總是小于或等于MaxOpenConns。Go會強制保證這點,并在必要時自動減少MaxIdleConns值。
SetConnMaxLifetime()方法用于設置ConnMaxLifetime的極限值,表示一個連接保持可用的最長時間。默認連接的存活時間沒有限制,永久可用。
如果設置ConnMaxLifetime的值為1小時,意味著所有的連接在創(chuàng)建后,經(jīng)過一個小時就會被標記為失效連接,標志后就不可復用。但需要注意:
理論上,ConnMaxLifetime為無限大(或設置為很長生命周期)將提升性能,因為這樣可以減少新建連接。但是在某些情況下,設置短期存活時間有用。比如:
如果您決定對連接池設置ConnMaxLifetime,那么一定要記住連接過期(然后重新創(chuàng)建)的頻率。例如,如果連接池中有100個打開的連接,而ConnMaxLifetime為1分鐘,那么您的應用程序平均每秒可以殺死并重新創(chuàng)建多達1.67個連接。您不希望頻率太大而最終影響性能吧。
SetConnMaxIdleTime()方法在Go 1.15版本引入對ConnMaxIdleTime進行配置。其效果和ConnMaxLifeTime類似,但這里設置的是:在被標記為失效之前一個連接最長空閑時間。例如,如果我們將ConnMaxIdleTime設置為1小時,那么自上次使用以后在池中空閑了1小時的任何連接都將被標記為過期并被后臺清理操作刪除。
這個配置非常有用,因為它意味著我們可以對池中空閑連接的數(shù)量設置相對較高的限制,但可以通過刪除不再真正使用的空閑連接來周期性地釋放資源。
所以有很多信息要吸收。這在實踐中意味著什么?我們把以上所有的內(nèi)容總結(jié)成一些可行的要點。
1、根據(jù)經(jīng)驗,您應該顯式地設置MaxOpenConns值。這個值應該低于數(shù)據(jù)庫和操作系統(tǒng)對連接數(shù)量的硬性限制,您還可以考慮將其保持在相當?shù)偷乃?,以充當基本的限流作用?/p>
對于本書中的項目,我們將MaxOpenConns限制為25個連接。我發(fā)現(xiàn)這對于小型到中型的web應用程序和API來說是一個合理的初始值,但理想情況下,您應該根據(jù)基準測試和壓測結(jié)果調(diào)整這個值。
2、通常,更大的MaxOpenConns和MaxIdleConns值會帶來更好的性能。但是,效果是逐漸降低的,而且您應該注意,太多的空閑連接(連接沒有被復用)實際上會導致性能下降和不必要的資源消耗。
因為MaxIdleConns應該總是小于或等于MaxOpenConns,所以對于這個項目,我們還將MaxIdleConns限制為25個連接。
3、為了降低上面第2點的風險,通常應該設置ConnMaxIdleTime值來刪除長時間未使用的空閑連接。在這個項目中,我們將設置ConnMaxIdleTime持續(xù)時間為15分鐘。
4、ConnMaxLifetime默認設置為無限大是可以的,除非您的數(shù)據(jù)庫對連接生命周期施加了硬限制,或者您需要它協(xié)助一些操作,比如優(yōu)雅地交換數(shù)據(jù)庫。這些都不適用于本項目,所以我們將保留這個默認的無限制配置。
與其硬編碼這些配置,不如更新cmd/api/main.go文件通過命令行參數(shù)讀取配置。
ConnMaxIdleTime值比較有意思,因為我們希望它傳遞一段時間,最終需要將其轉(zhuǎn)換為Go的time.Duration類型。這里有幾個選擇:
1、我們可以使用一個整數(shù)來表示秒(或分鐘)的數(shù)量,并將其轉(zhuǎn)換為time.Duration。
2、我們可以使用一個表示持續(xù)時間的字符串——比如“5s”(5秒)或“10m”(10分鐘)——然后使用time.ParseDuration()函數(shù)解析它。
3、兩種方法都可以很好地工作,但是在這個項目中我們將使用選項2。繼續(xù)并更新cmd/api/main.go文件如下:
File: cmd/api/main.go
go數(shù)據(jù)庫有sql2go官網(wǎng)。
用于將 sql 語句轉(zhuǎn)換為 golang 的 struct. 使用 ddl 語句即可。例如對于創(chuàng)建表的語句: show create table xxx. 將輸出的語句,直接粘貼進去就行。toml2go網(wǎng)。用于將編碼后的 toml 文本轉(zhuǎn)換問 golang 的 struct。
非關系型數(shù)據(jù)庫(NoSQL):
指的是分布式的、非關系型的、不保證遵循ACID原則的數(shù)據(jù)存儲系統(tǒng)。NoSQL數(shù)據(jù)庫技術與CAP理論、一致性哈希算法有密切關系。所謂CAP理論,簡單來說就是一個分布式系統(tǒng)不可能滿足可用性、一致性與分區(qū)容錯性這三個要求。
一次性滿足兩種要求是該系統(tǒng)的上限。而一致性哈希算法則指的是NoSQL數(shù)據(jù)庫在應用過程中,為滿足工作需求而在通常情況下產(chǎn)生的一種數(shù)據(jù)算法,該算法能有效解決工作方面的諸多問題但也存在弊端,即工作完成質(zhì)量會隨著節(jié)點的變化而產(chǎn)生波動。
GO在存儲過程或語句中起結(jié)束,終止上面語句的作用。
官網(wǎng)的解釋:
GO用信號通知 Microsoft? SQL Server? 實用工具一批 Transact-SQL 語句的結(jié)束。
注釋:
GO 不是 Transact-SQL 語句;而是可為 osql 和 isql 實用工具及 SQL Server 查詢分析器識別的命令。
SQL Server 實用工具將 GO 解釋為應將當前的 Transact-SQL 批處理語句發(fā)送給 SQL Server 的信號。當前批處理語句是自上一 GO 命令后輸入的所有語句,若是第一條 GO 命令,則是從特殊會話或腳本的開始處到這條 GO 命令之間的所有語句。SQL 查詢分析器和 osql 及 isql 命令提示實用工具執(zhí)行 GO 命令的方式不同。有關更多信息,請參見 osql 實用工具、isql 實用工具和 SQL 查詢分析器。
GO 命令和Transact-SQL 語句不可在同一行上。但在 GO 命令行中可包含注釋。
用戶必須遵照使用批處理的規(guī)則。例如,在批處理中的第一條語句后執(zhí)行任何存儲過程必須包含 EXECUTE 關鍵字。局部(用戶定義)變量的作用域限制在一個批處理中,不可在 GO 命令后引用。