เขียน node.js ใช้งาน MySQL database

เขียน node.js ใช้งาน MySQL database

ก็คงไม่ต้องเกริ่นอะไรกันมาก เพราะนี้เป็นบทความใน series การเรียนรู้ และทดสอบใช้งาน node.js ถ้ายังไม่อ่านบทความก่อนหน้านี้ อ่านก่อนเลยเป็นสิ่งแรก กับ เขียนโปรแกรม node.js เพื่อศึกษาธรรมชาติ ของ node.js

คราวนี้ เรามาทำงานกับระบบเก็บข้อมูลยอดนิยมตัวหนึ่ง นั่นก็คือ MySQL นั่นเอง ผมเชื่อเหลือเกินว่า คนทำเว็บ แทบทุกคนต้องเคยใช้มันอย่างแน่นอน ก็เลยหยิบมาเป็นตัวอย่าง เพื่อการเรียนรู้ จะได้เข้าใจง่ายๆครับ โดยจบเนื้อหานี้ ผมจะให้ทุกท่านได้รู้ว่า node.js มันทำงานร่วมกับ IO ที่เป็นแบบ Asynchronous ได้อย่างไร และถ้าเราจะเขียนให้เป็นแบบ Synchronous ได้อย่างไร เพราะว่า แต่ละงานมันมีรายละเอียดการทำงานที่แตกต่างกันนั่นเองครับ source code ในบทความนี้ download ได้ที่นี่

การเรียกใช้งาน package

หากยังจำกันได้ ผมเคยเล่าไว้ในบทความเก่าๆ ว่า node.js นั้นมี package มาให้เราใช้งานมากมาย ซึ่งมันจะช่วยอำนวยความสะดวกให้กับเรา ทำให้เราทำงานต่างๆเร็วขึ้น ท่านสามารถค้นหา node package อื่นๆได้จาก https://npmjs.org/ ซึ่ง node package ทุกตัว ก่อนที่ท่านจะใช้งาน ผมขอเน้นเลย ว่าต้องอ่านทำความเข้าใจมันด้วยนะครับ ถ้าเราไม่อ่าน มันก็เหมือนกับว่า เราเอา microwave ให้เด็กประถม ใช้ปรุงอาหาร โดยไม่ให้อ่านคู่มือ และไม่เคยมีใครเคยสอนมาก่อน แน่นอนว่า ไม่สามารถใช้งานได้อย่างแน่นอน ทั้งๆที่ microwave สามารถทำอาหารได้ตั้งหลายอย่างและ เกือบทุก package เราจะต้องเรียกติดตั้ง ก่อนใช้งานเสมอครับ (ติดตั้งครั้งเดียว ใช้งานได้ตลอดไป) โดยคำสั่งติดตั้ง มันก็จะบอกอยู่ในหน้าคู่มือนั่นเอง (ถ้าไม่อ่านแล้วจะติดตั้งมันยังไง ยังไงก็ต้องอ่านครับ) สำหรับวันนี้ node package ที่ทุกท่านจะต้องติดตั้งในวันนี้ นั่นก็คือ MySQL นั่นเอง สำหรับ node-mysql package นั้น สามารถดูคู่มือการใช้ตัวเต็มๆ ได้ที่ https://github.com/felixge/node-mysql

ติดตั้ง MySQL node package

ให้เรา shell เข้าไปที่ folder ที่เราจะใช้งานก่อนนะครับ แล้วค่อยสั่งติดตั้ง หากเราติดตั้ง ตอนที่เราอยู่ที่ folder ไหน เราก็จะทำงานกับ package นั้นได้เฉพาะ folder นั้นเท่านั้น

npm install mysql@2.0.0-alpha9

สำหรับคำสั่งติดตั้ง อาจจะไม่เหมือนตัวอย่างนี้ เนื่องจากมีการปรับปรุง version ใหม่ ดังนั้นก็ขอให้ทุกท่านอิงตามคู่มือของผู้พัฒนาเป็นหลักนะครับ

เริ่มเขียน node.js อ่านค่าจาก database

เรื่องการสร้าง database , table, field ของ MySQL ผมขอข้ามไปเลยนะครับ ถือว่าทุกท่านมี database แล้ว

ผมจะใช้ตัวอย่างในเนื้อหานี้ ด้วย table ชื่อ "table_test" ซึ่งภายใน มีแค่ field (id, value) เท่านั้น และมีข้อมูลอยู่แล้ว ดังภาพนี้ครับ

mysql table เก็บข้อมูล id value

และผมจะทำการ select ข้อมูลออกมาแสดง ได้ดังนี้ครับ

var mysql = require('mysql');

var mysql_conn = mysql.createConnection({
    host: '127.0.0.1',
    user: 'root',
    password: '',
    database: 'db'
});

mysql_conn.query('SELECT * FROM table_test', function(err, rows) {
    if (err) {
        throw err;
    }
    for (id in rows) {
        console.log(rows[id]);
    }
});

อธิบายการทำงาน ของ MySQL โค้ด ก็คือ

  • บรรทัดแรก เป็นการ load module ใส่ตัวแปรชื่อ mysql
  • บรรทัด 3-8 ทำหน้าที่เชื่อม connection เข้าไปที่ mysql โดยเอา connection ที่เชื่อมได้นั้น ใส่ไว้ในตัวแปรชื่อ mysql_conn
  • บรรทัด 10 สั่ง query ตามปกติ ถ้าสังเกตุให้ดี ทางด้านขวามือ มันมี ตัวแปรที่รับค่าออกมา 2 ตัว คือ err และ rows สำหรับตัวแปร err นี้ เค้าจะเอาไว้รับค่า error ในกรณีที่เกิด error ต่างๆ ซึ่งเราควรเอา error นี้มาตรวจสอบก่อนด้วย ไม่อย่างนั้นเราอาจจะได้ค่าที่ผิดปกติไปใช้งานต่อ
  • บรรทัดที่ 11-13 ผมเช็คว่า ถ้ามี error ให้ throw ออกมา โปรแกรมก็จะบอกว่า error อะไร และจะหยุดทำงานทันที
  • บรรทัดที่ 14-16 เอาผลลัพท์ ที่ได้จากการ query ที่ใสน่ในตัวแปร rows มาวน loop เพื่อ print ออกหน้าจอ

ซึ่งผลของมันคือ ดังนี้ครับ

ผลลัพท์ที่ได้จากการ query

แต่หากใครได้ error หน้าตาแบบนี้ แสดงว่าคุณลืมติดตั้ง mysql package นะครับ 

can not find module mysql error

ย้อนกลับขึ้นไปอ่านข้างบนใหม่อีกทีครับ ในการติดตั้ง ถ้าติดตั้งเรียบร้อยแล้ว ก็จะใช้งานได้เลยครับ

แค่นี้เองครับ node.js เราก็ทำงานเชื่อมกับ MySQL ได้แต่ แต่.... มันยังไม่จบง่ายๆแค่นั้นหรอกครับ ผมลงเปลี่ยนโค้ดนิดเดียว โดยเพิ่มการทำงาน ก่อน และ หลังจากการ query database เปรียบเสมือนว่า เรามีงานอื่นๆ ที่ต้องทำก่อนไป query แล้ว เราก็ query เอาผลมาใช้ทีหลัง โดยผมจะเพิ่ม start before mysql , end after mysql เข้าไป ก่อนและหลัง เพื่อดูลำดับการทำงานของผม ดังนี้

var mysql = require('mysql');

var mysql_conn = mysql.createConnection({
    host: '127.0.0.1',
    user: 'root',
    password: '',
    database: 'db'
});
console.log('start before mysql');
mysql_conn.query('SELECT * FROM table_test', function(err, rows) {
    if (err) {
        throw err;
    }
    for (id in rows) {
        console.log(rows[id]);
    }
});
console.log('end after mysql');

ซึ่งผลที่ได้มันก็น่าจะเป็นข้อความ start before mysql แล้วตามด้วยผลลัพท์ จาก mysql แล้ว ตามด้วยข้อความ  end after mysql แต่ผลลัพท์ที่ได้ กลับเป็นแบบนี้

start end มาก่อน ผลลัพท์จาก mysql

ถึงตรงนี้แล้ว คุณทราบหรือไม่ครับ ว่าทำไมถึงเป็นแบบนี้ ถ้าไม่ทราบ แสดงว่า คุณอ่านบทความก่อน ยังไม่เข้าใจ ขอเชิญให้ไปอ่านใหม่อีกรอบครับ 555 จิ้มเลย เขียนโปรแกรม node.js เพื่อศึกษาธรรมชาติ ของ node.js

เฉลย มันเป็นเพราะว่าคำสั่งที่ทำงานแบบ MySQL ชุดนั้น มันเป็นการทำงานแบบ Asynchronous นั่นเอง ดังนั้น เมื่อทำงาน บรรทัดที่ 9 แล้ว บรรทัดที่ 10 เป็น Asynchronous ก็เลยเริ่ม query (แต่ยังไม่เสร็จนะ) ก็เลื่อนไปทำบรรทัดที่ 18 ต่อ ข้อความ end after mysql มันเลยออกมาก่อนผลลัพท์ของ MySQL นั่นเอง แล้วเมื่อ MySQL ทำงานเสร็จ มันก็ค่อยแสดงผลออกมาตามหลัง

นี่คือการที่มันทำงาน แบบ ไม่มี blocking เพราะว่า ปกติการทำงานกับ IO ต่างๆ (MySQL ก็เป็น IO ตัวนึงนั่นแหล่ะ) มันจะใช้เวลานานกว่าการประมวลผลมากมายหลายล้านเท่านัก มันเลย เอาการทำงานแบบ Asynchronous ไปเข้าคิวเวลาเอาไว้ เพื่อรอทำงานเมื่อถึงเวลา (ซึ่งมันก็เสี้ยววินาทีนั่นแหล่ะ) ทำให้เอาพลังประมวลผลไปทำงานอื่นต่อได้เลยทีนี้ ปัญหาก็เกิดทันที เพราะถ้าเกิดว่า เราต้องเอาผลลัพท์จาก MySQL มาประมวลผลต่อล่ะ จะทำอย่างไร?

node.js ใช้งาน MySQL แบบ Synchronous

MySQL เราก็สามารถเขียนมันให้เป็นแบบ synchronous ได้นะครับ จริงๆหน้าคู่มือเค้าก็บอกเอาไว้เลย โดยเค้าจะเรียกมันว่า การ "stream" โดยเราก็เปลี่ยนใช้โค้ด ดังนี้แทน

var mysql = require('mysql');

var mysql_conn = mysql.createConnection({
    host: '127.0.0.1',
    user: 'root',
    password: '',
    database: 'db'
});
console.log('start before mysql');
var query = mysql_conn.query('SELECT * FROM table_test');
query
  .on('error', function(err) {
    // Handle error, an 'end' event will be emitted after this as well
  })
  .on('result', function(row) {
    // Pausing the connnection is useful if your processing involves I/O
    mysql_conn.pause();
    
    processRow(row, function() {
      mysql_conn.resume();
    });
  })
  .on('end', function() {
    // all rows have been received
    console.log('end after mysql');
  });
function processRow(row,callback){
    console.log(row);
    callback();
}

อธิบายโค้ด ก็คือ

  • บรรทัด 10 คือการ query แล้วใส่ในตัวแปร query
  • บรรทัด 11 คือการเรียกใช้ method ต่างๆในตัวแปร query
  • บรรทัด 12-14 คือการเรียกใช้ หากมี error ให้ทำงานในนั้น
  • บรรทัด 15-22 คือการทำงานกับผลลัพท์ ซึ่ง จะเห็นว่า มันจะเริ่มต้นด้วยการ pause ก่อน ให้หลับตานึกถึงน้ำที่ไหลออกจากท่อ ลงมาในแก้ว เมื่อเต็มแก้ว แล้วเราก็ปิดก๊อก นั่นล่ะครับ
  • บรรทัด 19,21 คือการเอาผลลัพท์ (เอาน้ำในแก้ว) ไปเข้า function processRow ซึ่ง function นี้ก็แค่ print ออกมาที่หน้าจอ (เทน้ำออกจากแก้ว) แล้ว callback กลับไป เพื่อให้ทำงานได้ต่อ (ถ้าใครลืม call back มันจะหยุดที่ตรงนี้ แล้วไม่ทำอะไรต่อแล้วนะครับ)
  • บรรทัด 20 หลังจากที่ได้รับ callback ก็กลับมาเปิดท่อน้ำอีกครั้ง แล้ววนไปที่ บรรทัดที่ 15 ใหม่ เป็นอย่างนี้ไปเรื่อยๆ จนน้ำหมด ก็จะข้ามไปบรรทัดที่ 23
  • บรรทัด 23 เริ่มทำงาน เมื่อข้อมูลที่ query ออกมานั้นถูกใช้ไปจนหมดแล้ว ก็ให้แสดงข้อความ end after mysql

ซึ่งผลลัพท์ที่ได้ เป็นดังนี้ครับ เป๊ะเลย

ผลลัพท์ เรียงลำดับถูกต้อง

ว่าด้วยเรื่องของ stream

เรื่องนี้ เป็นอีกเรื่องที่ต้องทำความเข้าใจ โดยเฉพาะคนที่ต้อง interface กับ IO ต่างๆ มันสำคัญมาก เพราะว่า stream นี่แหล่ะ ที่จะเป็นผู้ช่วยได้เป็นอย่างดี

การที่เราไป interface กับระบบ IO อื่นๆ ไม่ว่าจะเป็นการอ่าน database , การเปิด port เพื่อ รับ หรือ ส่งข้อมูล มันเหมือนเรา เปิดท่อน้ำ เพื่อ ส่งน้ำไปมาหากัน ซึ่ง ถ้าให้มองภาพรวม เหมือนกับว่า โปรแกรม เราคือโรงงานขนาดใหญ่ แต่ว่า เวลาเราทำงานกับ IO ต่างๆ เช่น อ่านข้อมูลจาก Harddisk มันกลับกลายเป็นเปิดท่อน้ำ เท่าท่อของปืนฉีดน้ำ แล้วโรงงานที่ต้องใช้น้ำปริมาณมากๆ ก็ต้องมารองน้ำ จนเต็ม เพื่อเอาไปใช้งานต่อได้ นี่คือปัญหาที่ทำให้การประมวลผลในปัจจุบัน มันช้าอยู่ครับ เพราะเกิด "คอขวด" ที่ IO ต่างๆนั่นเอง คือ CPU, RAM มันพัฒนาไปไกลถึงไหนต่อไหนแล้ว รับส่งข้อมูลกันทีหลาย หลาย Gb (giga bit) ต่อวินาที แต่ HDD มันส่งข้อมูลได้เต็มที่ก็แค่ 1 Gb (giga bit) ต่อวินาทีเท่านั้น (SSD ก็เลยออกมาแก้ปัญหา)

ดังนั้น เมื่อระบบ ทำงานไปเร็วกว่า แล้วข้อมูลที่ส่งมาไม่ทัน มันจึงมีกระบวนการหยุดรอคอย เพื่อรองน้ำให้เต็มถัง แล้วค่อยเอาน้ำไปใช้ เค้าเรียกมันว่า "stream" นั่นเอง โดยเราต้องมองภาพให้ออก ว่าการ stream นั้นมันเกิดขึ้นได้ ทั้ง ขาเข้า และ ขาออก (แต่ปกติเราจะทำงานกับขาเข้าเป็นส่วนใหญ่) ดังนั้น คนที่ทำ project ใหญ่ๆ ที่มีการ transfer data กันเยอะ ต้องมองภาพให้รอบด้วย ตัวอย่างเช่น การ stream ภาพยนตร์ออกไปที่ user ตามคาบเวลา เช่นเรา stream ออกไป วินาทีละ 500 kbps แต่เกิดจังหวะนั้น internet ที่ user ใช้เกิด drop speed ลงไปเหลือ 128kbps โปรแกรมเราจะทำอย่างไร? ทางเลือกก็มีแค่ ข้าม ชุดข้อมูลเหล่านั้นไปเลย หรือว่า หยุดรอเพื่อให้ส่งข้อมูลให้ครบก่อน แล้วค่อยส่งชุดถัดไป

หลาย package ที่ทำงานกับ IO ก็จะมีชุดของ stream ติดมาด้วยแล้ว เช่นถ้าหากทำงานกับ file ในเครื่อง node จะมี package ที่ชื่อว่า fs ซึ่งเป็น package ที่ไม่ต้องติดตั้งเพิ่มเติม เพราะติดมากับ node.js ตั้งแต่ตอนติดตั้งเลย ก็มี ส่วนการทำงานที่เป็น stream มาให้ใช้งานด้วยเช่นกัน อ่านได้จาก http://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options

ผมจำได้ว่า เดือนก่อน มีคนถามผมใน facebook https://www.facebook.com/meewebfree.world เรื่องของการใช้ node.js อ่านค่าจากตาชั่ง digital ซึ่ง stream นี่แหล่ะครับ จะเข้ามาช่วยทำงาน ไม่ว่าข้อมูลจะเข้ามาเร็วไป หรือช้าไป node.js ก็จะไม่พลาดทุกข้อมูลครับ (แต่ควรไปเก็บใน database อื่นที่ไม่ใช่ MySQL นะ เพราะมันจะทำงานไม่ทัน หรืออาจจะใช้ table แบบ memory ก็ได้เร็วดีครับ) หลังจากที่ได้ข้อมูลแล้วก็ประมวลผลต่อได้เลยทันทีเช่นเดียวกัน เพราะมันไม่เกิด blocking เพื่อรอข้อมูลนั่นเอง (นี่ไง ประโยชน์ของ Asynchronous)

เอา node.js มาทำเว็บได้มั้ย ไหนๆก็เชื่อม MySQL ได้แล้ว

ได้ครับ แต่ผมคิดว่า ผมใช้ php เขียนหน้าเว็บ เขียนระบบได้เร็วกว่า และอย่างน้อย ก็ไม่ต้องศึกษา environment ใหม่ๆอีกด้วย (เช่น หากอยากประมวลผลภาพด้วย GD library ขึ้นมา node.js จะต้องเขียนยังไง?) เพราะว่า การเอา node.js มาทำหน้าเว็บนั้น คุณจะไม่ได้ทำงานบน apache แล้ว เพราะ node.js นี่แหล่ะครับ จะทำหน้าที่แทน apache เลย  (เปิด socket รับ request เองแล้วประมวลผลต่อเลย) ดังนั้นผมว่า ประสบการณ์ PHP กว่า 7 ปีของผม เขียนงานได้เร็ว และหลากหลายกว่ามาเริ่มต้น node.js แน่นอนครับ แต่สำหรับคนที่อยากลอง ก็ไปลองได้กับ express นะครับ http://expressjs.com/ มันคือ source code ที่เขียนขึ้นมาด้วย node.js เมื่อเอามาติดตั้งแล้ว จะทำให้เราได้หน้าเว็บขึ้นมาใช้ โดยใส้ในเป็น node.js ทั้งหมด (ส่วนหน้าเว็บ ก็เป็น HTML , Javascript อะไรตามปกตินั่นแหล่ะ)

จบแล้วครับ สำหรับการเอา node.js มาเชื่อมกับ database แบบ MySQL มันก็ไม่ได้มีอะไรยากหรอกครับ ลอกโค้ดตัวอย่าง แล้วเอามาปรับ ก็ทำงานได้แล้วครับ 5555 จริงๆ คนที่ทดสอบ เรียนรู้มาถึงตรงนี้ได้ จะสามารถเอาไปต่อยอดได้อีกเยอะมากๆแล้วครับ เพราะว่า จะเข้าใจการทำงานของ node.js ,IO, stream, Asynchronous แต่อย่างที่บอก จะเจ๋งหรือไม่เจ๋ง ก็ขึ้นอยู่กับการออกแบบระบบเป็นสำคัญเลยครับ ถ้าออกแบบได้ดี ระบบจะทำงานได้เร็วมากๆ ในแบบที่ php,python ไม่สามารถให้คุณได้เลย

Create: Modify : 2013-10-13 20:15:09 Read : 27305 URL :