-->

วันเสาร์ที่ 25 กุมภาพันธ์ พ.ศ. 2555

Data Warehouse Strategy


ในกรณีที่ได้ทำทุกวิถีทางตามที่ได้กล่าวมาข้างต้นหมดแล้ว แต่ยังไม่สามารถแก้ไขปัญหาหรือเพิ่มประสิทธิภาพของฐานข้อมูลได้ อาจจำเป็นต้องพิจารณาการปรับเปลี่ยนรูปแบบฐานข้อมูลให้เป็นแบบ Data Warehouse ซึ่งเป็นการนำเอาข้อดีของฐานข้อมูลแต่ละแบบมาผสมกัน 


  
 ตัวอย่างภาพรวมของ Data Warehouse


Hardware Upgrade Strategy


คือการขยาย Server เช่น  ถ้าใช้ server 1 เครื่องแล้วยังช้า ก็ต้องขยายเป็น 2 Servers, 4 Server, 8 Servers ไปเรื่อยๆ เพื่อเป็นการแยกกันทำงาน เสมือนการแบ่งเบาภาระซึ่งกันและกัน ไม่ใช่เครื่องใดเครื่องหนึ่งทำงานหนักจนเกินไปจนเกิดปัญหาด้านความล่าช้าของข้อมูล


Reduce Locking and Blocking


       ในกรณีที่ตัวเซิฟเวอร์ของฐานข้อมูลนั้นมีผู้ใช้หลายคน ย่อมเกิดการแย่งกันใช้งานข้อมูลภายในฐานข้อมูลอย่างแน่นอนหรืออาจมีการต้องรอ(Queue) ซึ่งตามปกติแล้วเมื่อมี User เข้ามาใช้ข้อมูลชุดเดียวกันพร้อมๆ กัน, User คนแรกจะทำการ lock ข้อมูล เพื่อป้องกันไม่ให้ user คนที่เข้ามาทีหลังมาแย่งใช้ข้อมูลชุดเดียวกันอยู่แล้ว ดังนั้น user ที่เข้ามาทีหลังจะต้องเกิดการรอ จนกว่า user คนแรกจะปิดการใช้งาน data ออกไปเอง แต่เราก็สามารถเลือกที่จะไม่รอก็ได้ ซึ่งมีอยู่หลายวิธีที่จะนำมาใช้แก้ปัญหาตรงนี้ เช่น การทำ Server Farm, Replication, Data Mirror หรือแม้กระทั่งการทำ Transaction Isolation Level เป็นต้น

Index Strategy


เป็นวิธีที่นิยมที่สุดเพราะมีหลักการที่ง่าย และได้ผลเป็นอย่างมากในการลดระยะเวลาในการ Query ข้อมูล โดยที่ไม่ต้องไปยุ่งเกี่ยวกับ Querey Syntax เลย หลักการของ Index คือการทำให้ Query ตัวเดิมสามารถที่จะ Scan disk น้อยลงเพราะว่าโดยปกติแล้ว เวลาที่ SQL Server ทำงานหรือค้นหาข้อมูลเพื่อให้ได้ผลลัพธ์ตาม Query Syntax นั้น ทาง SQL Server จะใช้ทรัพยากรทั้งหมด (Resources) ไม่ว่าจะเป็น CPU, Disk, Memory, etc. แล้วจุดที่ถือว่าเป็นตัวร้ายที่สุด ก็คือ disk และ Bottle neck ที่ใหญ่ที่สุดเลยคือ Disk ถ้าสามารถหาวิธีลดการ Scan Disk ได้ ย่อมลดปัญหาเรื่อง Bottle neck และทำให้ฐานข้อมูลทำงานเร็วขึ้นอย่างเห็นได้ชัด 

ใน SQL server ก็ทำการแบ่งหน้า (Page) ไว้เก็บข้อมูลเหมือนกัน โดย 1 Page จะมีขนาดเท่ากับ 8KB เสมอ และไม่สามารถแก้ไขได้ แต่ใน Oracle จะสามารถกำหนดขนาดของ Page ได้ ทีนี้ใน 1 page จะเก็บข้อมูลได้กี่ Record ก็ขึ้นอยู่กับความยาวของ Record ตัวอย่างเช่น ถ้า 1 record มีความยาวเท่ากับ 1 KB แสดงว่าใน 1 page สามารถเก็บข้อมูลได้ 8 Records


ตารางข้อมูล (table) ใน MySQL นั้นสามารถเก็บข้อมูลที่มีขนาดใหญ่มากได้ ซึ่งจะทำให้เราดึงข้อมูลได้ช้าลง ในการที่จะทำให้การ query ของเรามีประสิทธิภาพนั้น จะต้องทำการกำหนด  Indexให้กับ table ซึ่งการทำ Index ใน filed นั้น ๆ จะช่วยให้สามารถค้นหาข้อมูลใน filed นั้น ๆ (ที่ถูกกำหนดเป็น Index) ได้อย่างรวดเร็วขึ้น ซึ่ง Index ในMySQL ประกอบไปด้วยชนิดต่าง ๆ ดังนี้
  • NONUNIQUE  Index เป็น Index ที่สามารถมีค่าใน field นั้น ๆ ซ้ำกันหลาย ๆ แถวได้
  • UNIQUE index เป็นการกำหนดว่าค่าของ field  นั้น ๆ จะต้องไม่ซ้ำกันเลยในทุก ๆ เรคอร์ด
  • Primary Key มีลักษณะเช่นเดียวกับ UNIQUE แต่เพิ่มข้อจำกัดบางอย่างเข้าไป (ไม่สามารถเป็นค่าnull ได้
  • Fulltext index เป็นการออกแบบสำหรับการค้นหาข้อความ

 ซึ่งในเรื่องของการสร้างและลบ Indexนี้จะอธิบายถึงเรื่องต่าง ๆ ดังนี้
  • การกำหนด index ขณะสร้าง table ด้วยคำสั่ง CREATE TABLE
  • การใช้ PRIMARY KEY
  • การเพิ่ม Indexใหม่เข้าไปในตารางเดิม ด้วยคำสั่ง ALTER TABLE และ CREATE INDEX
  • การลบ Index จากตาราง ด้วยคำสั่ง ALTER TABLE หรือ DROP INDEX

เทคนิคการเลือก Index อย่างไรให้ดี

การเลือก Index มีสูตรตามนี้  :  DISTINCT_KEYS / NUM_ROWS
โดย column ที่เราเลือกควรจะเป็นค่าที่ไม่ซ้ำและไม่เป็นค่า NULL
เราควรจะกำหนด Index ให้กับ file ที่
  • ถูกนำไปใช้ในการกำหนดเงื่อนไขในการ query ด้วยคำสั่ง where บ่อย ๆ
  • ถูกนำไปใช้ในการเรียงข้อมูลในการ query ด้วยคำสั่ง order by บ่อย ๆ
  • มีค่าที่แตกต่างกันเป็นส่วนมาก (field ที่มีค่าเหมือน ๆ กัน หรือซ้ำกันมาก ๆ ไม่ควรจะนำมากำหนด Index
  • ถูกนำไปใช้ในการ JOIN บ่อย ๆ

ตัวอย่างในการเลือก index ที่ดี

       ถ้า Table ของเรามีข้อมูล 100,000 record และใน column ที่เราต้องการจะนำมาทำ index มี จำนวน 88,000 record ที่มีค่าไม่ซ้ำกัน
       หลังจากนั้นเราเอามาเข้าสูตร  88,000 / 100,000 = 0.88
       โดยถ้าผลลัพธ์เข้าใกล้ 1 เท่าไรก็เข้าข่ายที่เป็น index ที่ดีได้

ตัวอย่างในการเลือก index ที่ไม่ดี

       ถ้า Table ของเรามีข้อมูล 100,000 record และใน column ที่เราต้องการจะนำมาทำ index มี จำนวน 500 record ที่มีค่าไม่ซ้ำกัน
       หลังจากนั้นเราเอามาเข้าสูตร  500 / 100,000 = 0.005
       โดยถ้าผลลัพธ์ออกมาแบบนี้ เราอ่านแบบ full table จะมีประสิทธิภาพมากกว่าการใช้ index เพราะข้อมูลที่เราต้องการนั้นเป็นส่วนมากใน Table

ตัวอย่างการทำ Index

         ข้อมูลที่จะทำการทดสอบต่อไปนี้จะใช้ฐานข้อมูลที่ชื่อว่า employees และใช้ตารางที่ชื่อ employees ซึ่งมีจำนวนข้อมูลภายในตารางทั้งหมด 300,024 แถว



รูปที่ 7 แสดงการ Select ข้อมูลทั้งหมดในตาราง employees

เนื่องจากการทำ index คือการนำ Column ที่มีการใช้งานบ่อยมาทำ ดังนั้นตัวอย่างนี้ได้ทดลองเลือก first_name
มาทดสอบ Index โดยมีขั้นตอนดังนี้


              1.ทดสอบ Query เพื่อดูผลลัพธ์ก่อนการทำ Index




รูปที่ 8 แสดงผลลัพธ์ก่อนทำ Index



         2.ทำการกำหนด Index ให้กับ first_name 



รูปที่ 9 คำสั่ง SQL สำหรับการใส่ Index ให้ first_name



         3.ทดสอบ Query ตามขั้นตอนแรกอีกครั้ง เพื่อดูผลลัพธ์หลังจากการทำ Index



รูปที่ 10 แสดงผลลัพธ์หลังทำ Index
                
         เมื่อเปรียบเทียบก่อนและหลังทำ Index จะพบว่า สามารถช่วยลดระยะเวลาในการ Query ข้อมูลลงไปได้หลายเท่าตัวเลยทีเดียว


Database Server Configuration


เป็นการกำหนดค่าพื้นฐานต่างๆของตัวฐานข้อมูลตรงๆเลย เช่น การปรับแต่งเรื่องหน่วยความจำของฐานข้อมูล เป็นต้น 

ตัวอย่างตาราง

SQL Server
Configuration Settings
Advanced
Setting?
Requires
Restart?
Default Value
Current Value
affinity mask
Yes
Yes
0

awe enabled
Yes
Yes
0

cost threshold for parallelism
Yes
No
5

cursor threshold
Yes
No
-1

fill factor (%)
Yes
Yes
0

index create memory (KB)
Yes
No
0

lightweight pooling
Yes
Yes
0

locks
Yes
Yes
0

max degree of parallelism
Yes
No
0

max server memory (MB)
Yes
No
2147483647

max text repl size (B)
No
No
65536

max worker threads
Yes
Yes
255

min memory per query (KB)
Yes
No
1024

min server memory (MB)
Yes
No
0

nested triggers
No
No
1

network packet size (B)
Yes
No
4096

open objects
Yes
Yes
0

priority boost
Yes
Yes
0

query governor cost limit
Yes
No
0

query wait (s)
Yes
No
-1

recovery interval (m)
Yes
No
0

scan for startup procs
Yes
No
0

set working set size
Yes
Yes
0

user connections





ตัวอย่างเช่น เรื่อง Fill Factor (รายการที่ 5) เป็นกำหนดค่าเพื่อแก้ปัญหาเกี่ยวกับการใช้ Index คือเมื่อเราเพิ่ม index เข้าไปมากๆ จะมีผลทำให้การ Select เร็วขึ้นก็จริง แต่จะมีผลข้างเคียงคือทำให้ การ Insert ข้อมูลช้าลง การกำหนดค่า Fill Factor ที่เหมาะสมจะช่วยทำให้ การ Insert เร็วขึ้นนั่นเอง

เทคนิคอื่นๆในการทำ Performance Tuning


1) ใช้ Normalization เพื่อ Optimize Transactional Process
การทำ Normalization นั้น จะช่วยในมุมการใช้คำสั่ง Insert, Update, Delete  ทำให้เกิด Performance ที่ดี เพราะจะช่วยขจัดปัญหา เรื่องการซ้ำซ้อนของข้อมูล และลดโอกาสที่จะเกิดการผิดพลาดในการประมวลผลข้อมูลในตาราง แต่จะมีผลตรงกันข้ามกับคำสั่ง Select เพราะว่าใน Database หลายๆ ตัวที่อาจจะพบว่า Query เร็ว แต่ Insert ช้าก็ได้


 

2) แยกไฟล์ MDF และ LDF ไว้กันคนละ physical disk

โดยคำสั่ง Insert, Update, Delete จะกระทำกับไฟล์ LDF เป็นหลัก ทั้งนี้ในงาน multi-user ขนาดใหญ่แล้วจะเกิด load read-write อยู่บน physical disk ก้อนเดียวกัน ถ้าเป็นไปได้ก็ควรจะแยกไฟล์ MDF และ LDF ไว้คนละ physical disk ก็จะทำให้เกิดการแบ่ง load read-write ไปไว้คนละ physical disk 

3) แยก tempdb ไว้บน harddisk ก้อนใหม่

หลักการจะเหมือนกับการแยกไฟล์ MDF และ LDF ครับ คือการแยก load แต่ tempdb จะเป็น temp สำหรับ database ทุกก้อนที่อยู่ใน Server  tempdb จะรับงานหนักมาก ถ้าเป็นไปได้ก็ควรแยก tempdb ไว้คนบน physical disk ต่างหากได้จะดีมาก

 

4) สำหรับ table ขนาดใหญ่ให้ใช้ Table partition

                สาเหตุหลักของการทำ partitioning มากจาก tables ที่มีขนาดใหญ่ (very large tables and indexes) โดยทำการ แยกส่วนออกมาเป็นส่วนย่อยๆ เรียกว่า partitions     มองขนาดจากใหญ่ไปเล็กจะเป็นดังนี้  Tables->Partitions->Rowsโดยสามารถแบ่ง ข้อมูลออกเป็น range ใช้ข้อมูลใน data เช่น ปี เป็นต้น
ตัวอย่าง
table Customer มีพนักงานปี 2008-2010  จาก Table Customer จะประกอบด้วย Partition คือ customer_2008, customer_2009, customer_2010 เป็นต้น โดยสามารถ Query โดยระบุว่าจะใช้  Patitioning ไหนเพื่อเพิ่มความเร็วในการ Query หรือ ระบุว่าใช้ Table customer เลยก็ได้ การทำ table partition เป็นการแยก load ทาง horizontal โดยจะทำได้ตั้งแต่ sql server 2005 ขึ้นไป เป็นวิธีการกระจาย load ที่ฉลาดมากที่สุดวิธีหนึ่ง โดยสามารถกำหนดเงื่อนไขในการแบ่ง table เช่น แบ่งตามปีที่ซื้อสินค้า, แบ่งตามแผนก เป็นต้น  
ข้อจำกัดในการใช้ Table partition คือ

1. Data Type ที่ใช้สำหรับทำเป็นเงื่อนไขใน Partition Function ต่อไปนี้จะใช้ไม่ได้ครับ เช่น text, ntext, image, xml, varbinary(max), varchar(max) และ Data type ที่สร้างมาจาก CLR
2. จำนวน partition สูงสุดที่สร้างได้ก็คือ สามารถสร้างได้ไม่เกิน 1,000 partition (โดยส่วนตัวก็ไม่เคยใช้งานถึงครับ)
3. จำนวน boundary ที่สร้างได้สูงสุดไม่เกิน 999



 

5) สำหรับ database ขนาดใหญ่ให้ใช้วิธีการแยกไฟล์ออกเป็นหลายๆ ไฟล์โดยใช้ File Group

หลักการจะเหมือนกับการทำ partition table แต่เป็นระดับ Database แทน โดยการแยก table กระจายไปแต่ละ physical disk แต่วิธีการแยกเราจะใช้ file group เป็นเหมือนการแบ่งกลุ่มให้กับข้อมูล ซึ่งสามารถทำได้ทุกเวอร์ชั่นของ SQL Server



 

6) เมื่อใช้ Data type แบบ TEXT, NTEXT, IMAGE ให้แยกเก็บต่างหากจาก Table Storage

โดยปกติแล้วการเก็บข้อมูลของ column ที่มี data type เป็น text, ntext, image นั้นจะเก็บอยู่ใน page เดียวกับ column อื่นๆ เพราะฉะนั้น เวลาใช้คำสั่ง select ก็จะทำให้ sql server ไปอ่าน column เหล่านี้ขึ้นมาด้วย ซึ่งถือว่าเป็น column ที่พิเศษ ดังนันจึงควรจะทำการแยก column ที่เป็น text, ntext, image แยกออกมาต่างหาก เพื่อเพิ่ม performance โดยใช้คำสั่ง
EXEC sp_tableoption 'orders', 'text in row', 'ON'

7) พยายามใช้ constraint ที่เป้น built-in เช่น primary key, foreign key, check, default, unique แทนการเขียน trigger ขึ้นมาเอง

contraint ต่างๆ ที่มีอยู่แล้ว ทาง microsoft ได้ทำการทดสอบเรื่อง performance มาเป็นอย่างดีอยู่แล้ว พยายามใช้ built-in เป็นหลักเพื่อให้ได้ performance ที่ดี แต่ถ้า built-in ที่มีอยู่ ไม่สามารถตอบสนองสิ่งที่เราต้องการได้จริงๆ ถืงจะเริ่มนึกถึงการเขียน trigger ขึ้นมาใช้งานเองอีกทีนึง

8 ) แยก Index ไว้คนละ Physical Disk โดยใช้ File Group

การสร้าง index จะทำให้การ access data ทำได้เร็ว แต่ก็ควรที่จะแยก index ไว้คนละ physical disk โดยใช้ file group ได้เหมือนกัน

9) ใช้ Disk RAID เพื่อเพิ่ม Performance

            ถ้ามีงบประมาณมากหน่อย ก็อาจจะนึกถึงการซื้อ Hardware RAID Disk ซึ่งจะได้ performance ที่ดีแน่นอน


Query Cache Strategy


          เป็นการนำผลที่ได้จากการ Query เก็บลงข้อมูล Cach  จากนั้นเมื่อมีการ Query ด้วย Statement เดิม มันจะเข้าไปหาใน Cach ก่อน หากพบข้อมูลมันจะดึงออกมาแสดงผลทันที ไม่ต้องไป Query จาก Table มาใหม่ ทำให้ผลการค้นหานั้นเร็วขึ้นมาก โดยปกติ SQL Server จะมีการทำงานแบบนี้อยู่แล้ว แต่สำหรับ MySQL จะต้องทำการเปิดคุณสมบัติ Query Cach เสียก่อนจึงจะสามารถใช้งานได้

 ตัวอย่างการทำ Query Cach

การทำ Query Cach สำหรับ MySQL นั้น มีวิธีดังนี
 1. ตรวจสอบว่ามีการเปิดคุณสมบัติ Query Cach ไว้หรือไม่ 


การที่จะใช้ Query Cache ได้นั้น ตัวแปร have_query_cache ต้องเท่ากับ ‘YES’  ตัวแปร ‘query_cache_type’ เท่ากับ ‘ON’ นอกจากนี้ต้องคอนฟิกค่า query_cache_size ด้วย เพื่อจองขนาด memory เพื่อใช้เก็บ query cache  ค่า 0 คือการปิดคุณสมบัติ query cache


      2. ทดสอบ Query ปกติ ก่อนทำการเปิด Query Cach ได้ผลลัพธ์ดังนี้



พบว่าใช้เวลาประมาณ 0.21 วินาทีในการ Query



 3. ทำการคอนฟิกค่า query_cache_size แก้ไขไฟล์ /etc/my.cnf โดยเพิ่มคอนฟิก query_cache_size ลงไป ให้อยู่ภายใต้คอนฟิกของ [mysqld] เช่นต้องการจอง memory ขนาด 32 Mbytes สำหรับทำเป็น cache ตัวอย่างคอนฟิกไฟล์จะเป็นดังนี้

หลังจากแก้ไขคอนฟิกไฟล์ /etc/my.cnf ให้รีสตาร์ต Service MySQL Server ใหม่






       4. ทดสอบ Select ครั้งแรกหลังจากเปิด Query Cache



ยังคงใช้เวลา 0.23 วินาที เนื่องจากยังไม่มีการเก็บข้อมูลลงใน Cache
สามารถใช้คำสั่ง SHOW STATUS เพื่อดูสถิติการใช้ cache ของ MySQL

ค่าตัวแปร Qcache_not_cached เพิ่มเป็น 1 คือการ SELECT ครั้งนี้ไม่ได้ดึงข้อมูลจาก cache และค่า Qcache_inserts เป็น 1 คือเริ่มมีการใส่ผลลัพธ์เข้าไปใน cache

ทดลองรัน SELECT อีกครั้งนึง โดยพิมพ์ statement ให้เหมือนเดิมทุกอย่าง ทั้งตัวพิมพ์ใหญ่ พิมพ์เล็ก การเว้นวรรค ต้องเหมือนกันหมด  ย้ำอีกที ต้องเหมือนกันหมด เพราะ MySQL ใช้ในการเปรียบเทียบกับ statement ที่เก็บไว้ใน cache


เวลาที่ใช้กลายเป็น 0.00 วินาที เพราะว่าผลลัพธ์จากการ SELECT ครั้งนี้ MySQL ไปดึงมาจาก cache แทน เราสามารถตรวจสอบว่าผลลัพธ์จากการ SELECT มาจาก cache โดยดูค่า Qcache_hits ที่เพิ่มขึ้น จากคำสั่ง SHOW STATUS