อย่าล็อก Table กันได้มั้ยครับ?

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

ลูกค้าขององค์กรที่ผมทำงานอยู่มีมากมายครับ ในแต่ล่ะวันมีเข้ามาจำนวนราว 8,000 คนขึ้นไป ดังนั้นการออกใบเสร็จรับเงินจึงถือเป็นเรื่องสำคัญสุดขีดครับ เพราะออกใบเสร็จได้ก็แสดงว่าได้รับเงินเข้ากระเป๋าอย่างสมบูรณ์นั่นเอง และถ้าออกไม่ได้ แสดงว่าองค์กรจะสูญเสียเงินรายได้ถึงวันล่ะ 20 ล้านบาท!!!

ทีนี้เรื่องยุ่ง ๆ มันก็อยู่ตรงที่ผมได้รับมอบหมายให้มาตรวจสอบว่าทำไมออกใบเสร็จรับเงินล่าช้า แต่ผมต้องควานหาครับ ว่าต้นตอนั้นเกิดจากที่ใด แถมต้องรายงานความคืบหน้าด้วยว่าดำเนินการยังไงไปบ้างแล้ว

ประเด็นมันอยู่ที่ระบบ Billing ดังกล่าว มีทีมงานดูแลอยู่ก่อนแล้วและมันไม่ใช่ระบบที่ผมดูแลอยู่ โดยทีมงานดังกล่าวประกอบไปด้วยนักพัฒนาซอฟต์แวร์ 4 – 5 คนและนักวิเคราะห์ระบบ 1 คน

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

จากการประชุมทำให้ผมได้ทราบว่าปัญหาไม่น่าจะเกิดขึ้นทางระบบ hardware หรือระบบ network แต่น่าจะมาจากฐานข้อมูล เพราะทางทีม System Admin แจ้งว่า CPU Time ของ Database Server ขึ้นสูงถึง 100% ตลอดมาตั้งแต่เริ่มเกิดปัญหาแล้ว อีกทั้ง Virtual Memory ก็ใช้เกิน 70% อีกด้วย ถือได้ว่าวิกฤติมาก

ผมจึงตั้งประเด็นไปที่ data dictionary ของระบบ Billing ครับ ว่ามีการกำหนด index กันอย่างถูกต้องหรือเปล่า โดยขอให้ทีมงานชี้แจงให้ผมทราบครับ ว่าฐานข้อมูลที่ใช้มีรายละเอียดใด ๆ บ้างซึ่งได้รับคำตอบมาดังนี้ครับ

  • ฐานข้อมูลทำงานบน Microsoft Windows 2003 วิ่งอยู่บน Clustering
  • ฐานข้อมูลที่ใช้เป็น Microsoft SQL Server
  • ในฐานข้อมูลดังกล่าวมี Table ที่เกี่ยวกับระบบ Billing ราว ๆ 30 Table
  • มี Table ที่มีข้อมูลเกิน 10,000,000 เรคคอร์ดอยู่ราว 10 Table
  • มีการ join table กันตามหลักการของฐานข้อมูล (ยั้วเยี้ยไปหมด)

ตอนแรกผมตั้งประเด็นไปที่ index ครับ จึงขอให้ทางทีมงานแจ้งแก่ผมว่า table ใหญ่ ๆ ที่ใช้อยู่เป็นประจำทุกวันนั้น มีรายละเอียด index อย่างไรบ้าง

จากการวิเคราะห์ของผมพบว่า การกำหนด index เป็นไปอย่างถูกต้องและสมบูรณ์ตามหลักการเชื่อมโยง Table ครับ ไม่น่าจะทำให้เกิดการล่าช้าใด ๆ ผมจึงเปลี่ยนประเด็นไปที่โค้ดโปรแกรมของระบบ Billing ครับ จึงขอให้ทีมงานให้สิทธิ์ในการเข้าสู่ Source Code Server เพื่อให้ผมเข้าไป Check In ดู Source Code ได้

Source Code ของระบบ Billing มีขนาดใหญ่โตมากครับ ประกอบไปด้วยฟอร์มเป็นร้อยฟอร์ม และรายงานเป็นร้อย ๆ ตัว แถมมีระบบ Enhance หลาย ๆ อย่างด้วย

ผมพุ่งเป้าไปที่ Source Code ในส่วนของการพิมพ์ใบเสร็จรับเงินทันทีครับ และในที่สุดผมก็พบชุดคำสั่งราว ๆ 5 บรรทัดครับ ซึ่งเป็นตัวสร้างปัญหา มันเป็นงี้ครับ

Dim rs as new ADODB.recordset

rs.CursorLocation = adUseServer

rs.LockType = adOpenStatic

rs.CursorType = adLockOptimistic

set rs = con.execute(str)

ถึงผมจะรู้ Visual Basic 6.0 ไม่มากนั้น แต่ก็พอดูออกครับ ว่าโค้ดซึ่งกำหนดการล็อกฐานข้อมูลมันแม่ง ๆ ก็เลยค้นจาก msdn ดูก็อธิบายโค้ดดังกล่าวได้ดังนี้ครับ

  • ให้ใช้ CPU และ Virtual Memory แต่ที่ฝั่ง Database Server ทางฝั่ง Client ไม่รับรู้และไม่ช่วยอะไร Database Server ทั้งสิ้น
  • ทุกครั้งที่จะค้นข้อมูล ให้จองผืนหน่วยความจำขนาดใหญ่ ไว้ที่ Database Server เพื่อให้เวลาอ่านข้อมูล สามารถอ่านเรคคอร์ดเดินหน้าและอ่านเรคคอร์ดถอยหลังได้
  • ให้ล็อก Table เอาไว้ ไม่แบ่งให้ใคร ณ ตอนนั้น ถึงจะล็อกเพื่อแสดงผลอย่างเดียวก็ตาม โดยให้ล็อกเต็มรูปแบบ เพื่อเพิ่ม, ลบ และปรับปรุงข้อมูล

ผมจึงสอบถามทีมงาน Billing ครับ ว่าการออกใบเสร็จรับเงินเนี่ย ในระหว่างที่กำลังค้นข้อมูล มีการ update ข้อมูลไปด้วยหรือไม่? คำตอบคือไม่, แล้วมีการอ่านข้อมูลเดินหน้าถอยหลังหรือไม่? คำตอบคือไม่ อ่านเดินหน้าเพียงอย่างเดียวเท่านั้น และสุดท้ายจึงถามว่าสเป๊กเครื่องของ User ซึ่งกระจายอยู่ในองค์กรทั้งหมดราว 300 กว่าเครื่อง เป็นสเป๊กเครื่องระดับไหน ก็ได้รับคำตอบว่าเป็น CPU 1.2 GHz

เมื่อได้รับคำตอบอย่างนี้แล้ว ผมจึงเข้าใจทันทีว่า ทำไมระบบจึงล่าช้า, ทำไม CPU และ Virtual Memory บน Database Server ถึงถูกใช้จนเปอร์เซ็นต์ขึ้นสูง

ผมจึงสั่งการให้ทีมงานพัฒนาซอฟต์แวร์ของระบบ Billing ทุกคน แก้โค้ดในส่วนของการอ่านข้อมูลจากฐานข้อมูลที่ตนเองรับผิดชอบอยู่ทั้งหมดโดยด่วน โดยให้แก้เป็นดังนี้ครับ

Dim rs as new ADODB.recordset

rs.CursorLocation = adUseClient

rs.LockType = adForwardOnly

rs.CursorType = adLockReadOnly

set rs = con.execute(str)

ซึ่งสิ่งที่ผมให้ทีมงานพัฒนาซอฟต์แวร์ของระบบ Billing แก้ไข อธิบายความหมายได้ดังนี้ครับ

  • ให้ใช้ CPU และ Virtual Memory แต่ที่ฝั่ง Client ด้วย ไม่ใช่ให้ทุกอย่างไปโหลดอยู่ที่ Database Server แต่เพียงอย่างเดียว
  • ไม่ต้องจองผืนหน่วยความจำ เพราะการอ่านข้อมูลทำเพื่ออ่านเรคคอร์ดแบบเดินหน้าเท่านั้น ไม่ได้อ่านเรคคอร์ดแบบถอยหลังด้วย ดังนั้นไม่ต้องจองผืนหน่วยความจำที่ Database Server ให้เกินความจำเป็น
  • อย่าล็อก Table แบบเต็มรูปแบบ เพราะแค่อ่านอย่างเดียว ดังนั้น ล็อกแบบอ่านอย่างเดียวก็พอ Client อื่นจะได้เข้าใช้ Table เดียวกันได้ด้วย

ภายหลังแก้โค้ดโดยเลือกแก้ไขในฟอร์มและรายงานที่ถูกใช้บ่อย ๆ ซึ่งทุกคนใช้เวลาแก้ไขกันราว 10 นาที ผมก็ตรวจสอบการทำงานของระบบอีกครั้ง แล้วจึงอนุมัติให้ทีมงาน build ทั้ง project ขึ้นสู่ Application Server

จากนั้นจึงติดต่อให้ User ซึ่งประจำการอยู่กับ Client ทั้ง 300 กว่าเครื่อง ให้เริ่มลองใช้ซอฟต์แวร์ แล้วก็พบผลลัพท์ที่น่ายินดีครับว่า Client ทั้งหมดที่เคยเข้าถึงระบบอย่างล่าช้า แถมพิมพ์ใบเสร็จใบนึงต้องใช้เวลาถึง 2 นาที กลับทำงานได้อย่างรวดเร็ว โดยใช้เวลาเพียง 12 วินาทีเท่านั้นในการอ่านข้อมูล

เมื่อทราบวิธีการแก้ไขแล้ว ผมจึงสั่งการให้ทีมพัฒนาฯแก้ไขโค้ดส่วนที่เหลือทั้งหมด ให้สอดคล้องกับรูปแบบการแก้ปัญหาที่ได้เคยวางเอาไว้ครับ เพื่อให้มันทำงานได้เร็วกว่า 12 วินาที

จากปัญหานี้ทำให้ทราบอย่างนึงว่า ถ้าเราเขียนซอฟต์แวร์เพียงคนเดียว, มีผู้ใช้ซอฟต์แวร์ของเราเพียงคนเดียวและ ต่อเชื่อมเข้าฐานข้อมูลเพียง Client เดียว เราจะเขียนให้ล็อก Table กันยังไงก็ได้ครับ ไม่ต้องใส่ใจ แต่ถ้าเมื่อไหร่เราต้องเขียนซอฟต์แวร์ร่วมกับทีมงานอีกหลายคน , มีผู้ใช้ซอฟต์แวร์ของเราเป็นร้อย ๆ คนและต่อเชื่อมเข้าฐานข้อมูลโดย Client เป็นร้อย ๆ เครื่องล่ะก็

ได้โปรดพิจารณารายละเอียดการล็อก Table ให้จงหนักครับ เพราะนั่นเป็นบทบาทของนักพัฒนาซอฟต์แวร์ ไม่ใช่นักวิเคราะห์ระบบครับ

Related Posts

ใส่ความเห็น

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น ช่องข้อมูลจำเป็นถูกทำเครื่องหมาย *