導航:首頁 > 數據處理 > go怎麼連資料庫

go怎麼連資料庫

發布時間:2024-10-19 02:11:04

① go語言資料庫連接池

Go語言使用gorm對MySQL進行性能測試

之前寫過了Go語言gorm框架MySQL實踐,其中對gorm框架在操作MySQL的各種基礎實踐,下面分享一下如何使用gorm框架對MySQL直接進行性能測試的簡單實踐。

這里我使用了一個原始的Go語言版本的FunTester測試框架,現在只有一個基本的方法,實在是因為Go語言特性太強了。框架設計的主要思路之一就是利用Go語言的閉包和方法參數特性,將一個func()當做性能測試的主題,通過不斷運行這個func()來實現性能測試。當然還有另外一個思路就是運行一個多線程任務類,類似Java版本的com.funtester.base.constaint.ThreadBase抽象類,這樣可以設置一些類的屬性,綁定一些測試資源,適配更多的測試場景。

下面演示select的性能測試,這里我用了隨機ID查詢的場景。

這里我使用從35開始遞增的ID進行刪除。

這里使用了select的用例部分,隨機ID,然後更新name欄位,隨機10個長度的字元串。

這里用到了FunTester欄位都是隨機生成。

到這里可以看出,性能測試框架用到的都是gorm框架的基礎API使用,這里MySQL連接池的管理工作完全交給了gorm框架完成,看資料說非常牛逼,我們只需要設置幾個參數。這個使用體現很像HttpClient設置HTTP連接池類似,這里我們也可以看出這些優秀的框架使用起來都是非常簡單的。

PS:關於gorm的基礎使用的請參考上一期的文章Go語言gorm框架MySQL實踐。

如何在Go語言中使用Redis連接池

一、關於連接池

一個資料庫伺服器只擁有有限的資源,並且如果你沒有充分使用這些資源,你可以通過使用更多的連接來提高吞吐量。一旦所有的資源都在使用,那麼你就不能通過增加更多的連接來提高吞吐量。事實上,吞吐量在連接負載較大時就開始下降了。通常可以通過限制與可用的資源相匹配的資料庫連接的數量來提高延遲和吞吐量。

如何在Go語言中使用Redis連接池

如果不使用連接池,那麼,每次傳輸數據,我們都需要進行創建連接,收發數據,關閉連接。在並發量不高的場景,基本上不會有什麼問題,一旦並發量上去了,那麼,一般就會遇到下面幾個常見問題:

性能普遍上不去

CPU大量資源被系統消耗

網路一旦抖動,會有大量TIME_WAIT產生,不得不定期重啟服務或定期重啟機器

伺服器工作不穩定,QPS忽高忽低

要想解決這些問題,我們就要用到連接池了。連接池的思路很簡單,在初始化時,創建一定數量的連接,先把所有長連接存起來,然後,誰需要使用,從這里取走,幹完活立馬放回來。如果請求數超出連接池容量,那麼就排隊等待、退化成短連接或者直接丟棄掉。

二、使用連接池遇到的坑

最近在一個項目中,需要實現一個簡單的WebServer提供Redis的HTTPinterface,提供JSON形式的返回結果。考慮用Go來實現。

首先,去看一下Redis官方推薦的GoRedisdriver。官方Star的項目有兩個:Radix.v2和Redigo。經過簡單的比較後,選擇了更加輕量級和實現更加優雅的Radix.v2。

Radix.v2包是根據功能劃分成一個個的subpackage,每一個subpackage在一個獨立的子目錄中,結構非常清晰。我的項目中會用到的subpackage有redis和pool。

由於我想讓這種被fork的進程最好簡單點,做的事情單一一些,所以,在沒有深入去看Radix.v2的pool的實現之前,我選擇了自己實現一個Redispool。(這里,就不貼代碼了。後來發現自己實現的Redispool與Radix.v2實現的Redispool的原理是一樣的,都是基於channel實現的,遇到的問題也是一樣的。)

不過在測試過程中,發現了一個詭異的問題。在請求過程中經常會報EOF錯誤。而且是概率性出現,一會有問題,一會又好了。通過反復的測試,發現bug是有規律的,當程序空閑一會後,再進行連續請求,會發生3次失敗,然後之後的請求都能成功,而我的連接池大小設置的是3。再進一步分析,程序空閑300秒後,再請求就會失敗,發現我的Redisserver配置了timeout300,至此,問題就清楚了。是連接超時Redisserver主動斷開了連接。客戶端這邊從一個超時的連接請求就會得到EOF錯誤。

然後我看了一下Radix.v2的pool包的源碼,發現這個庫本身並沒有檢測壞的連接,並替換為新server{location/pool{content_by_lua_block{localredis=require"resty.redis"localred=redis:new()localok,err=red:connect("127.0.0.1",6379)ifnotokthenngx.say("failedtoconnect:",err)returnendok,err=red:set("hello","world")ifnotokthenreturnendred:set_keepalive(10000,100)}}}

發現有個set_keepalive的方法,查了一下官方文檔,方法的原型是syntax:ok,err=red:set_keepalive(max_idle_timeout,pool_size)貌似max_idle_timeout這個參數,就是我們所缺少的東西,然後進一步跟蹤源碼,看看裡面是怎麼保證連接有效的。

function_M.set_keepalive(self,...)localsock=self.sockifnotsockthenreturnnil,"notinitialized"endifself.subscribedthenreturnnil,"subscribedstate"endreturnsock:setkeepalive(...)end

至此,已經清楚了,使用了tcp的keepalive心跳機制。

於是,通過與Radix.v2的作者一些討論,選擇自己在redis這層使用心跳機制,來解決這個問題。

四、最後的解決方案

在創建連接池之後,起一個goroutine,每隔一段idleTime發送一個PING到Redisserver。其中,idleTime略小於Redisserver的timeout配置。連接池初始化部分代碼如下:

p,err:=pool.New("tcp",u.Host,concurrency)errHndlr(err)gofunc(){for{p.Cmd("PING")time.Sleep(idelTime*time.Second)}}()

使用redis傳輸數據部分代碼如下:

funcredisDo(p*pool.Pool,cmdstring,args...interface{})(reply*redis.Resp,errerror){reply=p.Cmd(cmd,args...)iferr=reply.Err;err!=nil{iferr!=io.EOF{Fatal.Println("redis",cmd,args,"erris",err)}}return}

其中,Radix.v2連接池內部進行了連接池內連接的獲取和放回,代碼如下:

//,executesthegivencommand//(returningitsresult),(p*Pool)Cmd(cmdstring,args...interface{})*redis.Resp{c,err:=p.Get()iferr!=nil{returnredis.NewResp(err)}deferp.Put(c)returnc.Cmd(cmd,args...)}

這樣,我們就有了keepalive的機制,不會出現timeout的連接了,從redis連接池裡面取出的連接都是可用的連接了。看似簡單的代碼,卻完美的解決了連接池裡面超時連接的問題。同時,就算Redisserver重啟等情況,也能保證連接自動重連。

如何在go語言中使用redis連接池

1.在創建連接池之後,起一個goroutine,每隔一段idleTime發送一個PING到Redisserver。其中,idleTime略小於Redisserver的timeout配置。

2.連接池初始化部分代碼如下:

p,err:=pool.New("tcp",u.Host,concurrency)errHndlr(err)gofunc(){for{p.Cmd("PING")time.Sleep(idelTime*time.Second)}}()

3.使用redis傳輸數據部分代碼如下:

funcredisDo(p*pool.Pool,cmdstring,args...interface{})(reply*redis.Resp,errerror){reply=p.Cmd(cmd,args...)iferr=reply.Err;err!=nil{iferr!=io.EOF{Fatal.Println("redis",cmd,args,"erris",err)}}return}

4.其中,Radix.v2連接池內部進行了連接池內連接的獲取和放回,代碼如下:

//,executesthegivencommand//(returningitsresult),(p*Pool)Cmd(cmdstring,args...interface{})*redis.Resp{c,err:=p.Get()iferr!=nil{returnredis.NewResp(err)}deferp.Put(c)returnc.Cmd(cmd,args...)}

這樣,就有了系統keepalive的機制,不會出現timeout的連接了,從redis連接池裡面取出的連接都是可用的連接了。看似簡單的代碼,卻完美的解決了連接池裡面超時連接的問題。同時,就算Redisserver重啟等情況,也能保證連接自動重連。

使用Go實現一個資料庫連接池

開始本文之前,我們看一段Go連接資料庫的代碼:

本文內容我們將解釋連接池背後是如何工作的,並探索如何配置資料庫能改變或優化其性能。

轉自:

整理:地鼠文檔:

那麼sql.DB連接池是如何工作的呢?

需要理解的最重要一點是,sql.DB池包含兩種類型的連接——「正在使用」連接和「空閑」連接。當您使用連接執行資料庫任務(例如執行SQL語句或查詢行)時,該連接被標記為正在使用,任務完成後,該連接被標記為空閑。

當您使用Go執行資料庫操作時,它將首先檢查池中是否有可用的空閑連接。如果有可用的連接,那麼Go將重用這個現有連接,並在任務期間將其標記為正在使用。如果在您需要空閑連接時池中沒有空閑連接,那麼Go將創建一個新的連接。

當Go重用池中的空閑連接時,與該連接有關的任何問題都會被優雅地處理。異常連接將在放棄之前自動重試兩次,這時Go將從池中刪除異常連接並創建一個新的連接來執行該任務。

連接池有四個方法,我們可以使用它們來配置連接池的行為。讓我們一個一個地來討論。

SetMaxOpenConns()方法允許您設置池中「打開」連接(使用中+空閑連接)數量的上限。默認情況下,打開的連接數是無限的。

一般來說,MaxOpenConns設置得越大,可以並發執行的資料庫查詢就越多,連接池本身成為應用程序中的瓶頸的風險就越低。

但讓它無限並不是最好的選擇。默認情況下,PostgreSQL最多100個打開連接的硬限制,如果達到這個限制的話,它將導致pq驅動返回」sorry,toomanyclientsalready」錯誤。

為了避免這個錯誤,將池中打開的連接數量限制在100以下是有意義的,可以為其他需要使用PostgreSQL的應用程序或會話留下足夠的空間。

設置MaxOpenConns限制的另一個好處是,它充當一個非常基本的限流器,防止資料庫同時被大量任務壓垮。

但設定上限有一個重要的警告。如果達到MaxOpenConns限制,並且所有連接都在使用中,那麼任何新的資料庫任務將被迫等待,直到有連接空閑。在我們的API上下文中,用戶的HTTP請求可能在等待空閑連接時無限期地「掛起」。因此,為了緩解這種情況,使用上下文為資料庫任務設置超時是很重要的。我們將在書的後面解釋如何處理。

SetMaxIdleConns()方法的作用是:設置池中空閑連接數的上限。預設情況下,最大空閑連接數為2。

理論上,在池中允許更多的空閑連接將增加性能。因為它減少了從頭建立新連接發生概率—,因此有助於節省資源。

但要意識到保持空閑連接是有代價的。它佔用了本來可以用於應用程序和資料庫的內存,而且如果一個連接空閑時間過長,它也可能變得不可用。例如,默認情況下MySQL會自動關閉任何8小時未使用的連接。

因此,與使用更小的空閑連接池相比,將MaxIdleConns設置得過高可能會導致更多的連接變得不可用,浪費資源。因此保持適量的空閑連接是必要的。理想情況下,你只希望保持一個連接空閑,可以快速使用。

另一件要指出的事情是MaxIdleConns值應該總是小於或等於MaxOpenConns。Go會強制保證這點,並在必要時自動減少MaxIdleConns值。

SetConnMaxLifetime()方法用於設置ConnMaxLifetime的極限值,表示一個連接保持可用的最長時間。默認連接的存活時間沒有限制,永久可用。

如果設置ConnMaxLifetime的值為1小時,意味著所有的連接在創建後,經過一個小時就會被標記為失效連接,標志後就不可復用。但需要注意:

理論上,ConnMaxLifetime為無限大(或設置為很長生命周期)將提升性能,因為這樣可以減少新建連接。但是在某些情況下,設置短期存活時間有用。比如:

如果您決定對連接池設置ConnMaxLifetime,那麼一定要記住連接過期(然後重新創建)的頻率。例如,如果連接池中有100個打開的連接,而ConnMaxLifetime為1分鍾,那麼您的應用程序平均每秒可以殺死並重新創建多達1.67個連接。您不希望頻率太大而最終影響性能吧。

SetConnMaxIdleTime()方法在Go1.15版本引入對ConnMaxIdleTime進行配置。其效果和ConnMaxLifeTime類似,但這里設置的是:在被標記為失效之前一個連接最長空閑時間。例如,如果我們將ConnMaxIdleTime設置為1小時,那麼自上次使用以後在池中空閑了1小時的任何連接都將被標記為過期並被後台清理操作刪除。

這個配置非常有用,因為它意味著我們可以對池中空閑連接的數量設置相對較高的限制,但可以通過刪除不再真正使用的空閑連接來周期性地釋放資源。

所以有很多信息要吸收。這在實踐中意味著什麼?我們把以上所有的內容總結成一些可行的要點。

1、根據經驗,您應該顯式地設置MaxOpenConns值。這個值應該低於資料庫和操作系統對連接數量的硬性限制,您還可以考慮將其保持在相當低的水平,以充當基本的限流作用。

對於本書中的項目,我們將MaxOpenConns限制為25個連接。我發現這對於小型到中型的web應用程序和API來說是一個合理的初始值,但理想情況下,您應該根據基準測試和壓測結果調整這個值。

2、通常,更大的MaxOpenConns和MaxIdleConns值會帶來更好的性能。但是,效果是逐漸降低的,而且您應該注意,太多的空閑連接(連接沒有被復用)實際上會導致性能下降和不必要的資源消耗。

因為MaxIdleConns應該總是小於或等於MaxOpenConns,所以對於這個項目,我們還將MaxIdleConns限制為25個連接。

3、為了降低上面第2點的風險,通常應該設置ConnMaxIdleTime值來刪除長時間未使用的空閑連接。在這個項目中,我們將設置ConnMaxIdleTime持續時間為15分鍾。

4、ConnMaxLifetime默認設置為無限大是可以的,除非您的資料庫對連接生命周期施加了硬限制,或者您需要它協助一些操作,比如優雅地交換資料庫。這些都不適用於本項目,所以我們將保留這個默認的無限制配置。

與其硬編碼這些配置,不如更新cmd/api/main.go文件通過命令行參數讀取配置。

ConnMaxIdleTime值比較有意思,因為我們希望它傳遞一段時間,最終需要將其轉換為Go的time.Duration類型。這里有幾個選擇:

1、我們可以使用一個整數來表示秒(或分鍾)的數量,並將其轉換為time.Duration。

2、我們可以使用一個表示持續時間的字元串——比如「5s」(5秒)或「10m」(10分鍾)——然後使用time.ParseDuration()函數解析它。

3、兩種方法都可以很好地工作,但是在這個項目中我們將使用選項2。繼續並更新cmd/api/main.go文件如下:

File:cmd/api/main.go

golang配製高性能sql.DB

有很多教程是關於Go的sql.DB類型和如何使用它來執行SQL資料庫查詢的。但大多數內容都沒有講述SetMaxOpenConns(),SetMaxIdleConns()和SetConnMaxLifetime()方法,您可以使用它們來配置sql.DB的行為並改變其性能。

轉自:

整理:go語言中文文檔:

在本文我將詳細解釋這些設置的作用,並說明它們所能產生的(積極和消極)影響。

一個sql.DB對象就是一個資料庫連接池,它包含「正在用」和「空閑的」連接。一個正在用的連接指的是,你正用它來執行資料庫任務,例如執行SQL語句或行查詢。當任務完成連接就是空閑的。

當您創建sql.DB執行資料庫任務時,它將首先檢查連接池中是否有可用的空閑連接。如果有可用的連接,那麼Go將重用現有連接,並在執行任務期間將其標記為正在使用。如果池中沒有空閑連接,而您需要一個空閑連接,那麼Go將創建一個新的連接。

默認情況下,在同一時間打開連接的數量是沒有限制(包含使用中+空閑)。但你可以通過SetMaxOpenConns()方法實現自定義限制,如下所示:

在這個示例代碼中,連接池現在有5個並發打開的連接數。如果所有5個連接都已經被標記為正在使用,並且需要另一個新的連接,那麼應用程序將被迫等待,直到5個連接中的一個被釋放並變為空閑。

為了說明更改MaxOpenConns的影響,我運行了一個基準測試,將最大打開連接數設置為1、2、5、10和無限。基準測試在PostgreSQL資料庫上執行並行的INSERT語句,您可以在這里找到代碼。測試結果:

對於這個基準測試,我們可以看到,允許打開的連接越多,在資料庫上執行INSERT操作所花費的時間就越少(打開的連接數為1時,執行速度3129633ns/op,而無限連接:531030ns/op——大約快了6倍)。這是因為允許打開的連接越多,可以並發執行的資料庫查詢就越多。

默認情況下,sql.DB允許連接池中最多保留2個空閑連接。你可以通過SetMaxIdleConns()方法改變它,如下所示:

從理論上講,允許池中有更多的空閑連接將提高性能,因為這樣就不太可能從頭開始建立新連接——因此有助於提升資料庫性能。

讓我們來看看相同的基準測試,最大空閑連接設置為none,1,2,5和10:

當MaxIdleConns設置為none時,必須為每個INSERT從頭創建一個新的連接,我們可以從基準測試中看到,平均運行時和內存使用量相對較高。

只允許保留和重用一個空閑連接對基準測試影響特別明顯——它將平均運行時間減少了大約8倍,內存使用量減少了大約20倍。繼續增加空閑連接池的大小會使性能變得更好,盡管改進並不明顯。

那麼,您應該維護一個大的空閑連接池嗎?答案取決於應用程序。重要的是要意識到保持空閑連接是有代價的—它佔用了可以用於應用程序和資料庫的內存。

還有一種可能是,如果一個連接空閑時間太長,那麼它可能會變得不可用。例如,MySQL的wait_timeout設置將自動關閉任何8小時(默認)內未使用的連接。

當發生這種情況時,sql.DB會優雅地處理它。壞連接將自動重試兩次,然後放棄,此時Go將該連接從連接池中刪除,並創建一個新的連接。因此,將MaxIdleConns設置得太大可能會導致連接變得不可用,與空閑連接池更小(使用更頻繁的連接更少)相比,會佔有更多的資源。所以,如果你很可能很快就會再次使用,你只需保持一個空閑的連接。

最後要指出的是,MaxIdleConns應該總是小於或等於MaxOpenConns。Go強制執行此操作,並在必要時自動減少MaxIdleConns。

現在讓我們看看SetConnMaxLifetime()方法,它設置連接可重用的最大時間長度。如果您的SQL資料庫也實現了最大連接生命周期,或者—例如—您希望方便地在負載均衡器後交換資料庫,那麼這將非常有用。

你可以這樣使用它:

在這個例子中,所有的連接都將在創建後1小時「過期」,並且在過期後無法重用。但注意:

從理論上講,ConnMaxLifetime越短,連接過期的頻率就越高——因此,需要從頭創建連接的頻率就越高。為了說明這一點,我運行了將ConnMaxLifetime設置為100ms、200ms、500ms、1000ms和無限(永遠重用)的基準測試,默認設置為無限打開連接和2個空閑連接。這些時間段顯然比您在大多數應用程序中使用的時間要短得多,但它們有助於很好地說明行為。

在這些特定的基準測試中,我們可以看到,與無限生存期相比,在100ms生存期時內存使用量增加了3倍以上,而且每個INSERT的平均運行時也稍微長一些。

如果您在代碼中設置了ConnMaxLifetime,那麼一定要記住連接將過期(隨後重新創建)的頻率。例如,如果您總共有100個連接,而ConnMaxLifetime為1分鍾,那麼您的應用程序可能每秒鍾殺死和重新創建1.67個連接(平均值)。您不希望這個頻率太大,最終會阻礙性能,而不是提高性能。

最後,如果不說明超過資料庫連接數量的硬限制將會發生什麼,那麼本文就不完整了。為了說明這一點,我將修改postgresq

閱讀全文

與go怎麼連資料庫相關的資料

熱點內容
金域服務小程序電子發票怎麼看 瀏覽:243
如何申請企業代理 瀏覽:998
南方哪些地方有古玩市場 瀏覽:675
數控車床程序怎麼手工輸入 瀏覽:512
武漢市有哪些花卉市場 瀏覽:233
談代理讓別人加盟這是什麼工作 瀏覽:376
怎麼導入qq聊天數據 瀏覽:834
企業信用記錄中有哪些信息 瀏覽:478
天天農場怎麼和好友交易 瀏覽:259
蘋果6怎麼設置程序 瀏覽:326
初中要交的學籍信息是什麼 瀏覽:107
web前端技術面佔多少比例 瀏覽:540
今年榨油技術交流大會在哪裡 瀏覽:798
怎麼讓老闆回信息 瀏覽:778
電子應用技術學什麼 瀏覽:687
中國碳交易如何操作 瀏覽:487
海口蘭瑞莎品牌怎麼代理 瀏覽:544
環境光感技術是什麼 瀏覽:133
抖音如何能直播代理 瀏覽:996
進口發熱眼罩有哪些產品 瀏覽:894