Connection Pool

Connection Pool 

              คือ ระบบงานที่มีการติดต่อกับฐานข้อมูลตลอดเวลา หรือติดต่อบ้าง ไม่ติดต่อบ้าง  หรือมีจำนวน Client เข้ามาติดต่อเป็นจำนวนมาก เปิดฐานข้อมูล แล้วก็ปิด  แล้วรู้หรือไม่ครับ ว่ามันทำงานอย่างไร  การเรียกใช้ฐานข้อมูลแต่ละครั้ง  อย่างน้อยจะต้องมี Overhead ในส่วนของการสถาปนาการเชื่อมต่อ ซึ่งจุดนี้ครับ เป็นหัวใจครับ หากเราลดการสถาปนาการเชื่อมต่อกับฐานข้อมูลได้  การติดต่อระหว่าง Client ต่างๆ ที่จะเข้ามานั้น ก็จะมีประสิทธิภาพมากขึ้นนั่นเอง เราจึงต้องใช้ Connection Pool นะครับ

วิธีที่ pool ทำงาน

Conection pool ทำงานโดยใช้แนวคิดง่ายๆ สมมุติตัวแปรให้ *sql.DB มีชื่อเล่นว่า “คุณชาย d” ต้องการเข้าถึงฐานข้อมูลข้างใต้ออปเจ็กต์ *sql.DB และการเชื่อมต่อหรือ connection ชื่อเล่นว่าน้อง c นะ
ชาย d จะร้องขอ connection หรือน้อง c จากบ้าน pool ก่อน ถ้ามีว่างเหลืออยู่ (idle) ก็จ่ายน้องมาให้ 1 คน ถ้าไม่มีก็จะปั๊มเพิ่มให้ใหม่ ถึงตอนนี้น้อง c จะถูกจับจองเป็นเจ้าของหัวใจ (ownership)โดยชาย d  เมื่อเค้าคนนั้นเสร็จกิจสมฤดี เค้าก็จะทำอย่างใดอย่างหนึ่งระหว่างส่ง น้อง c กลับไปยังบ้าน poll หรือไม่ก็ส่งต่อความเป็นเจ้าข้าวเจ้าของให้กับ object อื่นไปเวียนใช้เยี่ยงทาสก่อนจะส่งกลับบ้าน poll แค่นี้แหละ

database-152940_960_720

ทีนี้มีเมธอด…เอ้ยนิสัยอะไรบ้างที่ คุณชาย d มีและชอบทำมิดีมิร้ายกับน้อง c ยังไงบ้าง

  • คุณชาย db.Ping() เมื่อสั่ง Ping ปุ๊บจะส่งตัวน้อง c คืน pool ทันทีหลังเสร็จกิจล่มปากอ่าวยังไม่ทันทำอันใด
  • คุณชาย db.Exec() เมื่อเสร็จกิจจะส่งตัวน้อง c กลับ pool ทันที, แต่จะมีการคืนค่าออปเจกต์ผลลัพธ์ (result) ที่ยังคงอ้างอิงแอบกุ๊กกิ๊กกับน้อง c อยู่ให้กับเราด้วย, ดังนั้นมันอาจถูกเราเรียกมาจิกหัวใช้เมื่อใดก็ได้เพื่อการตรวจสอบผลลัพธ์ของฟังค์ชั่น Exec()
  • คุณชาย db.Query() จะส่งต่อความเป็นเจ้าของ (ownership) ของน้อง c ไปยังออปเจ็ค sql.Rows, ซึ่งจะคืนน้องกลับไปยัง pool เมื่อคุณสั่งให้เวียนเทียนเสร็จกิจทุกแถวแล้ว หรือไม่ก็สั่ง .Close() สั่งตัดเยื่อใยขึ้นมากลางคันก็ได้
  • คุณชาย db.QueryRow() จะส่งน้อง c ให้กับ ลูกน้องชื่อ sql.Row, ซึ่งจะปล่อยเธอไปเมื่อเค้าสั่ง .Scan() เรือนร่างเธอเสร็จแล้ว
  • คุณชาย db.Begin() จะส่งน้อง c ให้กับคนขับรถชื่อ sql.Tx, ซึ่งจะปลดปล่อยเธอเมื่อมีการสั่ง .Commit() หรือ .Rollback() เท่านั้น
ตัวอย่างท่ามาตรฐานหลังเปิด sql.Open() เพื่อให้แน่ใจว่าต่อสายติดแล้วให้สั่ง db.Ping() ดังนี้
db, err := sql.Open("driverName", "dataSourceName")
if err != nil {
    log.Fatal(err)
}
defer db.Close()
err = db.Ping()
if err != nil {
    log.Fatal(err)
}
โน๊ตว่าบรรทัด log.Fatal(err) เมื่อเจอปัญหาคุณสามารถเปลี่ยนให้มันทำอย่างอื่นที่ฉลาดกว่านี้ได้เสมอนะ อันนี้ใส่ไว้แบบง่ายๆเท่านั้น

Consequence

ผลบุญจากการใช้บ้าน connection pool ก็คือ คุณไม่จำเป็นจะต้องคอยเช็คหรือรับมือจัดการกับการเชื่อมต่อที่อาจล้มเหลวขึ้นมากลางคันอีกเลย ตัว database/sql จะจัดการให้เราเอง โดยหลังบ้านมันจะแอบพยายามส่งเชื่อมต่อ  10 ครั้งหลังพบว่าการเชื่อมต่อหลุด แล้วมันก็เพียงแค่จะดึงน้อง c2 จากคนอื่นมาส่งให้ หรือปั๊มน้อง c3 ใหม่ออกมา นั่นหมายถึงว่าโค้ดของคุณจะสะอาดปราศจากโรค…เอ้อ…โลจิกการตรวจสอบคอนเนคชั่นน่ารำคาญ

การตั้งค่า Connection pool

  • db.SetMaxOpenConns(n int)

สิ่งนี้จะเซ็ตจำนวน connection ที่ pool จะเปิดเชื่อมต่อกับฐานข้อมูล มันรวม connection ที่กำลังใช้อยู่และ connection ที่อยู่ว่างๆ ทิ้งไว้ไม่มีงานทำค้างใน pool ด้วย ถ้าคุณทำการเรียกใช้ connection จาก pool และพวกมันไม่มีตัวว่างเลย และจำนวนถึงขีดจำกัดพอดี ถึงตอนนี้การเรียกของคุณจะถูกบล๊อค เป็นได้เลยว่าจะนาน ดังนั้นกำหนดค่าเริ่มต้นดีฟอลท์คือ 0 ซึ่งหมายถึงไม่จำกัดทิ้งไว้ละกัน
  • db.SetMaxIdleConns(n int)

    กำหนดจำนวน  connection ที่สามารถเปิดทิ้งไว้ว่างๆ (idle) หลังจากถูกปลดปล่อย ค่าดีฟอลท์คือ 0 ซึ่งหมายถึงตัวการเชื่อมต่อจะไม่ถูกเก็บไว้ใน pool เลย นี่อาจนำไปสู่สถานการณ์ที่มี connection จำนวนมากถูกปิดๆ เปิดๆ บ่อยๆอย่างรวดเร็ว ซึ่งอาจไม่ใช้แบบที่คุณต้องการก็ได้
กุญแจสำคัญที่ควรสังเกตุเกี่ยวกับ connection pool คือมันขึ้นอยู่กับว่าคุณจิกใช้น้อง c อย่างไร และคุณตั้งค่าบ้าน pool ไว้อย่างไร? ซึ่งมันอาจทำให้เกิดพฤติกรรมไม่พึงประสงค์บางอย่างได้เช่น
  1. เปิด connection กับ database มากเกินไป นำไปสู่ error ได้มากมาย
  2. เกิดการ Block ในขณะที่รอการเชื่อมต่อ
  3. Operation สามารถล้มเหลวได้เมื่อ pool มีการเชื่อมต่อที่ล้มเหลวเกินกว่า 10 ครั้ง ซึ่งเป็นผลจากข้อจำกัดตั้งต้นที่ให้ลองต่อใหม่ไม่เกิน 10 ครั้ง
แต่ส่วนใหญ่ สิ่งที่มีอิทธิพลต่อพฤติกรรมจะเกิดจากว่าคุณจิกใช้ sql.DB อย่างไรมากกว่าว่าคุณตั้งค่า pool อย่างไรนะครับ

วันนี้ก็จบไปแล้วกับเรื่อง Connection Pool เป็นไงง่ายนิดเดียว 55555 วันนี้ลาไปก่อนนะครับ ฝันดีครับชาวบล็อกเกอร์ทุกคน ^^

ความคิดเห็น

โพสต์ยอดนิยมจากบล็อกนี้

Go ดีกว่า C++ หรือ Python อย่างไร ?

Go ไม่มี class และ object แต่ทำไมถึงมี Method และ Interface !?

ประวัติความเป็นมาของภาษา Go