GIN 101-S: คุยกับ MongoDB และ Redis บน Golang

Piirapol Pajharawat
InsightEra
Published in
4 min readMar 9, 2020
Photo by Kolar.io on Unsplash

หลังจากที่เราได้สร้าง web service กันไปใน part ที่แล้ว สิ่งสำคัญอีกอย่างหนึ่งที่ web service เกือบทั้งหมดจะต้องมีนั่นก็คือ database นั่นเอง ซึ่งในบทความนี้ ผมจะพูดถึงการใช้ Golang คุยกับ database 2 ชนิดได้แก่ MongoDB และ Redis ครับ แต่ก่อนอื่น เรามาทำความรู้จักกับ database ทั้งสองชนิดนี้กันครับ

MongoDB

https://www.mongodb.com/

MongoDB คือ non-relational database ที่ใช้ระบบการเก็บข้อมูลแบบ document-based ซึ่งจะเก็บข้อมูลในรูปแบบคล้าย JSON document โดยทางผู้สร้างเชื่อว่าการเก็บข้อมูลในลักษณะนี้ เป็นการเก็บข้อมูลได้หลากหลายและเป็นธรรมชาติมากที่สุด (เนื่องจากระบบเก่า หรือ SQL จะเก็บข้อมูลโดยแบ่งเป็น row และ column) ซึ่งทางผู้สร้างก็ทำการ claim ว่า ไม่มี database ไหนที่จะสามารถทำให้งานของคุณ productive มากไปกว่านี้ได้อีกแล้ว

ตัวอย่างการเก็บข้อมูลแบบ document บน database ของ MongoDB https://www.mongodb.com/blog/post/getting-started-with-python-and-mongodb

Redis

https://en.wikipedia.org/wiki/Redis

Redis คือ open source database ที่เก็บข้อมูลแบบ in-memory data structure store โดยเก็บข้อมูลในรูปแบบ key-value เนื่องจากเป็นการเก็บข้อมูลใน memory (หรือก็คือ RAM) ทำให้เวลาที่ใช้ในการประมวลผล (query) น้อยมาก แต่ด้วยการเก็บข้อมูลที่มีเพียงแค่ key กับ value ประกอบกับการเก็บข้อมูลไว้บน RAM จึงทำให้ redis ไม่เหมาะกับการเก็บข้อมูลระยะยาวและมีจำนวนมากเหมือน MongoDB

โดยส่วนมาก redis จะถูกใช้เพื่อทำ caching หรือเป็น temporary database เพื่อรองรับ concurrent connection ในปริมาณมาก และ เพิ่มความรวดเร็วในการประมวลผลของระบบเท่านั้น (แก้ปัญหาคอขวดใน database หลักเพราะ query ไม่ทัน)

ตัวอย่าง data types ที่สามารถเก็บบน redis ได้ (นี่เป็นแค่ส่วนหนึ่งเท่านั้น ยังมี type ที่เก็บได้มากกว่านี้) https://dzone.com/articles/counting-distinct-users-in-real-time-with-redis-us

Prerequisite

มาเริ่มกันเลยดีกว่า! (บทความนี้ไม่สอนลง Mongo กับ Redis นะ) ในบทความนี้ผมจะใช้ docker ในการ run mongo กับ redis service (เพราะเค้ามี image ที่ build มาเสร็จเรียบร้อยแล้ว) ถ้าหากใครที่ยังไม่รู้วิธีใช้ docker แนะนำว่าให้อ่านบทความนี้ก่อนครับ (หรือถ้ามี mongo กับ redis อยู่แล้วก็ใช้อันนั้นได้นะ)

ถ้ามี docker แล้ว เรามา run docker image กันเลย!

$ docker run -d -p 27017:27017 mongo$ docker run -d -p 6379:6379 redis

คำสั่ง docker run จะ pull image ให้อัตโนมัติหากเราไม่มี image นั้นอยู่ หลังจาก run แล้ว เรามาลองเช็คดูว่า image ของเรา run แล้วจริงหรือเปล่าด้วยคำสั่ง

$ docker ps

หากได้ผลลัพธ์ตามนี้แสดงว่า image ของเรา run ได้อย่างถูกต้อง

Installation

เราจะใช้ 2 repo หลักๆ ในการเข้าไปเป็นมะริ่งกิ่งก่อง สะระน๊องก่องแก่งกับ database นั่นก็คือ mgo และ go-redis ครับ (ในส่วนของ redis ใน repo เค้าให้ลง v7 ด้วย go mod แต่จากประสบการณ์ของผม ใช้ตัวธรรมดาจะง่ายต่อการลงมากกว่าครับ)

$ go get -u github.com/globalsign/mgo$ go get -u github.com/go-redis/redis

import เพื่อใช้งาน

$ import "github.com/globalsign/mgo"$ import "github.com/go-redis/redis"

Connect with MongoDB

สำหรับ mongo เราจะใช้ library อีก 1 ตัวเพื่อช่วยในการ query นั่นคือ bson ครับ

$ go get -u github.com/globalsign/mgo/bson$ import "github.com/globalsign/mgo/bson"

ทีนี้เราก็พร้อมคุยกับ database แล้ว! อันดับแรกเรามาลองเชื่อมต่อกับ database ดูกันครับ เริ่มจาก mongo ก่อน

จากในตัวอย่าง เราจะทำการเชื่อมต่อ mongo ด้วยคำสั่ง mgo.Dial โดยใส่ argument เป็น localhost:27017 โดย function นี้จะ return session กลับมา ซึ่งเราจะใช้ตัวนี้ในการทำงานทั้งหมด หลังจากได้ session แล้ว เราก็ทำการเลือก database และ collection ที่เราจะใช้ ด้วยคำสั่ง

mongoConnection := session.DB("dummy").C("testCollection")

โดย DB จะเป็นการเลือก database และ C จะเป็นการเลือก collection การเลือก database และ collection นั้น ไม่จำเป็นว่าจะต้องมี database และ collection อยู่ก่อนแล้ว เราสามารถตั้งชื่อใหม่ให้มันเลยได้

เนื่องจาก mongo เก็บข้อมูลในรูปแบบ BSON เราจึงต้องเตรียม data โดยตั้งชื่อให้มันใน BSON format ด้วยครับ

type test struct {  
Name string `bson:"name"`
Number int `bson:"number"`
}

หลังจากเลือกแล้ว เราก็มาลอง insert document ด้วยคำสั่ง

mongoConnection.Insert(t)

ซึ่งคำสั่งนี้ก็จะทำการยัดข้อมูลที่เราเตรียมไว้เข้าไปใน collection ที่เลือกไว้ทันที ถ้าหากเราเลือก database และ collection ที่ยังไม่เคยมีมาก่อน มันจะทำการสร้าง database และ collection ให้โดยอัตโนมัติครับ ซึ่งหากทำทุกอย่างถูกต้อง data ที่เข้าไปอยู่ใน mongo จะมีหน้าตาแบบนี้ครับ (อันนี้เข้าไปดูด้วย MongoDB Compass)

หลังจาก insert ได้แล้ว เรามาลอง query กันดูเลยครับ

ในการ query เราจะใช้คำสั่ง Find ตามด้วย query ในรูปแบบ bson ครับ (ก็คือเขียนเหมือน json แต่เอา bson.M มาครอบข้างหน้า) จากนั้นเราใช้ .One เพื่อเป็นการบอกว่า เราต้องการ query แค่ document เดียว แล้วทำการ assign เข้าไปในตัวแปร result โดยจะได้ผลลัพธ์หน้าตาแบบนี้ (ผมใช้ spew ในการ print เพราะต้องการดูรายละเอียดของ object ครับ)

เรายังสามารถเลือกที่จะ assign ข้อมูลที่ query มาใส่ตัวแปรที่กำหนด structure ไว้แล้วได้ด้วย (ยังจำ type test ข้างบนได้ใช่มั้ย ;w;)

var formatResult test
err = mongoConnection.Find(bson.M{"name":"test"}).One(&formatResult)
spew.Dump(formatResult)

ซึ่ง function นี้ก็จะทำการ map ข้อมูลที่ได้มากับ field ที่มีอยู่ใน structure ให้ครับ

ถ้าเราพยายาม query สิ่งที่ไม่มีอยู่ใน collection ตัว function ก็จะ return กลับมาว่า not found อยู่ใน error ครับ

อย่าลืมปิด session หลังใช้เสร็จด้วยนะครับ

session.Close()

Connect with Redis

สำหรับ redis ไม่ต้องลง library เพิ่มครับ เรามาลองเชื่อมต่อกับ redis กันเลย

ถ้าเชื่อมต่อสำเร็จ สิ่งที่โปรแกรม print ออกมาจะเป็นคำว่า PONG และ <nil> ครับ โดยเราจะเชื่อมต่อไปที่ database 0 ซึ่งเป็น default database (database ของ redis จะเป็น running number เริ่มที่ 0 ครับ)

เนื่องจาก redis เก็บข้อมูลเป็น key และ value การเตรียมข้อมูลเพื่อใส่เข้าไปใน redis จะค่อนข้างแตกต่างกับ mongo พอสมควรครับ

โดยคำสั่งที่ใช้ยัดข้อมูลเข้าไปใน redis จะใช้ function Set() ครับ ซึ่ง function นี้รับ 3 argument นั่นก็คือ key , value , และ expiration ซึ่งถ้าตั้งเป็น 0 หมายถึงไม่มีวันหมดอายุนั่นเองครับ (แต่จริงๆ แล้วไม่ดีนะ เพราะถ้าเราใส่ data เข้าไปเรื่อยๆ โดยที่ไม่มีตัวไหนหมดอายุไปเลย สุดท้ายแล้ว memory มันก็จะเต็ม)

ในตัวอย่างนี้ผม set ข้อมูลเข้าไป 2 ชุดนั่นก็คือ

  • key name มี value test
  • key number มี value 200

ส่วนการ query ข้อมูล เราจะทำโดยใช้ function Get() แล้วตามด้วย Result()

ถ้าการ query สำเร็จ result ที่ได้จะเป็นค่า value ของ key นั้นๆ แต่หากเรา query key ที่ไม่มีอยู่ redis จะ return ค่า redis: nil กลับมาครับ

เสร็จแล้วก็อย่าลืมปิด connection เหมือนเดิมครับ

err := redisClient.Close()

Conclusion

มาถึงตรงนี้ คิดว่าทุกคนน่าจะพอเข้าใจ concept ในการคุยกับ database บน Golang พอสมควรนะครับ (รึเปล่าหว่า) จริงๆ แล้วบทความนี้ไม่ได้มีการพูดถึง Gin แต่เป็นการเกริ่นเรื่องก่อนที่จะเอาไปใช้ด้วยกันใน part ถัดๆไป (จริงๆ คือตอนแรกกะจะรวมอยู่ในบทความเดียวแต่ไม่อยากให้ part นึงมันยาวเกินไป เลยแยกอันนี้ออกมาเป็นเหมือน side story ครับ) หวังว่าบทความนี้จะเป็นประโยชน์ได้บ้าง และทุกคนจะไม่ประท้วงผมตามข้อบังคับที่ ๖๙ นะครับ 😂

References

--

--