เจาะลึก database noSQL redis

เจาะลึก database noSQL redis

วันนี้เราจะมาเจาะลึก database แบบ noSQL ตัวหนึ่ง นั่นก็คือ redis ครับ ก่อนหน้านี้ ผมเคย over view เอาไว้บ้างแล้ว อ่าน บทความ redis คราวนี้ ผมจะมาเจาะลึกเลย สำหรับคนที่กำลังพิจารณา ว่าจะเอามาใช้ดีหรือเปล่า ก็ลองอ่านดูครับ โดยเนื้อหา วันนี้ ผมจะเน้น ว่า ประโยชน์ของ redis คืออะไร เอามาทำงานอะไรได้บ้าง แล้ว feature แต่ละอย่างเอาไปประยุกต์กับงานที่ทำได้อย่างไร ซึ่งผมบอกได้เลย ว่ามันไม่ใช่แค่ database ธรรมดาๆ แน่นอนครับ

ผมจะเขียน feature และ ประโยชน์ของ redis อ้างอิงบนพื้นฐาน redis version 2.6.16 นะครับ ซึ่งเป็น version ล่าสุด ณ วันที่เขียนบทความนี้
redis

Redis คืออะไร

redis คือ database แบบ Key - Value ซึ่งเป็น database แบบ simple มากที่สุดแบบหนึ่ง หลักการทำงานของ Key - Value ก็คือ เอา key ไปค้น ว่าเก็บ value อะไรอยู่ ก็เอา value ออกมาใช้ เหมือนการที่เราสร้างตัวแปรไว้ในโปรแกรมนั่นล่ะครับ เพียงแต่ว่า มีความยืดหยุ่นต่างกัน และสามารถ store ค่าเอาไว้ได้ แม้ว่าเราจะปิดโปรแกรมไปแล้วก็ตาม แต่ว่า redis ยังให้คุณได้มากกว่า เพราะว่ามันมี data structure แบบพิเศษ หลายอย่าง ไม่ว่าจะเป็น string, hashes, lists, sets, sorted sets ซึ่งแต่ละแบบมันมีโครงสร้าง การทำงาน และ feature ที่ให้เราหยิบมาใช้ได้แตกต่างกัน

Atomic operation

งานที่ต้องการความเร็วสูงมากๆ ในการดำเนินการกับข้อมูลที่เก็บ โดยต้องมั่นใจว่าไม่มีใครมาเปลี่ยนแปลงค่าก่อน ที่การดำเนินการนี้จะแล้วเสร็จ redis มี feature ที่เรียกว่า atomic operation ซึ่งมันมีอยู่หลายอย่าง เช่น การเพิ่มค่าที่เก็บใน key, การต่อท้าย string, การเพิ่ม list, การหาค่า intersect, การหาค่า union, การหา different หรือแม้กระทั่ง หาค่า member ที่มี value ที่สูงที่สุด ซึ่ง atomic operation นี้ จะทำงานได้อย่างรวดเร็วที่สุด เท่าที่มันจะเป็นไปได้

in-memory dataset

ด้วยโครงสร้างการทำงานหลักของ redis นั้น จะทำงานบน ram เพื่อให้ได้ประสิทธิภาพ ที่ดีที่สุด แต่ว่า ตัว redis เองสามารถตั้งค่า ให้เก็บลง Harddisk ตามเงื่อนไขที่เรากำหนดได้ เพื่อความถาวรของข้อมูล โดยการกำหนดค่าจะมีทั้ง จำนวนข้อมูลที่เปลี่ยนแปลง หรือ เวลาที่ผ่านไป หรือทั้งสองอย่าง ซึ่ง redis แบบเดิมๆ หลังจากติดตั้งเสร็จ จะตั้งค่าแบบนี้เป็นมาตรฐานมาอยู่แล้ว ดังนั้นไม่ต้องกลัวว่า ไฟดับแล้วข้อมูลจะหายหมด เพราะเมื่อ start redis ขึ้นมาใหม่ มันก็จะอ่านข้อมูลจากที่เก็บลง harddisk ล่าสุดเอาไว้ขึ้นมาทำงานต่อได้เลย

master slave replication

redis สามารถทำ master slave replication ได้ง่าย เพียงแค่ตั้งค่าไม่กี่บรรทัดเท่านั้น และการ synce ข้อมูลกัน ก็ทำได้รวดเร็วแบบไม่มี blocking ด้วย(ไม่ต้องหยุดรอการ update ก่อนจะใช้งานได้ต่อ)

feature อื่นๆ ที่น่าสนใจ

redis เองยังมี feature อื่นๆที่น่าสนใจอีกมาก เช่น Transaction คือการสั่งหลายๆคำสั่งไปในรอบเดียว ให้มันทำงานหลายๆอย่าง แล้วคืนค่ามาทีเดียว เพื่อเพิ่มความเร็วให้กับโปรแกรมได้อีก หรือว่า การทำ Pub/Sub เป็นพื้นที่นึง สำหรับเก็บ flag การ publish โดย ทุกโปรแกรมที่มา subscribe พื้นที่นี้อยู่ ไม่ว่าใครก็ตามที่ publish ออกมาที่พื้นที่นี้ ทุกโปรแกรมก็จะได้รับ signal ตัวนี้ เพื่อไปทำงานต่อได้เลย หรือ ไม่ว่าจะเป็น Lua scripting คือการเขียนโปรแกรมด้วยภาษา Lua เพื่อส่งให้เข้าไปทำงานกับข้อมูลใน redis เพื่อความรวดเร็วแบบที่สุด ในการดำเนินการกับข้อมูลปริมาณมากๆ หรือ อีกอันก็คือ การตั้งเวลาให้หมดอายุ เราสามารถตั้งเวลาหมดอายุให้กับทุกค่าที่เราเก็บใน redis ได้ เมื่อถึงเวลา ก็จะหมดอายุหายไปเอง

เชื่อมต่อได้กับหลายภาษา

ตอนนี้ มี library สำหรับเชื่อมต่อ redis กับภาษาต่างๆมากมาย ไม่ว่าจะเป็น C, C#, C++, Clojure, Common Lisp, D, Dart, emacs lisp, Erlang, Fancy, Go, Haskell, haXe, Io, Java, Lua, Node.js, Objective-C, Perl, PHP, Pure Data, Python, Ruby, Scala, Scheme, Smalltalk, Tcl หรือแม้กระทั่ง libray ที่เป็นแบบ ORM ก็ยังมีอีกมากมายหลายสิบตัว หรือหากภาษาที่คุณใช้ ไม่มีใน list ก็สามารถอ่านคู่มื่อ เพื่อเขียนคำสั่งเชื่อมต่อ socket โดยตรงได้เลยเช่นกัน เพราะว่า เป็นการเชื่อม socket และรับส่งคำสั่งกันด้วย string ตามแบบปกติเลยนั่นเอง

ประเภทข้อมูลที่เก็บใน redis

ผมจะพยายามเทียบกับ MySQL เพื่อให้พอมองเห็นภาพบ้างนะครับ เพราะว่าเจ้า redis มันมีพฤติกรรมการเก็บข้อมูล การเรียกใช้ข้อมูลที่แตกต่างกันไปมากพอสมควร โดย redis เนี่ย เค้าออกแบบ "รูปแบบของข้อมูล" มาให้เราเลือกใช้ได้หลายแบบ แต่กับ MySQL แน่นอนว่าคือ String ซึ่งเป็น ASCII หรือ แบบ Binary เท่านั้น แต่ว่า MySQL จะไม่มีข้อมูลที่มีพฤติกรรมแปลก ผมยกตัวอย่างคือ redis มี List ซึ่งการเก็บข้อมูลมันจะเป็นเหมือนคนเข้าแถวยาวๆ เราสามารถ เพิ่มข้อมูลที่หัวแถว หรือ ท้ายแถว และกลับกัน คือการดึงข้อมูลที่หัวแถว หรือท้ายแถวออกได้ ซึ่งแบบนี้ MySQL ไม่มี หรือถ้ามี ก็คงต้อง sort กันวุ่นวายไปหมด ดังนั้น ไอเจ้า รูปแบบข้อมูล แบบต่างๆที่มีอยู่ใน redis จึงเป็นหน้าที่ของคนใช้ ที่จะต้องศึกษามันให้หมด เพื่อให้เอามาใช้ประโยชน์ในแต่ละอย่างได้อย่างเต็มที่ ผมเองศึกษา รูปแบบการเก็บข้อมูลของ redis มาพอสมควร และงานที่ผมเขียน ก็ได้ใช้มันเกือบครบทุกตัวเลยด้วย เดี๋ยวจะเล่าให้ฟัง ว่าแต่ละตัวมีประโยชน์อย่างไร

เก็บข้อมูล redis ประเภท String

ก็ไม่ได้มีอะไรประหลาดเลยครับ การทำงานกับ Key Value ส่วนใหญ่ เราก็ทำงานกัน string เป็นหลักๆอยู่แล้ว แต่ว่า string ก็มีนัย อยู่นะครับ เช่นถ้าเราเก็บแบบ Binary เราก็จะสามารถเก็บไฟล์ เช่นรูปภาพ Jpeg ได้เลย (เหมือนใน MySQL ครับ) แต่ต่างกันตรงที่ redis ไม่มี string แบบ ASCII เพราะ default เป็น Binary มาเลย ดังนั้น หน้าตาตอนที่เก็บเข้าไปเป็นอย่างไร ออกมาเหมือนเดิมไม่มีเพี้ยนแน่นอนครับ (mysql จะเจอยุคก่อน ที่ใส่ภาษาไทยไปแล้ว ออกมาเป็น ??? นั่นเพราะว่าเป็นเรื่องของ ASCII code ครับ) หรือว่า เพื่อเพิ่มความสะดวกกว่านั้นอีก เก็บเป็น JSON เลยก็ยังได้ครับ โดยเค้าจะ limit ขนาดเอาไว้ที่ 512 Mega bytes นะครับ โดยมี feature แถมให้ สำหรับ ข้อมูลประเภทนี้ก็คือใช้เป็นตัวนับได้ โดยมีการทำงานแบบ atomic oeration ในการ เพิ่มค่า (INCR), ลดค่า(DECR) , เพิ่มตามจำนวนที่ระบุ (INCRBY)

  • ต่อท้าย string ด้วย APPEND command - เอา String ออกมาบางช่วง เช่น ข้อความ This is a string เก็บใน mykey แล้วเราสั่ง GETRANGE mykey 0 3 ก็จะได้ตัวหนังสือ This ออกมา
  • ทำงานแบบ BIT ได้ เช่น SETBIT,GETBIT

เก็บข้อมูล redis ประเภท Lists

อันนี้ จะมีประโยชน์ ในการเก็บเป็น list รายการ ที่ลำดับเป็นส่วนสำคัญ โดยเราสามารถเอาไป sort ได้ หรือว่า จะเพิ่มเข้าไปตามตำแหน่งที่ระบุอย่างแน่ชัดได้ และยังมีการ push ที่หัว หรือ ท้าย รวมไปถึงการดึงข้อมูลออก จากหัว และท้ายด้วยเช่นกัน โดยให้มองว่าเป็นการเก็บข้อมูลแบบเป็นสายยาวๆ ที่ต่อกันอย่างมีลำดับนั่นล่ะครับ เพราะลำดับ ถือว่าเป็นสิ่งสำคัญ

ด้วยการที่ลำดับเป็นสิ่งสำคัญนี้เอง เราจึงเอามาประยุกต์ในงานที่เก็บข้อมูลแบบ เข้าคิวได้ ว่า ลำดับงานไหน มาก่อน มาหลัง แล้วเรายังสามารถ ตัดช่วงตรงกลาง หรือ ตามช่วงที่กำหนดก็ได้ โดยเนื้อที่เก็บข้างใน อาจจะเป็นหมายเลข ID หรือ คำสั่ง MySQL ที่รอเข้าคิวเพื่อจะเอาไป query ต่อไป เหล่านี้เป็นต้นครับ

สมมุติว่า ถ้าคุณทำงานกับ node.js แล้ว node.js ประมวลผลเสร็จแล้ว ได้ 1 ล้าน record แล้วต้องเอาเข้า MySQL ก็เอามาใส่ใน lists นี้เอาไว้ก่อน จากนั้น ก็ค่อยเขียน node.js อีกชุดหนึ่ง มาคอยอ่านแล้ว stream เข้า MySQL ต่อไป จนกว่าจะหมด list ส่วน node.js ตัวแรกที่ทำงานเสร็จไปก่อนแล้วก็จะได้ข้ามไปทำงานอื่นต่อเลย เป็นต้น หรือว่า อีกตัวอย่าง คือ การที่เราโหลด รายการทั้งหมด ขึ้นมา แล้วต้องประมวลผล ซึ่งมันใช้เวลานาน เราก็เอารายการเหล่านั้น ใส่ใน list เอาไว้ก่อน แล้วค่อยๆ ถึงไปประมวลผลทีละรายการจนกว่าจะหมด list แค่นี้เองครับ หรืออีกตัวอย่าง คือเอาไปใช้ เป็นรายการใน timeline ของ social network ก็ได้

โดย list มี limit อยู่ที่ไม่เกิน 2 ยกกำลัง 32 หรือประมาณ 4 พันล้านรายการ ต่อ 1 list นะครับ ดังนั้นโอกาสจะใช้เกิน คงยากอยู่นะ สำหรับการเข้าถึงข้อมูลด้านหัว หรือ ท้ายเนี่ย จะทำได้อย่างรวดเร็วมากๆ แต่ว่า การเข้าถึงตรงกลาง จะช้า โดยเฉพาะ list ที่มีขนาดใหญ่มากๆ โดยจะใช้ O(N) operation ครับ

เก็บข้อมูล redis ประเภท Sets

set จะเป็นกลุ่มของข้อมูล ถ้าให้นึกภาพง่ายๆ มันคือ กล่อง 1 ใบ ที่เราจะใส่ข้อมูลอะไรลงไปในนั้นก็ได้ เช่น 12, 2000, 30 ในกล่องใบเดียวกัน โดย sets ก็จะคล้าย list แต่ต่างกันเรื่องเดียวคือ เรื่องของ ลำดับ ที่ไม่มีความสำคัญ และจัดเรียงข้อมูลที่เก็บไม่ได้ ประโยชน์ของ set มันคือการเข้าถึงข้อมูลที่รวดเร็วครับ เพราะว่า มันเกิดมาเพื่อการเข้าถึงและค้นหาแบบเร็วสุดๆ เพราะ การเพิ่มข้อมูล, ลบข้อมูล , เช็คว่ามีข้อมูลหรือไม่ ใช้ O(1) opertion เท่านั้น และยังมีประโยชน์ในการเอามาทำ UNION, Intersec, หา diff , นับจำนวน, เช็คการมีอยู่ของข้อมูล เป็นต้น

การเก็บข้อมูลแบบ set จะไม่สามารถเก็บข้อมูลซ้ำกันได้ เช่น เก็บค่า 120 เข้าไปสองรอบ มันก็จะมี 120 เพียงตัวเดียว ดังนั้น เวลาที่เราจะเก็บข้อมูลอะไรเข้าไป เราจึงมั่นใจได้ว่า เราจะได้ข้อมูลที่ unique โดยที่ไม่ต้องเช็คการมีอยู่ก่อนเลย (บางงาน ต้องเช็คก่อน ว่าซ้ำหรือไม่ ถ้าไม่ซ้ำค่อยเก็บ แต่แบบ sets ไม่ต้อง โยนเข้าไปเลยเท่านั้นพอ) สำหรับจำนวนสมาชิกที่เก็บ ก็สูงถึง 4พันล้านกว่าตัว (2 ยกกำลัง 32 เช่นกัน)

โดยการเอามาประยุกต์ใช้ ก็เช่น การเก็บข้อมูล unique ip ของ visitor ที่เข้ามาใชงานเว็บ  หรือว่า การหาความสัมพันธ์ ระหว่างข้อมูลที่อยู่ใน set สอง set ว่ามี UNION, Intersect กันอย่างไร ตรงนี้ ทางคณิตศาตร์ เรื่อง set จะได้ประโยชน์มาก  แต่ถ้าเอามาใช้หน้าเว็บ ก็เอามาใช้หาความสัมพันธ์ของ TAG ที่เกี่ยวข้องกัน หรือ เรา อาจจะใช้ id ของ object โยนเข้าไปใน set จากนั้นเอา มาใช้เป็น index ก็ได้ เช่น งานหนึ่ง ต้องประกอบไปด้วย ส่วนย่อย 10 ชิ้น โดยผมก็เอาส่วนย่อยทั้งสิบชิ้น ใส่เข้าไปใน set แล้วมาอ่านทีหลังว่า ใน 10 ชิ้นนั้นมันคืออะไรบ้าง โดยเอา id ที่ได้ไปค้นรายละเอียดต่อไปอีกรอบนั่นเอง

โดยส่วนใหญ่ของ sets มันถูกประยุกต์ใช้ทำหน้าที่เหมือน index ของอะไรบางอย่าง เพราะว่าเวลาที่เราเก็บข้อมูลแบบ string เราก็จะแยก keys กันไปเรื่อย โดยเมื่อนานไป เราจะไม่รู้เลย ว่าเรามี key อะไรบ้างแล้ว ดังนั้น หากทุกครั้งที่เราสร้าง key ใหม่เราเอาชื่อ keys มาเก็บใน sets ก่อน แล้วเราก็อ่านจาก sets ดูว่ามี keys อะไรบ้าง ก็สะดวกกว่ามากครับ

เก็บข้อมูล redis ประเภท Hashes

เป็นแบบที่ถูกใช้งานมากที่สุด และ ใช้งานสะดวกที่สุด และเค้าแนะนำให้ใช้งานมากที่สุด อันเนื่องมาจากมันเก็บข้อมูลได้หลากหลายนั่นเอง การเก็บข้อมูลแบบ hashes ถ้ามองเผินๆ มันก็คล้ายกับแต่ละ row ของ MySQL นี่แหล่ะครับ เพราะว่า แต่ละ Keys เก็บค่าได้หลายอย่าง หลายแบบ เช่น เราสร้าง key user:data:26 แล้วเก็บ ชื่อ, นามสกุล, อายุ,เพศ เป็นต้น ที่เค้าบอกว่า แนะนำให้ใช้แบบนี้ เพราะว่ามันทำงานได้เร็ว รวมทั้งมันยังมีการบีบอัดข้อมูลทำให้ประหยัดเนื้อที่การเก็บ และลดการกระจายตัวของ redis keys ด้วยครับ เพราะแทนที่เราจะแยกเก็บ ชื่อ นามสกุล อายุ เพศ ของแต่ละคน ก็เก็บรวมทั้งหมดเข้าไว้ใน keys เดียวกันเลย สำหรับการเรียกข้อมูลกลับขึ้นมา ก็ทำได้ง่าย ไม่ว่าจะเรียกครั้งเดียว ออกมาทุกค่า หรือ เลือกที่จะเรียกบางค่าออกมาก็ได้ครับ ผมก็ใช้อยู่รู้สึกสะดวกมากทีเดียว

เก็บข้อมูล redis ประเภท Sorted sets

แบบนี้ จะเหมือนกับ sets เลย ก็คือ เก็บสมาชิกต่างๆเข้ามา แต่ว่า ความแตกต่างของมันก็คือ มีคำสั่งมาให้ดำเนินการสำหรับ ค่าที่เก็บได้ด้วย ในลักษณะ score ranking ก็คือ เราสามารถใช้ sorted sets ในการเก็บคะแนน ที่ค่าที่เก็บมีผลต่อลำดับ เพื่อใช้ในการเรียงคะแนน ลำดับ และยังสามารถเก็บคะแนนที่ซ้ำกันได้อีกด้วย (sets ปกติ จะเก็บค่าที่ซ้ำไม่ได้)

โดยจุดเด่นๆ ก็คือ ลูกเล่น ที่ให้เราใช้งานการ sort นี่แหล่ะครับ ที่มันจะตอบสนองเราด้วยความรวดเร็วมากๆ เพราะว่าถ้าเราเลือกใช้แบบ SQL ปกติ ถึงแม้ว่าจะมี index แต่มันก็ต้อง scan ทั้งหมดก่อนแล้วจึงค่อยมาจัดเรียงอีกรอบนึง แต่แบบ sorted sets นั้น มันมี algorithm ที่ทำงานได้อย่างรวดเร็วมากๆ รวมถึง feature การเลือกช่วงคะแนน เป็นช่วงที่เราต้องการ หรือว่า เอามาประยุกต์เพื่อใช้เก็บเป็น index ของ keys ของ redis ในส่วนงานอื่นก็ได้เช่นกัน เช่น เราสร้าง key แบบ sorted sets ขึ้นมา ตั้งชื่อว่า main:key:index แล้วเราก็ใส่ member เข้าไปว่า 1 > user:data:26 , 2 > user:data:27 เป็นต้น (อย่าลืมว่า ถ้าทำแบบนี้ คือเราจะเอา index มาใช้ประโยชน์ในการ sort ต่อทีหลัง)

การใช้งาน redis ควรมี index ทำเองไว้ด้วย

อย่างที่กล่าวไป ตั้งแต่ต้นครับ ว่า redis มันคือ keys value นั่นคือ เราสามารถสร้าง key ชื่ออะไรก็ได้ เช่น group:subgroup:id หรือ abc ก็ยังได้ แต่ปัญหาก็คือ ถ้าเราสร้าง key ไปแล้ว มันก็จะมีค่าอยู่อย่างนั้น ตลอดไป โดยที่เราอาจจะลืมมันไปแล้ว ว่าเคยมีชื่ออยู่ ทีนี้ปัญหาจะเกิดก็ต่อเมื่อเราใช้งานหลาย Application บน redis database เดียวกัน แล้วถ้าเกิดไปตั้งชื่อซ้ำกันล่ะก็ เกิด bug ของงานอย่างแน่นอนเลย หรือว่า บาง key ที่เราตั้งขึ้นมาใช้ชั่วคราว แต่ลืมลบมันออก มันก็จะติดเป็นขยะอยู่อย่างนั้นตลอดไปครับ ดังนั้น สิ่งที่ควรทำก็คือ การสร้างคู่มือ เอาไว้อันนึง แล้ว บนทึกทุกครั้งที่มีการสร้าง keys ใหม่ ว่า ชื่อ keys อะไร แล้วทำหน้าที่อะไร เป็นต้น อาจจะไม่ต้องยาวมาก สั้นๆ บรรทัดเดียว อ่านแล้วเข้าใจได้ทันที ประมาณนั้นครับ เท่านั้นก็พอแล้ว คู่มือนี้ ยังมีประโยชน์มาก ในการเอามาอ้างอิงตอนเขียนโปรแกรมครับ เพราะว่าเราจะได้ไม่ต้องรื้อ source code ดู แต่ดูจากคู่มือ เพื่อเข้าไปดำเนินการดึงค่าออกมาได้ทันที

Create: Modify : 2013-10-17 20:49:57 Read : 6122 URL :