亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Go語言配置數(shù)據(jù)庫連接池的實現(xiàn)

 更新時間:2021年12月08日 10:15:46   作者:Go語言由淺入深  
本文內(nèi)容我們將解釋連接池背后是如何工作的,并探索如何配置數(shù)據(jù)庫能改變或優(yōu)化其性能。文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下

開始本文之前,我們看一段Go連接數(shù)據(jù)庫的代碼:

//openDB()函數(shù)返回一個sql.DB連接池。
func openDB() (*sql.DB, error) {
    //使用sql.Open()創(chuàng)建一個空連接池
    db, err := sql.Open("postgres", "postgres://username:password@localhost/db_name")
    if err != nil {
        return nil, err
    }
 
    //創(chuàng)建一個具有5秒超時期限的上下文。
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
 
    //使用PingContext()建立到數(shù)據(jù)庫的新連接,并傳入上下文信息,連接超時就返回
    err = db.PingContext(ctx)
    if err != nil {
        return nil, err
    }
    // 返回sql.DB連接池
    return db, nil
}

本文內(nèi)容我們將解釋連接池背后是如何工作的,并探索如何配置數(shù)據(jù)庫能改變或優(yōu)化其性能。

注意:本文包含了很多的理論,雖然它很有趣,但對應(yīng)用程序的構(gòu)建并不重要。如果你覺得它太枯燥,可以先瀏覽一下,然后再回頭看。

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

需要理解的最重要一點(diǎn)是,sql.DB池包含兩種類型的連接——“正在使用”連接和“空閑”連接。當(dāng)您使用連接執(zhí)行數(shù)據(jù)庫任務(wù)(例如執(zhí)行SQL語句或查詢行)時,該連接被標(biāo)記為正在使用,任務(wù)完成后,該連接被標(biāo)記為空閑。

當(dāng)您使用Go執(zhí)行數(shù)據(jù)庫操作時,它將首先檢查池中是否有可用的空閑連接。如果有可用的連接,那么Go將重用這個現(xiàn)有連接,并在任務(wù)期間將其標(biāo)記為正在使用。如果在您需要空閑連接時池中沒有空閑連接,那么Go將創(chuàng)建一個新的連接。

當(dāng)Go重用池中的空閑連接時,與該連接有關(guān)的任何問題都會被優(yōu)雅地處理。異常連接將在放棄之前自動重試兩次,這時Go將從池中刪除異常連接并創(chuàng)建一個新的連接來執(zhí)行該任務(wù)。

配置連接池

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

SetMaxOpenConns方法

SetMaxOpenConns()方法允許您設(shè)置池中“打開”連接(使用中+空閑連接)數(shù)量的上限。默認(rèn)情況下,打開的連接數(shù)是無限的。

注意“打開”連接等于“正在使用”加上“空閑”連接,不僅僅是“正在使用”連接。

一般來說,MaxOpenConns設(shè)置得越大,可以并發(fā)執(zhí)行的數(shù)據(jù)庫查詢就越多,連接池本身成為應(yīng)用程序中的瓶頸的風(fēng)險就越低。

但讓它無限并不是最好的選擇。默認(rèn)情況下,PostgreSQL最多100個打開連接的硬限制,如果達(dá)到這個限制的話,它將導(dǎo)致pq驅(qū)動返回"sorry, too many clients already"錯誤。

注意:最大打開連接數(shù)限制可以在postgresql.conf文件中對max_connections設(shè)置來更改。

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

設(shè)置MaxOpenConns限制的另一個好處是,它充當(dāng)一個非?;镜南蘖髌鳎乐箶?shù)據(jù)庫同時被大量任務(wù)壓垮。

但設(shè)定上限有一個重要的警告。如果達(dá)到MaxOpenConns限制,并且所有連接都在使用中,那么任何新的數(shù)據(jù)庫任務(wù)將被迫等待,直到有連接空閑。在我們的API上下文中,用戶的HTTP請求可能在等待空閑連接時無限期地“掛起”。因此,為了緩解這種情況,使用上下文為數(shù)據(jù)庫任務(wù)設(shè)置超時是很重要的。我們將在書的后面解釋如何處理。

SetMaxIdleConns方法

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

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

但要意識到保持空閑連接是有代價的。它占用了本來可以用于應(yīng)用程序和數(shù)據(jù)庫的內(nèi)存,而且如果一個連接空閑時間過長,它也可能變得不可用。例如,默認(rèn)情況下MySQL會自動關(guān)閉任何8小時未使用的連接。

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

另一件要指出的事情是MaxIdleConns值應(yīng)該總是小于或等于MaxOpenConns。Go會強(qiáng)制保證這點(diǎn),并在必要時自動減少M(fèi)axIdleConns值。

SetConnMaxLifetime方法

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

如果設(shè)置ConnMaxLifetime的值為1小時,意味著所有的連接在創(chuàng)建后,經(jīng)過一個小時就會被標(biāo)記為失效連接,標(biāo)志后就不可復(fù)用。但需要注意:

  • 這并不能保證一個連接將在池中存在一整個小時;有可能某個連接由于某種原因變得不可用,并在此之前自動關(guān)閉。
  • 連接在創(chuàng)建后一個多小時內(nèi)仍然可以被使用—只是在這個時間之后它不能被重用。
  • 這不是一個空閑超時。連接將在創(chuàng)建后一小時過期,而不是在空閑后一小時過期。
  • Go每秒運(yùn)行一次后臺清理操作,從池中刪除過期的連接。

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

  • 如果SQL數(shù)據(jù)庫對連接強(qiáng)制設(shè)置最大存活時間,這時將ConnMaxLifetime設(shè)置稍短時間更合理。
  • 有助于數(shù)據(jù)庫替換

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

SetConnMaxIdleTime方法

SetConnMaxIdleTime()方法在Go 1.15版本引入對ConnMaxIdleTime進(jìn)行配置。其效果和ConnMaxLifeTime類似,但這里設(shè)置的是:在被標(biāo)記為失效之前一個連接最長空閑時間。例如,如果我們將ConnMaxIdleTime設(shè)置為1小時,那么自上次使用以后在池中空閑了1小時的任何連接都將被標(biāo)記為過期并被后臺清理操作刪除。

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

實操一波

所以有很多信息要吸收。這在實踐中意味著什么?我們把以上所有的內(nèi)容總結(jié)成一些可行的要點(diǎn)。

1、根據(jù)經(jīng)驗,您應(yīng)該顯式地設(shè)置MaxOpenConns值。這個值應(yīng)該低于數(shù)據(jù)庫和操作系統(tǒng)對連接數(shù)量的硬性限制,您還可以考慮將其保持在相當(dāng)?shù)偷乃?,以充?dāng)基本的限流作用。

對于本書中的項目,我們將MaxOpenConns限制為25個連接。我發(fā)現(xiàn)這對于小型到中型的web應(yīng)用程序和API來說是一個合理的初始值,但理想情況下,您應(yīng)該根據(jù)基準(zhǔn)測試和壓測結(jié)果調(diào)整這個值。

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

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

3、為了降低上面第2點(diǎn)的風(fēng)險,通常應(yīng)該設(shè)置ConnMaxIdleTime值來刪除長時間未使用的空閑連接。在這個項目中,我們將設(shè)置ConnMaxIdleTime持續(xù)時間為15分鐘。

4、ConnMaxLifetime默認(rèn)設(shè)置為無限大是可以的,除非您的數(shù)據(jù)庫對連接生命周期施加了硬限制,或者您需要它協(xié)助一些操作,比如優(yōu)雅地交換數(shù)據(jù)庫。這些都不適用于本項目,所以我們將保留這個默認(rèn)的無限制配置。

配置連接池

與其硬編碼這些配置,不如更新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

package main

import (
    "context" 
    "database/sql"
    "flag"
    "fmt"
    "log"
    "net/http"
    "os"
    "time"
    _ "github.com/lib/pq"
)

const version = "1.0.0"

//添加maxOpenConns, maxIdleConns和maxIdleTime字段來存放連接池配置
type config struct {
    port int
    env  string
    db   struct {
        dsn          string
                maxOpenConns int
                maxIdleConns int
                maxIdleTime  int
    }
}
type application struct {
    config config
    logger *log.Logger
}

func main() {
    var cfg config
    flag.IntVar(&cfg.port, "port", 4000, "API server port")
    flag.StringVar(&cfg.env, "env", "development", "Environment (development|staging|production)")
    flag.StringVar(&cfg.db.dsn, "db-dsn", "postgres://username:password@localhost/dbname", "PostgreSQL DSN")
   
        //從命令參數(shù)中讀取連接池配置到config結(jié)構(gòu)體中
        flag.IntVar(&cfg.db.maxOpenConns, "db-max-open-conns", 25, "PostgreSQL max open connections")
        flag.IntVar(&cfg.db.maxIdleConns, "db-max-idle-conns", 25, "PostgreSQL max idle connections") 
        flag.StringVar(&cfg.db.maxIdleTime, "db-max-idle-time", "15m", "PostgreSQL max connection idle time")
    flag.Parse()
    logger := log.New(os.Stdout, "", log.Ldate|log.Ltime)
  
    //調(diào)用openDB()幫助函數(shù)(參見下面)來創(chuàng)建連接池
    db, err := openDB(cfg)
    if err != nil {
        logger.Fatal(err)
    }
  
    // defer調(diào)用, 以便main()函數(shù)退出之前關(guān)閉連接池。
    defer db.Close()
  
    //打印連接數(shù)據(jù)庫成功日志
    logger.Printf("database connection pool established")
    app := &application{config: cfg,
        logger: logger}
    srv := &http.Server{
        Addr: fmt.Sprintf(":%d", cfg.port), Handler: app.routes(),
        IdleTimeout: time.Minute,
        ReadTimeout: 10 * time.Second, WriteTimeout: 30 * time.Second,
    }
    logger.Printf("starting %s server on %s", cfg.env, srv.Addr)
    err = srv.ListenAndServe()
    logger.Fatal(err)
}

func openDB(cfg config) (*sql.DB, error) {
    db, err := sql.Open("postgres", cfg.db.dsn)
    if err != nil {
        return nil, err
    }
  
        //設(shè)置最大開放連接數(shù),注意該值為小于0或等于0指的是無限制連接數(shù)
        db.SetMaxOpenConns(cfg.db.maxOpenConns)

        //設(shè)置空閑連接數(shù),小于等于0表示無限制
        db.SetMaxIdleConns(cfg.db.maxIdleConns)

        //將空閑時間字符串解析為time.Duration類型
        duration, err := time.ParseDuration(cfg.db.maxIdleTime)
        if err != nil {
            return nil, err
        }

        //設(shè)置最大空閑超時
        db.SetConnMaxIdleTime(duration)
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
 
    err = db.PingContext(ctx)
    if err != nil {
        return nil, err
    }
    return db, nil
}

到此這篇關(guān)于Go語言配置數(shù)據(jù)庫連接池的實現(xiàn)的文章就介紹到這了,更多相關(guān)Go語言 數(shù)據(jù)庫連接池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • go語言中struct標(biāo)簽詳解

    go語言中struct標(biāo)簽詳解

    這篇文章主要給大家介紹了關(guān)于go語言中struct標(biāo)簽的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用go語言具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2023-07-07
  • golang特有程序結(jié)構(gòu)入門教程

    golang特有程序結(jié)構(gòu)入門教程

    GO語言是一門不錯的編程語言能夠到達(dá)靜態(tài)編譯語言的安全和性能,在本文中重點(diǎn)給大家介紹goland特有程序結(jié)構(gòu)及引用類型別名的特征,感興趣的朋友跟隨小編一起看看吧
    2021-06-06
  • Go一站式配置管理工具Viper的使用教程

    Go一站式配置管理工具Viper的使用教程

    Viper是一個方便Go語言應(yīng)用程序處理配置信息的庫,它可以處理多種格式的配置,這篇文章主要為大家介紹了它的具體使用教程,需要的可以參考下
    2023-08-08
  • Go語言基礎(chǔ)go接口用法示例詳解

    Go語言基礎(chǔ)go接口用法示例詳解

    這篇文章主要為大家介紹了Go語言基礎(chǔ)關(guān)于go接口的用法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,在日升職加薪
    2021-11-11
  • Golang易錯知識點(diǎn)匯總

    Golang易錯知識點(diǎn)匯總

    這篇文章匯總了在開發(fā)和刷面試題過程中遇到的Golang容易搞錯的知識點(diǎn),關(guān)鍵部分也都為大家寫了代碼示例,感興趣的小伙伴可以了解一下
    2022-09-09
  • goLang引入自定義包的方法

    goLang引入自定義包的方法

    今天小編就為大家分享一篇goLang引入自定義包的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-06-06
  • PHP結(jié)構(gòu)型模式之組合模式

    PHP結(jié)構(gòu)型模式之組合模式

    這篇文章主要介紹了PHP組合模式Composite Pattern優(yōu)點(diǎn)與實現(xiàn),組合模式是一種結(jié)構(gòu)型模式,它允許你將對象組合成樹形結(jié)構(gòu)來表示“部分-整體”的層次關(guān)系。組合能讓客戶端以一致的方式處理個別對象和對象組合
    2023-04-04
  • Golang變量直接初始化的方法詳解

    Golang變量直接初始化的方法詳解

    在 Go 語言中,我們常用的數(shù)據(jù)結(jié)構(gòu)有在Go語言中,你可以初始化不同的數(shù)據(jù)結(jié)構(gòu),例如數(shù)組、切片、結(jié)構(gòu)體、指針、map等,本文將給大家介紹一下Golang變量直接初始化的方法,需要的朋友可以參考下
    2023-08-08
  • 淺談golang結(jié)構(gòu)體偷懶初始化

    淺談golang結(jié)構(gòu)體偷懶初始化

    這篇文章主要介紹了淺談golang結(jié)構(gòu)體偷懶初始化,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 淺談golang package中init方法的多處定義及運(yùn)行順序問題

    淺談golang package中init方法的多處定義及運(yùn)行順序問題

    這篇文章主要介紹了淺談golang package中init方法的多處定義及運(yùn)行順序問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05

最新評論