เพิ่ม Index ให้ WordPress เพื่อลดโหลด

อันนี้เป็นบทความสั้นๆครับ เกิดจากการสังเกตว่าใน WordPress ของผมนั้นจะมี Query อันนึงที่ run เยอะมาก และบางทีก็นานมาก ตัวอย่างประมาณนี้ครับ

SELECT post_id FROM wp_postmeta WHERE meta_key = '_wp_attached_file' AND meta_value = '2015/11/top-5-antivirus.jpg';

ซึ่งจากที่เดาคือมันเช็คว่า Attachment ตัวนี้มี Post ไหนใช้บ้าง ผมไม่รู้ว่าตัวไหนเป็นคนเรียกใช้ แต่เนื่องจากมันเยอะมาก เลยทำการเพิ่ม index ให้มัน ทำให้มัน Query ได้เร็วขึ้น
ALTER TABLE wp_postmeta ADD INDEX first_50_key_10_value (meta_key(50),meta_value(10))

เพื่อความประหยัดผมเลย index meta_value แค่ 10 ตัวพอ (เพียงพอสำหรับ /ปี/เดือน/ ก็ลดไปได้เยอะและ)

แต่อันนี้ไม่ได้ช่วยกับทุกคนนะครับ บางทีมันอาจจะมาจาก Theme หรือ Plugin ก็ได้ ดังนั้นต่อให้ท่านใช้ไปก็ไม่อาจจะรับประกันได้ว่ามันจะช่วยท่านได้

บันทึกการซน Thaiware

อันนี้เป็นบันทึกสมัย QA เปิดใหม่ๆนะครับ ผมแจ้ง bug ไปเมื่อ 13 มกราคม 2013 ครับ ผมถือว่านานแล้วเลยเอาออกมาเล่าสู่กันฟัง หลายอย่างกันไปแล้วครับ แต่เอามาแสดงเป็นอุธาหรณ์สำหรับ Programmer ครับ

อันแรกที่เจอ เป็นผลจากการซนของผมครับ ความซนอย่างหนึ่งของผมคือ ถ้ารู้ว่าเป็น custom web ผมจะลองใส่ ‘ or ‘1’=’1 ลงไปครับ ถ้าเป็นหน้า login แล้วผ่านหรือ error ขึ้น ก็ Jackport ครับ เป็น SQL Injection พื้นฐาน ไม่ขออธิบายนะครับ หาตาม Google ได้ทั่วไป จริงๆทำแบบนี้แล้วเกิด memory limit เพราะเค้าไม่ได้ใส่ limit ไว้เลยดึงข้อมูลทั้งหมดมาใส่ ram แต่มันมากเกินไปใส่ไม่หมด วิธีแก้คือใช้ prepare หรืออย่างน้อยใช้ mysql_escape_string ครับ หรือไม่ก็เอา username ไป select อันเดียว แล้วตรวจ password ด้วย php ครับ

อันที่สอง ผมใช้ Dev Console ของ Chrome แก้ Cookie ตัว acc_member_id กับ acc_type ให้เป็นของ admin ได้ (จริงๆได้แค่ดูข้อมูลของ id=1 แต่คิดว่าถ้าลองดีๆ ก็น่าจะได้สิทธิไม่ยาก) ผมคาดว่าเค้าเอา ID จาก Cookie ไปค้นหาข้อมูลตรงๆใน Database เลยก็เป็นได้ ซึ่งไม่ควรทำ โดยทั่วไปเราไม่ควรเชื่อข้อมูลจากฝั่ง client หรือควรมีมาตรการยืนยันมากกว่านี้

อันที่สาม เป็น ผลมาจากครั้งที่ 2 ครับ ผมคาดว่าเค้าใช้ session ยืนยันตัวตนแทนในกรณีที่มี session ผมก็ใช้วิธีเดียวกับวิธีที่ 2 แต่คราวนี้ผมไม่แน่ใจว่าแก้พวก id กับ type ได้ไหม ใน mail ผมไม่ได้มีข้อมูลด้านนี้ แต่มีข้อมูลอีกแบบคือ ผมสามารถใส่ sql injection ลงไปในช่อง acc_member_id แล้วเกิด error memory limit ขึ้น ซึ่งผมเดาว่ามันคงไปดึงข้อมูลทั้งหมดใน DB ออกมาแน่ๆเลย ในตอนนั้นผมแนะนำให้เค้าใช้ mysqli (หรือ PDO) เพื่อให้ใช้ prepare statement เพื่อป้องกัน sql injection ครับ แต่จริงๆผมลืมแนะนำไปว่า ควรใส่ limit 1 ไว้ด้วย เพื่อประสิทธิภาพที่ดีครับ คือถ้าเจอ 1 ตัวแล้วมันจะหยุดหาไม่หาต่อ แล้วก็หากไม่มี session ควรยืนยันตัวตนผ่านวิธีอื่นไม่ใช่เอา id อย่างเดียวไปหาตรงๆ อย่างเช่นเอา pass มาก md5 หลายๆชั้นใส่ cookie แล้วเวลายืนยันก็ไป select id ออกมา แล้วเอา pass มาทำด้วยวิธีเดียวกันถ้าตรงกับ pass ที่เข้ารหัสใน cookie ค่อยเอาข้อมูลใส่ session [ref to mysqli.prepare]

อันสุดท้าย [สถานะวันที่ 22/11/13 คือยังไม่ได้รับการแก้ไข]  ดังนั้นผมยังไม่บอกไรมาก บอกได้แค่ XSS จ้า

วิธีการสุ่มค่าจาก SQL ที่ดีกว่าการใช้ ORDER BY RAND()

โดยทั่วไปเท่าที่ผมเห็น การจะสั่ง sql ให้ดึงแถวที่ต้องการมาโดยเรียงลำดับแบบสุ่มจะใช้วิธี ORDER BY RAND() เช่น

SELECT * FROM `table` WHERE 1 ORDER BY RAND()

แต่วิธีนี้ไม่ใช่วิธีที่ดีเลย เพราะสมมุตว่าข้อมูลมีจะนวน 100000 แถว จะต้องเกิดการ random ค่าทั้งหมด 100000 ครั้ง นอกจากนั้นเมื่อ random เสร็จ ก็ต้องเอาข้อมูลนั้นมาเรียงลำดับใหม่อีกครั้ง ซึ่งในขั้นตอนการเรียง โดยมากแล้ว ตัว Database จะไม่ยอมให้ใช้เนื้อที่เกินจำนวนหนึ่งใน RAM แต่ถ้าข้อมูลเยอะๆแบบนี้ จะใช้การเขียนลง file ก่อน แล้วค่อย sort ซึ่งทำให้ช้ากว่าใน RAM มาก

ดังนั้นผมจึงหาวิธีสุ่มค่าที่มีประสิทธิ์ภาพที่ดีกว่าในเน็ต ซึ่งได้ผลลัพธ์คือเว็บนี้ เค้าแนะนำให้ทำแบบนี้แทนครับ

SELECT  * FROM    (
        SELECT  @cnt := COUNT(*) + 1,
                @lim := 10
        FROM    `table`
) vars
STRAIGHT_JOIN
 (
        SELECT  r.*,
                @lim := @lim - 1
        FROM    `table` r
        WHERE   (@cnt := @cnt - 1)
                AND RAND(20090301) < @lim / @cnt
) i

โดย `table` หมายถึงชื่อตาราง และตรง @lim := 10 หมายถึงต้องการ 10 ตัว ส่วนตัวเลข 20090301 เค้าบอกเป็นเลขอะไรก็ได้ เค้าเอาเลขนี้มาจากวันที่โพส และควรเปลี่ยนทุกครั้ง เพื่อให้ผลไม่เหมือนเดิม

หลักการทำงานคือเหมือนเค้าใช้หลักความน่าจะเป็นแทนการสุ่มมั้งครับ

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