1

กระทู้: หัดใช้ Module Activator.js

ผมกำลังลองเล่น activator.js ของ node.js อยู่ครับ

https://npmjs.org/package/activator

แต่ว่าอยากใช้ database ร่วมกับ redis.io เลยอยากถามว่าผมจะใช้ hashes ดีไหม โดยใช้ key เป็น อีเมลครับ เพราะว่ามันต้องไม่ซ้ำกันอยู่แล้ว แล้วเวลา search หา key จะได้ง่ายด้วย
แต่ว่า value ที่ผมอยากใส่เป็น array ประมาณนี้

{
        email: 'email',
        password: 'password'
        firstname: 'blahblah',
        lastname: 'blahblah',
        address: {
            addr: '123 T.Bangrak',
            city: 'Bangkok',
            country: 'Thailand',
            zipcode: '20056'
        },
        activate: FALSE
        
    }

แล้วเวลาเรียกออกมาใช้จะทำยังไงเหรอครับ หรือว่าอยากเปลี่ยน activate: TRUE

2

ตอบ: หัดใช้ Module Activator.js

น่าจะไปไกลกว่าผมแล้วนะครับเนี่ย
เพราะว่าผมยังใช้ node.js เอาไว้ทำงานหลังบ้านเท่านั้นเอง

งานหน้าบ้านต่างๆ ผมใช้ php mysql ปกติเลยครับ

เหตุผล เพราะว่าผมคิดว่าเขียนด้วย php mysql ผมคิด ออกแบบ และเขียน ได้เร็วกว่า รวมทั้งมั่นใจใน stability มากกว่าครับ

ส่วน redis แม้ว่ามี feature ที่จะเก็บข้อมูลแบบ permanent ได้ แต่จากที่ผมคุยกับเพื่อนผมหาก data มหาศาล เช่น เป็นหลายล้าน key แบบนี้ redis จะเริ่มเอ๋อๆครับ (จังหวะการเขียนข้อมูลลง disk)

สวนเรื่องคำถาม
เท่าที่ดูโครงสร้างที่ส่งมา เป็น hash ถูกต้องแล้วครับ เพราะมันจะเก็บข้อมูลหน้าตาแบบนี้แหล่ะ แต่อาจจะต้องพิจารณาเรื่อง address นิด เพราะตัวนี้อาจจะต้องเก็บเป็น JSON ใน hash index address เลย

แต่เรื่องการ activate เนี่ย ไม่แน่ใจครับ อาจจะต้องศึกษาจากคู่มือดู แต่เท่าที่อ่านผ่านๆเหมือนว่า activator ก็มีการเก็บข้อมูลในส่วนของตัวเองอยู่ด้วยนะครับ ดังนั้น การแก้ไขข้อมูลน่าจะต้องแก้ไขทั้งส่วนที่เราเก็บของเรา และในส่วน activator ด้วยนะครับ อันนี้ไม่แน่ใจครับ เพราะว่าไม่เคยใช้เลย

3

ตอบ: หัดใช้ Module Activator.js

อ้อ ผมเขียนผิดไปหน่อยครับ ที่จริงผมตั้งใจใช้ hashes ประมาณนี้

user(เป็น key) poomrin@gmail.com เป็น field01 value ก็ตัวข้างบนครับ รายละเอียดเป็นแบบ .json ถ้ามีรายอื่นก็เพิ่ม field(n) นั่นแปลว่ามันจะมี key เดียว แต่ field จะเพิ่มขึ้นเรื่อยๆ ครับ

4

ตอบ: หัดใช้ Module Activator.js

ผมไปเจอมาอันหนึ่งครับเกี่ยวกับ loop json

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
<script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
<script>

var testJSON = {"cluster":[
                            {"node":[
                                        {"name":"one", "number":'100', "error":"none"},
                                        {"name":"two", "number":'200', "error":"none"},
                                        {"name":"three", "number":'300', "error":"found"},
                                        {"name":"four", "number":'400', "error":"none"}
                                    ]
                            }
                            
                          ]
               }

$( document ).ready(function() {
if (testJSON.cluster.length != 0) {
    $.each(testJSON.cluster, function(i, clstr) {
        $('.clusters').append('<ul class="nodes"></ul>');
        $.each(clstr.node, function(i, ndes) {
            $('.clusters ul.nodes').append('<li>' + ndes.name + '&nbsp' + ndes.number + '&nbsp' + ndes.error +'</li>');
        });
    });
}
});
</script>
</head>
<body>
 
<div class="clusters"></div>
</body>
</html>

ผมเลยคิดว่าถ้าผมเก็บค่าไว้ใน redis.io ตารางผมน่าจะปรับเป็นแบบนี้ถ้าเขียนให้ถูก แล้วอยากให้วนลูบไปใส่ในรูปแบบของ form ครับ

{user:[
        {email: 'email'},
        {password: 'password'},
        {firstname: 'blahblah'},
        {lastname: 'blahblah'},
        {address: [
            {
            addr: '123 T.Bangrak',
            city: 'Bangkok',
            country: 'Thailand',
            zipcode: '20056'
            },
           {
            addr: '123 T.Bangrak',
            city: 'Bangkok',
            country: 'Thailand',
            zipcode: '20056'
            }
        ]},
        {activate: FALSE}
        
    ]}

แก้ไขล่าสุดโดย poomrin (2014-01-22 07:17:53)

5

ตอบ: หัดใช้ Module Activator.js

ผมไปศึกษาเพิ่มมานิดหน่อยครับ เลยอยากถามเรื่องนี้แทนเรื่องเดิม

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">

</head>
<body>
<script>
var user = {
    name: 'Peter',
    score: 100,
    country: 'Chile',
    pets: ['pet1', 'pet2', 'pet3'],
    vehicles:{
        motorcycles: ['BMW','Harley Davidson', 'etc'],
        trucks:['Toyota', 'Honda', 'Hyndai'],
        sedan_car: ['volvo', 'Mercedes Benz', 'Ferrari']
    },
    profile: {
        type: 'private',
        account: 'premium'
    }
};

var change = user.vehicles.motorcycles[2];
change.replace('etc','Yamaha');
console.log(change); //its change nothings.
//console.log(user.vehicles.motorcycles[2]);

</script>
</body>
</html>

จะถามว่าถ้าหากผมต้องการเปลี่ยนค่า 'etc' ให้เป็นค่าอื่นโดยที่อ้างอิงแค่ตำแหน่งไม่ใช่ value ครับ ต้องทำอย่างไง เพราะลองอันบนเป็นการอ้างที่ค่าของมัน แต่ก็ไม่ work นะครับ

6

ตอบ: หัดใช้ Module Activator.js

กำลังงงครับ กับคำว่า "ให้เป็นค่าอื่นโดยที่อ้างอิงแค่ตำแหน่ง" มันหน้าตาเป็นยังไงครับ นึกไม่ออก
หรือหมายถึงเรื่องนี้ pointer/reference
http://stackoverflow.com/questions/8318 … plain-this

7

ตอบ: หัดใช้ Module Activator.js

ผมหมายถึงตำแหน่ง (key) นะครับ ถ้าใช้ method .replace() จะต้องอ้างอิง value ใน array ด้วยนะครับเลยคิดว่าใช้ไม่ได้ อย่างตำแหน่งอ้างอิง ในที่นี้คือ user.vehicles.motorcycles[2] ซึ่งมีค่า 'etc' อยู่ แต่ว่าอย่าเปลี่ยนค่านี้เป็นค่าอื่นครับ

**ผมหาวิธีแก้ค่าในตัวแปรได้ 3 วิธีครับ แต่อยากช่วยแนะนำหน่อย เพื่อป้องกันค่าที่ใส่เข้าไปเช่นใช้ typeof เพื่อกำหนดค่าที่ใส่เป็น int หรือ string เท่านั้นในรูปแบบ function ครับ

var user = {
    name: 'Peter',
    score: 100,
    country: 'Chile',
    pets: ['pet1', 'pet2', 'pet3'],
    vehicles:{
        motorcycles: ['BMW','Harley Davidson', 'etc'],
        trucks:['Toyota', 'Honda', 'Hyndai'],
        sedan_car: ['volvo', 'Mercedes Benz', 'Ferrari']
    },
    profile: {
        type: 'private',
        account: 'premium'
    }
};

user.vehicles.motorcycles[2] = 'tom';

console.log(user);


 
// for changing many positions with jQuery.
$.extend(true, user, {
    vehicles:{
        motorcycles: ['BMW','Harley Davidson', 'dava'],
    }
});


function changeValue(positionKey, positionValue) 
{
    positionKey === typeof int;
    positionValue === typeof string;
    user.vehicles.motorcycles[positionKey] = positionValue;
    console.log(user);
}

changeValue(2, "tommy")

แก้ไขล่าสุดโดย poomrin (2014-02-13 07:08:31)

8

ตอบ: หัดใช้ Module Activator.js

หมายถึงต้องการเปลี่ยนชื่อ index? ถ้าใช่ คำถามต่อมาก็คือ ทำไมต้องเปลี่ยน? ในเมื่อเราสามารถสร้าง index ใหม่ได้เลยโดยไม่ต้องคำนึงถึง index เดิม อีกประเด็นก็คือการเปลี่ยน index เป็นการทำให้ระบบทำงานซับซ้อนขึ้นด้วยหรือไม่ เพราะว่าการเปลี่ยนชื่อ index นั้นแปลว่า การอ้างอิงโปรแกรมในส่วนต่างๆ ก็ต้องอ้างอิงจาก index ต่างชื่อกัน ทั้งๆที่ใส้ในมันเป็นค่าเดียวกัน

ดังนั้น เป็นการออกแบบระบบที่ไม่ดี จึงไม่มีคนทำ หรือ ไม่นิยมทำครับ แต่ถ้ามีเหตุจำเป็นที่ต้องเปลี่ยนชื่อ index ลองอธิบายมาดูครับ ว่าความจำเป็นแบบไหนอย่างไร จะได้หาทางแก้ปัญหาด้วยวิธีที่ถูกต้องแทน

สำหรับเรื่อง validate ก็ใช้ if ครอบอีกทีครับ ที่เหลือก็คล้ายๆกับที่เขีนมานั่นแหล่ะครับ ถูกต้องแล้ว ก็คือ

if(typeof positionValue === 'string'){ บลาๆๆ}else{บลาๆๆ}

สำหรับขั้นตอนการเขียนโปรแกรมให้รัดกุม จะมีหลักๆประมาณนี้
1.ตอนที่รับค่า มาจากส่วนไหนของโปรแกรมก็ตาม พยายามตั้งให้อยู่ในรูปแบบที่เรากำหนด เช่น string, int ก็ว่ากันไป แล้วต้องพยายามกำหนด range ของข้อมูลด้วย เช่นถ้าเป็น int ต้องอยู่ใน range ไหน ถึงไหน เป็นต้น
2.เอาเข้า function check typeof อีกที เพื่อความมั่นใจว่ามันจะต้องเป็นค่านั้นอีกที
3.ตอนจะหยิบค่ามาใช้ ก็เช็คอีกที

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

เพราะว่า ข้อสองกับ ข้อสามเนี่ยมันเอาไว้ใช้ในกรณีที่ต่างกันนิดหน่อย ถ้าเราใช้ข้อสอง เราจะใช้ตอนก่อนเก็บลง database เพื่อให้เรามั่นใจว่า ข้อมูบที่เก็บลง database นั้นถูกต้อง เวลาจะหยิบใช้ครั้งต่อไปก็ไม่ต้องเช็คแล้ว แต่ถ้าเป็นข้อที่สาม เราจะใช้ตอนก่อนเอาไปแสดงผลหรือประมวลผลต่อ ซึ่งปกติตรงนี้จะถูกเรียกใช้ถี่กว่า (เพราะว่าโดยปกติ อัตราการ read มักจะมากกว่า write โดยธรรมชาติ) ดังนั้นทำให้เปลือง CPU กว่า

9

ตอบ: หัดใช้ Module Activator.js

คือผมคิดว่าแบบนี้ครับ
1. จะเก็บ value ใน redis.io แบบ json ผสม array เก็บไว้ คิดว่าจะใช้ email address เป็น fieldkey ในรูปแบบการเก็บแบบ hashes เช่นผมอาจตั้งชื่อ database ว่า user แล้วใช้ poomrin@gmail.com เป็น fieldkey ที่ 1 แล้วพอมีคนอื่นก็ทำในรูปแบบเดียวกัน คือ fieldkey ที่ 2 ไปเรื่อยๆ
2. หลังจากนั้น ผมก็สร้าง form ขึ้นมา เวลากรอกเสร็จก จะส่งค่าทั้งหมดใส่ไว้ใน json แล้วก็ insert ค่าไปเลย
3. หากต้องมีการแก้ไข ผมก็ในมันดึงค่าที่อยู่ใน key user จาก field ที่กำหนดตามชื่อ email address มา ซึ่งเป็นแบบ json นั่นแหละ แล้วทำการเก็ไขโดย form ซึ่งอาจจะสร้างฟอร์ม เพื่อแก้ไขบางจุด แล้ว update files ทั้งหมดทับลงไปใน fieldkey เดิม ซึ่งการแก้ไขผมคิดจะใช้ onLoad ใส่ตรง body tag แล้วให้มันดึงค่าเดิมออกมา

ส่วนเรื่อง security นี่ผมพอเข้าใจละครับ แปลว่า เราป้องกันส่วน write ให้ดีก็พอแล้ว เช่น ป้องกันด้าน client side ด้วย validator.js ส่วน server side ก็ป้องกัน ด้วยการใช้ typeof และเพิ่มด้วย tool อื่นๆ ที่ใช้เช็ค validate ได้ เพราะว่า user อาจจะปิด javascript ในการใช้ก็เป็นได้

10

ตอบ: หัดใช้ Module Activator.js

เป็นการออกแบบที่ผิดครับ
เพราะว่า redis มีข้อจำกัดเรื่องประสิทธิภาพ เมื่อขนาดของแต่ละ key ใหญ่เกินกว่าจุดๆหนึ่ง (จุดนี้เรา config เปลี่ยนได้ แต่ถ้าจำไม่ผิด เดิมๆจะตั้งเอาไว้ที่ 1024 bytes ต่อ 1 key)

ให้นึกย้อนไปที่จุดเริ่มต้นก่อนนะครับ ว่า redis คือ key-value memory database ดังนั้น การทำงานในลักษณะโครงสร้างที่ซับซ้อนของ redis จึงไม่ใช่สิ่งที่เหมาะสม คือทำได้แต่ไม่เหมาะ ลองนึกภาพ เอารถ toyota vios มาขนคนจำนวน 20 คนน่ะครับ ถ้างานเราเป็นการขนคนทีละ 20 คนเราอาจจะต้องเลือกรถ กระบะที่มาที่นั่งด้านหลังรถ หรือว่า รถ2แถว 6 ล้อแทนหรือเปล่า จึงจะเหมาะกับงาน

ถ้างานที่เล่ามา ผมแนะนำ mongodb เหมาะกว่า

แต่อย่างไรก็ดี ถ้าจะใช้ redis ทำงานแบบนี้ ผมแนะนำให้ทำแบบนี้ครับ
1.สร้างชุด index ขึ้นมา โดยใช้ data type เป็น sets เพราะว่า มีขั้นตอนการ test for existence ด้วย O(1) เท่านั้น โดยในนี้ให้บรรจุอีเมลของสมาชิกเอาไว้ เช่น ชื่อ user:all_email ข้างในมี member xxx:yyy.com
2. ข้อมูลที่จะเก็บ ที่ใช้ผูกกับ อีเมลแต่ละคนให้สร้าง key แยกกัน โดยอาศัยอีเมลเป็น key เลย เช่น key
user:xxx@yyy.com เป็นต้น แล้วใช้ data type เป็น hash ข้อมูลข้างในก็คือข้อมูลของ email แต่ละคนนั่นเอง จะเป็น plain text , json ก็แล้วแต่

เท่านี้เองครับ
ถ้ากรณีต้องการเปลี่ยนชื่ออีเมล ทำได้ง่ายมากครับ

1.สร้าง key ใหม่ด้วยอีเมลใหม่ เช่น user:[email2]
2.อ่าน value จาก key เก่า user:[emails] เข้าใส่ key ใหม่ user:[email2]
3.add email ใหม่ เข้า user:all_email
4.ลบ email เก่า ออกจาก user:all_email
5.ลบ key เก่าออก user:[emails]

ส่วนการเช็ค ก็ไปเช็คกับ member ของ user:all_email แทน เพื่อความรวดเร็วครับ

เท่านี้ เราก็จะยังรักษาสภาพการทำงานแบบ key value ของ redis เอาไว้ได้ครับ และทำงานได้เร็ว ทั้งการตรวจสอบ การเรียกข้อมูล การแก้ไขข้อมูล

เพิ่มเติม เรื่องนี้ไม่ใช่เรื่อง logic แต่เป็นเรื่อง architecture ครับ ถ้า architecture ผิด logic ก้จะออกมาแบบยากๆ และสุดท้ายคือระบบได้ไม่เร็วดั่งใจต้องการ
อีกอยาก ผมไม่แนะนำ redis กับการเก็บข้อมูลแบบถาวรนะครับ ใช้งาน redis ให้เผื่อใจเอาไว้ในกรณี server crash , ไฟดับ , ไฟตก ว่าเราจะพร้อมที่จะเสียข้อมูลเหล่านั้นไปถาวร เพราะมันคือ database memory ครับ (การเขียนลง disk เป็นการทำงานแบบ interval เท่านั้น ไม่ตรงไปตรงมาเหมือน MySQL ที่เขียนทุกคำสั่งลง disk ในทันทีครับ)

11

ตอบ: หัดใช้ Module Activator.js

ตอนแรกผมก็ไม่รู้ว่า MongoDB ทำงานแบบไหน แต่บังเอิญมันเหมือนที่ผมคิด หลังจากที่คุณบีบอกมา ผมเลยไปอ่าน document มันดูครับ

อันนี้เป็นความคิดที่อาจไม่ลึกนักเพราะผมไม่มีประสบการณ์

http://docs.mongodb.org/manual/tutorial … ing-field/

ผมใช้วิธีทำ id โดยลักษณะแบบลิ้งค์นี้ จากนั้นก็ทำงานแบบปกติ แต่ว่า เมื่อต้องการ write ข้อมูลเมื่อไหร่ ผมใช้  การเขียนพร้อมกัน 2 แบบ คือ แบบ MongoDB และ mySQL พร้อมกันไปเลย โดยอ้างอิงจาก id ที่ทำการอัพเดทเข้าไป ตัว MongoDB จะมีการ dump ข้อมูลอาจเป็นวันละครั้ง เช่นเที่ยงคืนหรือตีสอง โดยใช้ corn job แล้วเมื่อไหร่ไฟดับ ผมก็เอาข้อมูลที่เก็บใน mySQL มาอัพเดท โดยอาจเทียบกับเวลาที่ไฟฟ้าดับ แล้วใส่ข้อมูลทับลงไปตาม id นั้นๆ แบบนี้เป็นไปได้ไหมครับ เพราะว่า mySQL ก็จำเป็นต้องเก็บค่าเป็นแบบ json ด้วยถึงจะเอาไปทับที่เดิมได้นะครับ

เพิ่มเติม พึ่งอ่านมาครับ MongoDB มีระบบ backup คล้าย redis.io อยู่ครับ

MongoDB flushes writes to disk on a regular interval. In the default configuration, MongoDB writes data to the main data files on disk every 60 seconds and commits the journal roughly every 100 milliseconds. These values are configurable with the journalCommitInterval and syncdelay.

แล้ว mySQL ก็เก็บค่าเป็น json ไม่ได้ด้วย ต้องเก็บแยกชิ้นแล้วก็รวมเอาตาม format ที่ต้องการใช้ไหมครับ เพราะฉะนั้นเลิกคิดที่จะเก็บคู่กันไปได้เลย

แก้ไขล่าสุดโดย poomrin (2014-02-19 09:45:50)

12

ตอบ: หัดใช้ Module Activator.js

ถ้าให้ความน่าเชื่อถือ MongoDB น่าเชื่อถือว่า redis ครับ คือไฟตกไฟดับ ก็ยังเก็บข้อมูลได้ดีกว่า redis เพราะว่าตัวมันเกิดมาอยู่ตรงกลางๆ ระหว่าง ความเร็ว กับความน่าเชื่อถือของข้อมูล
ดังนั้น ถ้าจะเก็บถาวรใน mongo ก็ได้ และมี MySQL backup อีกชั้นก็สุดยอดครับ

การออกแบบหลาย layer แบบนี้ ปัจจุบัน น้อยคนนัก ที่จะทำได้ เพราะว่ามันยุ่งยาก เยอะ และดูแลลำบาก แต่สิ่งที่ได้รับกลับมาคือ ประสิทธิภาพที่สูงกว่ารันระบบเดียวมาก และข้อมูลก็ยังมีความน่าเชื่อถือด้วย และตัวคุณเองจะเป็นที่ต้องการของตลาด เพราะว่าคนที่ทำให้ระบบทำงานได้มีเยอะ แต่คนที่ทำให้ระบบทำงานได้ดี มีน้อยครับ

สำหรับเรื่องการแลกเปลี่ยนข้อมูลกันระหว่าง MongoDB <> MySQL อันนี้ ออกแบบ และเขียนได้หลายรูปแบบครับ แบบ on the fly ก็คือ คือเช็ค MongoDB ไม่มีก็อ่านใน MySQL ถ้าไม่มีอีกก็แปลว่าไม่พบเลย สร้างใหม่ ก็ว่ากันไป แต่หากไม่พบใน MongoDB แต่พบใน MySQL ก็อ่านจาก MySQL ขึ้นมาเก็บใน MongoDB แล้ว set expire เอาไว้ ก็ได้เหมือนกัน อันนี้ก็เป็นแนวทางหนึ่ง

อีกแนวทางหนึ่ง ก็เวลาเขียน ให้เขียนลงสองที่พร้อมกัน หรือเขียนลง MySQL แล้วอ่านจาก My ใส่ Mongo ก็ได้ เพราะว่าการ dump ข้อมูลระหว่างกัน มันจะใช้เวลาพอสมควรเมื่อขนาดข้อมูลโดยรวมของเรามีขนาดใหญ่ระดับหนึ่ง

แต่ว่าข้อมูลพื้นฐานบางอย่าง อาจจะต้องมีการ reload วันละครั้ง อันนั้นก็แยกกันไปอีกเรื่อง

เรื่องพวกนี้ มันมีปัจจัยเข้ามาเกี่ยวข้องเยอะแล้วครับ

เช่น
1.traffic เว็บเป็นอย่างไร
2.ต้องการความเร็วมากแค่ไหน
3.ต้องการความเชื่อถือมากแค่ไหน
4.concurrent เป็นอย่างไร
5.การประมวลผลปัจจุบันเป็นอย่างไร คอขวดที่ตรงไหน
6.ข้อมูลอะไรที่สำคัญ อะไรไม่สำคัญ
7.อัตราการ read/write เป็นอย่างไร

ถึงจะบอกได้ชัด ว่าการ link layer ของข้อมูลแต่ละชั้นควรเป็นแบบไหน

ส่วนเรื่องการเก็บเป็น json ใน MySQL ไม่รองรับก็จริงแต่เราแก้ปัญหาได้ด้วยการ แปลง json decode > array > serialize แล้วค่อยเอาไปเก็บใน MySQL ก็ได้ครับ เวลาใช้ ก็แปลงกลับ serialize > array > json encode ก็ได้เหมือนกันผมเคยใช้แบบนี้ แต่มีข้อเสียคือ MySQL เอามาเป็น condition ตอน search เพราะมันก็มองเป็น string ธรรมดา (แต่จริงๆ เก็บเป็น redis ก็ search ไม่ได้เหมือนกัน ต้อง retrieve ออกมาอ่านอย่างเดียว) แต่ถ้าเป็น mongo search ได้ครับ เพราะว่ามันทำงานเป็น BSON type ซึ่งคล้าย JSON นั่นล่ะครับ

เล่าสั้นๆเรื่อง mongo db มันจะมองโครงสร้างการเก็บเป็นแบบ document base ครับ ให้เราลองนึกถึงหน้าเว็บสักหน้า เราก็จะมี head title, header 2 , header3 , article , comment อีกหลายอันต่อไปยาวๆ จากคนหลายๆคน แบบนี้ mongo สามารถเก็บข้อมูลทั้งหมดได้ ใน record เดียว เท่านั้น (รวมทั้ง comment หลายๆอันยาวๆด้วยก็ตาม) ซึ่งจะต่างจาก MySQL ที่เราต้อง split comment แยก table ออก เวลาจะใช้ก็จับมาประกอบรวมกัน นี่คือ จุดแข็งของ MongoDB ครับ ทำงานได้ทั้ง memory + disk แต่ถ้าจำไม่ผิด ปกติจะทำงานบน disk นะครับ เป็น default

ทั้งนี้ ผมไม่แน่ใจว่า งานที่คุณกำลังทำ มี request หนักมากขนาดไหน เพราะว่าบางที back to basic ครับ คือ เขียนใส่แต่ MySQL อย่างเดียวเลยก็มากพอแล้วครับ (อาจจะเอา mysql memory table มาใช้ก็ช่วยได้เหมือนกัน) เพียงแต่เราต้องออกแบบ MySQL ให้เหมาะกับข้อมูลของเราสักหน่อย ผมยังไม่เจองานไหนที่เอามาใช้ออกแบบ MySQL ไม่ได้เลยนะครับ เพราะว่า RMDB มันเป็น database แบบพื้นฐานสุดๆที่ระบบส่วนใหญ่เค้าก็ทำงานกันได้โดยไม่มีปัญหาไม่ว่าโครงสร้างจะซับซ้อนขนาดไหนก็ตาม (แต่ความเร็วที่ได้นั่นก็เป็นอีกเรื่องหนึ่งครับ 5555)

งานที่ผมทำ ก็ผสมกันไป ส่วนใหญ่จะ mysql ล้วน เว้นแต่พวกที่ process หนักมากๆหรือเยอะมากๆ แบบนี้จะเอา redis+node เข้ามาช่วยเลยครับ คือ redis + MySQL และผมก็ตั้งสมมุติฐานว่า ถ้าไฟจะตกได้ตลอดเวลา จะทำอย่างไรกับข้อมูลให้ยังคงมีอยู่ และถูกต้องครับ หรือบางงาน ก็เป็นส่วนที่ต้อง process ทุกๆ 250ms และ process แต่ละครั้ง ก็ต้องจบใน 10ms ไม่เกินนี้ อะไรแบบนี้ครับ ถึงจะเอา redis เข้ามาใช้ (ยาวไปถึง lua ใน redis เลย ซึ่งน้อยคนที่จะใช้)

ลองค่อยๆออกแบบดูก่อนครับ มันมีหลายแนวทางให้เราทำ เลือกเอาที่เหมาะกับงานมากที่สุดครับ

อ้อ เรื่องการออกแบบ จะมาพร้อมกับประสบการณ์ครับ ดังนั้น ทำเยอะๆ เจอเยอะๆ แล้วจะได้ประสบการณ์เยอะก็จะออกแบบแก้ปัญหาได้ดีขึ้น

13

ตอบ: หัดใช้ Module Activator.js

ผมบังเอิญไม่อ่านเคสของพันธ์ทิพย์ ที่มีคนใช้ต่อวันเยอะมากๆ โดยมันจะหนักไปปทาง topic ใหม่ๆ ก็จะมีคนอ่าน คอมเม้น หรือว่าเริ่ม topic ใหม่ แล้วเค้าก็เปลี่ยนจาก mySQL มาเป็น mongoDB นี่แหละครับ แต่โจทย์เค้ามีอยู่ว่าจะกระจายน้ำหนักอย่างไรไปในแต่ละ server ให้กระจายๆ กันออกไป เพื่อให้ไม่หนักในเครื่องเดียว ผมก็เลยสงสัยว่า อ้าวถ้าเป็น topic เดียวกัน แล้วกระจายออกคอมเม้นท์ออกไปหลายๆ server แบบนี้แล้วมา render มันจะไม่ช้าเหรอ แต่ว่าถ้ามีคนดู topic นั้นเป็นแสนต่อวันละ จะเก็บเป็น topic + comments ไว้ใน record เดียว หรือไว้ในเครื่องเดียวมันจะไหวไหมนะ (ถ้าผมเป็นคนเขียน (คิดไปตามเรื่องเรื่อยเปื่่อย) ผมก็จะกระจายเหมือนกันแต่ กระจายแค่เขียนโปรแกรมเอาไว้จ่าย topic ออกไปในแต่ละ Server อาจจะใช้ตัวเลขแล้วให้หาญเลข แล้วเอาเศษ ส่งไปตาม Server นั้นๆ ) ส่วน topic เดียวกันส่งคอมเม้นท์ไปคนละ Server ผมคิดไม่ออกว่ามันจะเร็วได้อย่างไร (เห็นว่าทาง MongoDB มี shading ต้องตั้งค่าให้ตัวนึงเป็นตัวแม่คอยบอกว่าเรียกอันนี้มาให้ไปหาที่ server ลูกตัวไหนได้ด้วย ไม่แน่ใจว่าให้กำหนดขนาดของข้อมูล เมื่อถึงระดับก็ให้ย้ายไปอีกที่ตามนี้หรือเปล่านะครับ)

เพิ่มเติม ผมได้อ่านมาว่า ในแต่ละ document นั้นจะมีขนาดได้ไม่เกิน 4MB นี่ถ้าผมไม่ให้ใส่ ลายเซ็น หรือรูปภาพโดยตรง อาศัยลิงค์แทน คุณบีคิดว่า ผมจำเป็นต้องแยก ระหว่าง topic กับ comment ออกจากกันอยู่ไหมครับ (ในความคิดผมา text อย่างเดียว 4MB ผมไม่เคยเห็นเลย ยกเว้นต้องเป็นหนังสือตั้งใหญ่เลยมั๊ง)

แก้ไขล่าสุดโดย poomrin (2014-02-20 09:03:23)

14

ตอบ: หัดใช้ Module Activator.js

เมื่อวานตอบเอาไว้ยาวมาก สงสัยกดผิดล่ะมั้งเนี่ย วันนี้มาดูอีกที ไม่เห็น  sad

สรุปเป็นสั้นๆแทนแล้วกันครับ เอาเท่าที่จำความได้

เรื่องของ pantip จริงๆ เค้าใช้ sharding 3 เครื่อง แต่ไม่ได้กำหนดรูปแบบการ sharding คือให้มันทำงานอัตโนมัติ แต่ผลออกมาคือมันเบ้ไปหนักเครื่องนึง อีกเครื่องเบา อีกเครื่องก็กลางๆ แต่ไม่ใช่เรื่องการแยกเก็บคำถามตอบคนละ server ครับ น่าจะเก็บใน record เดียวของ mongo นั่นแหล่ะ

ส่วนที่คุณบอกว่า จะหารก่อนเอาเศษไประบุเครื่อง ก็คือวิธีที่เค้าเรียกว่า key sharding กรณีนี้ประกันได้ว่า 3 เครื่องได้เท่ากันแน่นอน เพราะสูตรมันลงตัว
แต่คุณต้องคิดเผื่อว่า เมื่อวันนึงต้องการเพิ่มเครื่องที่ 4 จะทำอย่างไรให้เรียกข้อมูลเดิม จากเครื่องที่ถูกต้องได้
สมมุติ id 6 เมื่อมี สามเครื่องคือ 6%3 = 0 และ id 9 9%3 = 0 แต่เมื่อมี  4เครื่องคือ 6%4=2 แต่ 9%4=1 อะไรแบบนี้ครับมันจะหาข้อมูลเก่าได้อย่างไร

ก็เลยมีอีกวิธีที่เค้าเรียกว่า range sharding คือแต่ละ server เก็บเป็น range แทน

ส่วนเรื่อง 1 record 4MB มันมากกว่าสิ่งที่ pantip ปรากฏอยู่ในปัจจุบันมากครับ เรื่องนี้ยังไม่น่าเป็นห่วงอะไร

15

ตอบ: หัดใช้ Module Activator.js

ผมติดปัญหารัน server ไม่ผ่านดังนี้ครับ ไปหาที่ไหนลองทำตามแล้วก็ไม่ได้

Sat Feb 22 15:32:28.114 [initandlisten] ERROR: Insufficient free space for journal files
Sat Feb 22 15:32:28.114 [initandlisten] Please make at least 3379MB available in /var/lib/mongo/journal or use --smallfiles

16

ตอบ: หัดใช้ Module Activator.js

จริงๆผมไม่ได้ใช้ mongo นะครับ เคยลง เทสใช้พักนึง แล้วก็เลิกใช้ครับ เพราะว่าเว็บใหญ่ๆ ในโลกเราหลายเว็บก็เคยใช้ แล้วเลิกใช้ไปเหมือนกัน ลองหาอ่านดูครับ เหมือนว่า พอใช้กับงาน production ที่ใหญ่ๆมากๆ data เยอะๆ แล้วมัน crash บ่อย อะไรประมาณนี้ครับ มันมีหลายเว็บหลายเคส ต่างๆกันไป

สำหรับ error ดังกล่าว google ดูก็ได้คำตอบนะครับ
http://stackoverflow.com/questions/1458 … -space-for

แต่ดูๆ เหมือนว่า พื้นที่ harddisk กำลังจะเต็มนะครับ
ลองสั่ง

df -h

แล้วได้ว่ายังไงครับ

17

ตอบ: หัดใช้ Module Activator.js

ผมแก้มันได้แล้วครับโดยตอน รัน Centos ผมใช้วิธี fixed harddisk แทนที่จะเป็นแบบ flexible แต่ดันมาติดอีกตรงที่ browser ไม่ยอม run express ทั้งๆ ที่ terminal ก็ปกติดี linux firewall ก็เพิ่มส่วน port แล้วแท้ๆ ลองปิด window firewall อีกต่างหากยังเปิดไม่ได้เลย (วันนี้ผมเครียดนะเนี่ย)

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

18

ตอบ: หัดใช้ Module Activator.js

ผมก็เป็นคนที่เคยไม่รู้มาก่อน

แล้วเวลาหาข้อมูลก็จะพบกับบทความที่สั้นๆ อยากได้แบบนี้ เราก็ทำ A  B  C เสร็จแล้ว
ผมก็เกิดคำถามว่า แล้วไอ A เนี่ย มันมาจากไหน ยังไง? เพราะลองเองแล้วมันไม่มี ไม่ได้ ไม่เหมือน
เพราะอย่างที่เล่ามานั่นแหล่ะครับ ไม่ค่อยมีใครเค้าบอกกัน อาจจะเพราะมันเสียเวลา หรือเค้าอาจจะคิดว่า คนที่จะทำ A B C ได้ ก็ต้องรู้อยู่แล้ว ว่าจะหา A ได้มายังไง

แต่ความจริงๆคือ ไม่รู้!

ดังนั้นบทความผมก็มักจะย้อนหลังลงไปแบบเบื้องต้น หรือเล่าเรื่องที่มันเป็นน้ำๆ (พื้นฐาน) ซะเยอะหน่อย คนที่รู้อยู่แล้ว ไม่อ่าน ผมก็ไม่ได้คิดอะไรมาก

ส่วนเรื่อง express อันนี้ ไม่แน่ใจเท่าไร
แต่ตรวจสอบได้ด้วยการรัน node ธรรมดา http port 80 (หรือ port เดียวกับที่ express รันอยู่) เพื่อเช็คว่า เราไม่ได้เจอปัญหาเรื่อง connection ถ้าผ่านเรื่อง connection ก็แปลว่าเป็นเรื่องการ config เองครับ
ทั้งนี้ 1 port รันได้ 1 งานเท่านั้นนะครับ

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

19

ตอบ: หัดใช้ Module Activator.js

ผมบื้อมากเลยครับ ว่าทำไมมันไม่ได้ซะที เพราะว่าผม ตอนใส่ url ผมใส่ http://localhost:3000 แต่ว่าผมใช้ใน Centos นี่น่า เพิ่งเกตตอนอาบน้ำตอนกลับจากทำงาน มันต้อง http://192.168.1.79:3000 แบบนี้ก็ได้เลย แต่ตอนนี้ติดปัญหานิดหน่อยครับเครื่อง connect กับ database

mongod --dbpath c:\node\nodetest2\data

ตัวอย่างมันเป็นแบบนี้นะครับ แลัวผมจะ ต่อยังไงเนี่ย ของผมตอน cd มันเป็นแบบนี้ /usr/local/src/node-v0.10.26/mongo

20

ตอบ: หัดใช้ Module Activator.js

เป็นเรื่องปกติ ที่หลายครั้ง เราจะแก้ปัญหาได้ เมื่อเราพักจากการอยู่หน้าจอครับ เคยเจอครั้งนึง ฝันเป็น solution แม้เจ้า สุดๆ

ตามที่อ้างอิง นั่นเป็น windows ถ้าตอนนี้ รันบน linux ก็หาให้เจอครับ ว่า mongo config ให้เก็บ database เอาไว้ที่ไหน ก็เอาpath ใส่เอาไว้

เค้าบอกว่า ถ้าค่า default จะอยู่ที่
/data/db/

ลองไปส่องๆดูว่ามีหรือเปล่าครับ
หรืออาจจะเป็น
/srv/mongodb, /var/lib/mongodb or /opt/mongodb
ถ้ายังไม่เจอ

อ้างอิงมาจาก http://docs.mongodb.org/manual/referenc … n-options/ ในส่วน dbpath ครับ

21

ตอบ: หัดใช้ Module Activator.js

ได้แล้วละครับ ผมทำแบบนี้

mongod --dbpath /usr/local/src/node-v0.10.26/mongo/data

ก่อนหน้านั้นผมเคยเอา data ไปไว้ที่อื่นมาก่อน แล้ว data นั้นก็ย้ายมาอยู่ที่ใหม่ด้วยคำสั่งนี้ด้วย

ส่วนใน express.js ผมก็เชื่อมมันด้วย

var mongo = require('mongoskin');
var db = mongo.db("mongodb://localhost:27017/nodetest2", {native_parser:true});

แค่นี้ครับ ตอนนี้ผมก็มีอะไรๆ ไว้เล่นสนุกๆ ละครับ ขอบคุณมาก เพราะว่าผมงงไม่เคยจับ Linux มาก่อนเลย

ต่อไปจะหัดเล่นกับ mongodb database ก่อนครับ แล้วถ้าเข้าใจดีแล้วจะลองทำแบบ + ด้วย mySQL รวมเข้าไปด้วย แล้วจะถามใหม่นะครับ

22

ตอบ: หัดใช้ Module Activator.js

ผมอยากใช้ Async.js กับ mongoDB ครับ จากตัวอย่างที่่ใช้ร่วม database เป็นแบบนี้

app.get('/user/:userId', function(req, res, next) {
    var locals = {};
    var userId = req.params.userId;
    async.parallel([
        //Load user
        function(callback) {
            db.get('users', userId, function(err, user) {
                if (err) return callback(err);
                locals.user = {
                    name: user.name,
                    email: user.email,
                    bio: user.bio
                };
                callback();
            });
        },
        //Load posts
        function(callback) {
            db.query('posts', {userId: userId}, function(err, posts) {
                if (err) return callback(err);
                locals.posts = posts;
                callback();
            });
        }
    ], function(err) { //This function gets called after the two tasks have called their "task callbacks"
        if (err) return next(err); //If an error occured, we let express/connect handle it by calling the "next" function
        //Here locals will be populated with 'user' and 'posts'
        res.render('user-profile', locals);
    });
});

แต่ว่าผมอยากใช้ mongodb ต่อแทนผมควรทำแบบไหนครับ อันนี้ตัวอย่างจากการดึง database

exports.adduser = function(db) {
  return function(req, res) {
    db.collection('userlist').insert(req.body, function(err, result){
      res.send(
        (err === null) ? { msg: '' } : { msg: err }
      );
    });
  }
};

ตรงส่วน callback อันที่ 2 ผมทำแบบนี้ได้ไหมครับ

app.get('/user/:userId', function(req, res, next) {
    var locals = {};
    var userId = req.params.userId;
    async.parallel([
        //Load user
        function(callback) {
            db.get('users', userId, function(err, user) {
                if (err) return callback(err);
                locals.user = {
                    name: user.name,
                    email: user.email,
                    bio: user.bio
                };
                callback();
            });
        },
        //Load posts
        function(callback) {
            function(db) {
  return function(req, res) {
    db.collection('userlist').insert(req.body, function(err, result){
      res.send(
        (err === null) ? { msg: '' } : { msg: err }
      );
    });
  }
};
    ], function(err) { //This function gets called after the two tasks have called their "task callbacks"
        if (err) return next(err); //If an error occured, we let express/connect handle it by calling the "next" function
        //Here locals will be populated with 'user' and 'posts'
        res.render('user-profile', locals);
    });
});

23

ตอบ: หัดใช้ Module Activator.js

เรื่องแรก การใช้ parallel
โดยปกติ การทำงานของ node.js เป็นแบบ asynchronous อยู่แล้ว นั่นหมายความว่า ไม่จำเป็นต้องใช้ parallel ก็ได้ครับ เพราะมันมีพฤติกรรมใกล้เคียงกันอยู่แล้ว
แต่สิ่งที่ต้องกังวลในการใช้ parallel คือ การที่คำสั่งด้านล่าง ทำเสร็จก่อนด้านบนนั่นล่ะครับ จะเป็น bug อันใหญ่หลวง เพราะหากว่าเราเขียนโดยตั้งใจว่า ให้ส่วนบนเขียนข้อมูลก่อน ส่วนล่างค่อยอ่านข้อมูลมาใช้ทำงานต่อ มันจะพลาด เพราะว่าด้านล่างดันทำงานก่อนด้านบน ตรงนี้ต้องระวัง

จากที่ถาม ต้องถามย้อนกลับไป ว่าทำไมถึงใช้ asynce กับ mongoDB แล้ว function node.js กับ callback ตามปกติ ไม่สามารถใช้งานกับ mongoDB ได้หรือครับ? (ที่ถามเพราะอย่างที่บอกว่า ผมไม่ได้ใช้ mongoDB มานานแล้ว)

ผมว่า อาจจะได้คำตอบ จากการใช้งาน function กับ callback ตามปกติของ javascript ก็ได้นะครับ ลองดูก่อนครับ ว่า function กับ callback แบบปกติของ javascript มันเขียนและทำงานอย่างไร

24

ตอบ: หัดใช้ Module Activator.js

ผมมองว่า async.js เหมือน โปรเจคเมเนเจอร์ครับ เช่นให้ทำงาน a b c นะ ให้เสร็จทั้งหมดก่อน แล้วส่ง action ต่อไป คล้ายกับตัวอย่างข้างล่างนี้ ซึ่งผมคิดว่ามันอ่านได้ง่ายดี ผมคิดว่าอาจจะใช้ 1 set นี้ทำงานสำหรับ 1 หน้า แล้วหากว่าต้องการเพิ่มลูกเล่นอย่างอื่นเพิ่มเติมก็ทำได้ง่ายๆ ไม่ต้อง nested หลายๆ ชั้นนะครับ

app.get('/user/:name', function(req, res, next) {
    var locals = {};
    var name = req.params.name;
    var userId;
    async.series([
        //Load user to get userId first
        function(callback) {
            db.query('users', {name: name}, function(err, users) {
                if (err) return callback(err);
                //Check that a user was found
                if (users.length == 0) {
                    return callback(new Error('No user with name '+name+' found.');
                }
                var user = users[0];
                userId = user.id; //Set the userId here, so the next tasks can access it
                locals.user = {
                    name: user.name,
                    email: user.email,
                    bio: user.bio
                };
                callback();
            });
        },
        //Load posts and photos in parallel (won't be called before task 1's "task callback" has been called)
        function(callback) {
            async.parallel([
                //Load posts
                function(callback) {
                    db.query('posts', {userId: userId}, function(err, posts) {
                        if (err) return callback(err);
                        locals.posts = posts;
                        callback();
                    });
                },
                //Load photos
                function(callback) {
                    db.query('photos', {userId: userId}, function(err, photos) {
                        if (err) return callback(err);
                        locals.photos = [];
                        //Iterate over each photo
                        async.forEach(photos, function(photo, callback) {
                            fs.exists(photo.path, function(exists) {
                                //Only add the photo to locals.photos if it exists on disk
                                if (exists) {
                                    locals.photos.push(photo);
                                }
                                callback();
                            });
                        }, callback);
                    });
                }
            ], callback); //Remember to put in the second series task's "task callback" as the "final callback" for the async.parallel operation
        }
    ], function(err) { //This function gets called after the two series tasks have called their "task callbacks"
        if (err) return next(err);
        //Here locals will be populated with 'user', 'posts' and 'photos
        res.render('user-profile', locals);
    });
});

25

ตอบ: หัดใช้ Module Activator.js

โอเค เข้าใจแล้วครับ
ตามที่ถามมา และยกตัวอย่างมา เกือบถูกทั้งหมดแล้ว ยกเว้นเรื่องเดียว คือ คุณลืมเรียก callback() หลังจากที่ได้ result จาก mongoDB แล้วเท่านั้นครับ