Showing posts with label Cassandra. Show all posts
Showing posts with label Cassandra. Show all posts

26.9.10

Setup Cassandra cluster in 10 minute

จำลองทำ Cassandra cluster โน็ตไว้กันลืมด้วย

my env:
  • โหนดแรกเป็นเครื่องหลักผมเองใช้ Mac OSX (Leopard) ติดตั้ง Cassandra .65
  • โหนดที่สองจำลองขึ้นมาบน VMWare เป็น Windows XP ติดตั้ง Cassandra .65

กำหนด hostname ให้เรียบร้อยทั้งสองโหนด
โหนดแรกชื่อเครื่อง tofu และ cassvm เป็นของโหนดที่สอง


กำหนด Seed เพื่อให้ Cassandra รู้ว่ามีสมาชิกอยู่ที่ใดกันบ้างและกำหนด ThriftAddress, ListenAddress ทั้งสองโหนด


ถัดมาลองสั่งรันที่โหนดแรกดู (อย่าลืมกำหนด Commitlog และ log4j ก่อนให้เรียบร้อย)




หลังจากนั้นลองรันที่อีกโหนดดูที่เป็น VMWare
จะขึ้นหน้าจอประมาณนี้ และ process hinted handoff ก็เริ่มทำงานไปพร้อมๆ กัน


ok เสร็จเรียบร้อยแล้วครับ ถ้าเข้าใจแนวคิด และทุกอย่างไม่พลาด ไม่น่าเกิน 10 นาทีก็ทดลองอย่างอื่นต่อได้แล้วครับ

21.9.10

CAP theorem

ความสามารถในการขยายตัวของระบบ (Scalable) กับ Service
รูปข้างล่างเป็นจำนวนผู้ใช้บน Facebook


  • ใน Facebook ตอนนี้มีผู้ใช้กว่า 500 ล้านคน
  • ประมาณ 50% ของผู้ใช้ทั้งหมดเข้าใช้งานทุกวัน
  • แต่ละคนใช้งานระบบเฉลี่ยอยู่ที่ 20 นาทีต่อคน
  • ในแต่ละเดือนมี 30 ล้าน content เช่น status update, comments, likes, photo uploads, video uploads, chat messages, inbox messages, group events, fan pages, friend connects, ... ถูกแชร์บน facebook
  • ในแต่ละเดือน application ถูกใช้มากกว่าล้านคน

  • ลองคิดเล่นๆ ว่าเค้าต้องออกแบบระบบยังไงให้ยืดหยุ่นรองรับการขยายตัว เพื่อให้รองรับปริมาณการใช้บริการต่างๆ จะเห็นว่าความสามารถในการขยายตัว จะสัมพันธ์กับสัดส่วนของบริการ และ product ใหม่ๆ ที่จะตามออกมา เหมือนอย่างที่ Google หลังจากค้นคว้าวิจัยเทคโนโลยี GFS, MapReduce, BigTable และถูกนำไปใช้ไม่นานหลังจากนั้นก็มีบริการและ product ใหม่ๆ ออกมา ในสัดส่วนที่เร็วกว่าแต่ก่อนมาก จะเห็นว่าภาพจะเป็นไปในแนวทางนี้ Infrastructure (scalable) -> Services-> Customer need

    Distributed system กับ ทฤษฏี CAP
    ระบบที่มีความสามารถในการขยายตัวส่วนใหญ่จะเป็นอยู่ใน model ของ distributed system เมื่อเราพูดถึง distributed เราจะต้องเจอกับทฤษฏี CAP ซึ่งมันเป็นแนวคิดที่เรียบง่ายแต่ไว้ใช้อธิบายเรื่องที่ซับซ้้อน ตัวทฤษฏี CAP มีต้นกำเนิดมาจาก Dr. Eric Brewer ในปี 2000 และกลายมาเป็นทฤษฏีที่มีผลกระทบต่อ โลก internet ในเวลาต่อมา

    CAP มาจากคำว่า Consistency, Availability, Partition Tolerance ซึ่งมันบอกว่าระบบที่เป็น distributed storage จะสามารถรองรับคุณสมบัติแค่สองคุณสมบัติเท่านั้น นั่นคือเราไม่สามารถเลือกทั้งหมดได้พร้อมๆ กัน ณ เวลาใดเวลาหนึ่ง ซึ่งแต่ละคุณสมบัติมีรายละเอียดดังนี้
  • Consistency ทุก node จะเห็นข้อมูลกันเดียวกันในเวลาเดียวกันไม่ว่าจะเข้าถึงข้อมูลที่ node ใดใน cluster ก็ตาม ดังนั้นข้อมูลจึงมีแค่สองสถานะคือถูก กับไม่ถูก โดย consistency มีสองประเภทคือ strongly consistent และ weak consistency
  • ตัวอย่างของ strongly consistent จะเทียบได้กับ database ที่มีคุณสมบัติ ACID (Atomicity, Consistency, Isolation, Durability)
  • ส่วน weak consistency จะเจอเมื่อเนื้อข้อมูลของเรามีการ replicate กันโดยแต่ละ node ใน cluster ก็จะมีข้อมูลเดียวกันอยู่ แต่อาจไม่ใช่ version ล่าสุด ข้อมูล version ล่าสุดจะถูกเก็บอยู่ใน node ใด node หนึ่งในวง cluster แล้วจะ replicate ข้อมูลล่าสุดออกไป ดังนั้นในตอนสุดท้ายข้อมูลในทุก node ก็จะเป็นข้อมูลชุดเดียวกัน
  • Availability ถึงแม้ node ใด node หนึ่ง fail แต่ก็จะไม่ขัดขวางการทำงานของระบบ ตัวระบบยังคงสามารถให้บริการต่อได้ เท่ากับว่าระบบจะต้องถูกออกแบบให้มี High-Availability เพื่อให้บริการได้ตลอดเวลาไม่ว่าจะเกิดเหตุการณ์ใดๆ ขึ้น เช่นเมื่อมีบาง node failures ขณะที่ software หรือ hardware กำลัง upgrade แต่ระบบโดยรวมยังคงให้บริการได้ต่อไป
  • Partition Tolerance ระบบยังคงต้องทำงานต่อไปถึงแม้ว่าเส้นทางการเชื่อมต่อระหว่าง node จะเสียหาย แต่ client ยังเข้าถึงได้อยู่
  • ถ้าระบบสามารถทนต่อสภาพเช่นนี้ได้ ตัว database ก็จะสามารถทำงานได้ตาม อ่าน, เขียนได้ตามปกติขณะที่ database 2 rack แยกออกจากกัน
  • ถ้าไม่รองรับคุณสมบัตินี้แล้วเราอาจทำการอ่านข้อมูลได้เพียงอย่างเดียว แต่ไม่สามารถที่จะเขียนข้อมูลลงใน database ได้

  • สำหรับ RDBMS ได้คุณสมบัติ CA นั่นคือ Consistency และ Availability แต่ no Partition tolerance นั่นคือเมื่อ host หนึ่ง fail ไป RDBMS ก็จะหยุดการทำงานไปด้วย ดังนั้น RDBMS จึงต้องพยายามการันตีว่าจะต้องให้บริการได้ตลอด (High-Availability) เพราะฉะนั้นจึงไม่เกิดกรณี Partition Tolerance แน่ๆ

    20.9.10

    Cassandra Sorting

    Cassandra ไม่มีความสามารถในการ query เหมือน RDBMS ทั่วไป ดังนั้นเราจึงไม่สามารถกำหนดรูปแบบการเรียงลำดับ ขณะที่ดึงข้อมูลออกมา
    เมื่อเราต้องการดึงข้อมูลแล้วให้มันเรียงลำดับ สิ่งเหล่านั้นเราต้องกำหนดลงใน ColumnFamily แต่ความจริง Cassandra จะเรียงลำดับข้อมูลตั้งแต่ตอนที่ใส่ข้อมูลลงใน cluster และคงที่ลำดับตัวข้อมูลเหล่านั้นไว้ดังนั้นเมื่อเราดึงข้อมูล ตัวข้อมูลเหล่านั้นจะถูกเรียงลำดับโดยอัตโนมัติ
    การเรียงลำดับสามารถกำหนดใน ColumnFamily ด้วย attribute ที่ชื่อ CompareWith ด้วยชนิดข้อมูลดังนี้
    ByteType, UTF8Type, LexicalUUIDType, TimeUUIDType, AsciiType, LongType การเรียงลำดับจะเรียงตาม Column.name และขึ้นอยู่กับชนิดข้อข้อมูลที่เรากำหนด
    ตัวอย่างการ Config เช่น
    
    
    ในกรณีของ SuperColumns เราสามารถใช้ attribute ที่ชื่อ CompareSubcolumnsWith เพื่อกำหนดชนิดข้อมูลเพื่อเรียงลำดับใน Column ที่อยู่ใน SuperColumn ได้
    ตัวอย่างการ Config เช่น
    
    
    
    ตัวอย่าง Column ถ้าเรามีข้อมูลตามนี้
    {name: 123, value: "hello there"},
    {name: 832416, value: "kjjkbcjkcbbd"},
    {name: 3, value: "101010101010"},
    {name: 976, value: "kjjkbcjkcbbd"}
    
    แล้วกำหนด CompareWith เป็นชนิดข้อมูล LongType ข้อมูลใน ColumnFamily จะถูกเรียงดังนี้
    {name: 3, value: "101010101010"},
    {name: 123, value: "hello there"},
    {name: 976, value: "kjjkbcjkcbbd"},
    {name: 832416, value: "kjjkbcjkcbbd"}
    
    แต่ถ้ากำหนด CompareWith เป็นชนิดข้อมูล UTF8Type ข้อมูลใน ColumnFamily จะถูกเรียงดังนี้
    {name: 123, value: "hello there"},
    {name: 3, value: "101010101010"},
    {name: 832416, value: "kjjkbcjkcbbd"},
    {name: 976, value: "kjjkbcjkcbbd"}
    
    ตัวอย่าง SuperColumn ถ้าเรามีข้อมูลดังนี้
    {
     name: "workAddress",  value: {   street: {name: "street", value: "1234 x street"},   city: {name: "city", value: "san francisco"},   zip: {name: "zip", value: "94107"}         } ,  {
    name: "homeAddress",
     value: {   street: {name: "street", value: "1234 x street"},   city: {name: "city", value: "san francisco"},   zip: {name: "zip", value: "94107"}  } }
    
    แล้วกำหนด CompareSubcolumnsWith & CompareWith เป็น UTF8Type จะได้ผลลัพธ์ดังนี้
    {
     name: "homeAddress",  value: {   city: {name: "city", value: "san francisco"},  
    street: {name: "street", value: "1234 x street"},
     zip: {name: "zip", value: "94107"}
    }
    ,  {
    name: "workAddress",
     value: {   city: {name: "city", value: "san francisco"},  
    street: {name: "street", value: "1234 x street"},
     zip: {name: "zip", value: "94107"} }
    }
    

    17.9.10

    Cassandra Data Model #2

    ถัดจากตอนแรก Cassandra Data Model #1 มาว่ากันต่อเรื่อง Data model

    ColumnFamilies
    เราสามารถจัดกลุ่มของ Column ได้ผ่าน ColumnFamily เพื่อให้มองภาพง่ายขึ้นตัวโดยโครงสร้างข้อมูลของ ColumnFamily จะเหมือนกับ table (ใช่แล้วผมหมายถึง table ใน RDBMS หนะแหละครับ) ซึ่งแต่ละ table ก็จะประกอบด้วยกี่ Row ก็ได้ และแต่ละ Row ก็จะเก็บ Column เท่าไหร่ก็ได้
    ความยืดหยุ่นในโครงสร้างมันอยู่ตรงนี้ ให้สังเกตุว่าแต่ละ Row เราจะเก็บกี่ Column ก็ได้เท่ากับว่าเราไม่ต้องมีการกำหนดตายตัวว่าแต่ละ Row จะมีได้แค่เท่านั้นเท่านี้ Column จุดนี้เองจึงทำให้ Cassandra ได้คุณสมบัติ Schemaless
    การเก็บข้อมูลของ Cassandra ตอนเก็บจะมีการเรียงลำดับข้อมูลทุกครั้งก่อนเก็บ ซึ่งการเรียงลำดับจะเรียงตาม key ของแต่ละ row
    การสร้าง ColumnFamily เรากำหนดได้ในไฟล์ storage-conf.xml แต่ในกรณีที่เราต้องการเปลี่ยนแปลง (เพิ่ม, แก้ไข, ลบ) ColumnFamily จำเป็นที่จะต้อง Restart Cassandra process ก่อนนะครับ ถึงจะเห็นผล
    ตัวอย่างการสร้าง ColumnFamily
    <keyspaces>
    <keyspace Name="Posts">  
    <columnfamily CompareWith="UTF8Type" Name="Tags"></ColumnFamily>  
    <columnfamily CompareWith="BytesType" Name="Authors"></ColumnFamily> 
    </Keyspace>
    </Keyspaces>
    
    เราสร้าง ColumnFamily ชื่อ Tags, Authors และกำหนดชนิดข้อมูลที่ใช้เป็น UTF8Type, ByteType ตามลำดับ

    ตัวอย่างโครงสร้างข้อมูลถ้ามองภาพในโลก Java
    public class ColumnFamily{
     Byte[] name;
     Map value;
    }
    ColumnFamily cf = new ColumnFamily();
    cf.name = "AddressBook";
    Map row = new HashMap();
    row.put("firstname", new Column("firstname", "Maomao"));
    row.put("familyname", new Column("familyname", "Mathies"));
    row.put("city", new Column("city", "Leiden"));
    cf.put("person1", row);
    
    Map row = new HashMap();
    row.put("firstname", new Column("firstname", "Maomao"));
    row.put("familyname", new Column("familyname", "Chen"));
    row.put("city", new Column("city", "Leiden"));
    cf.put("person2", row);
    
    ตัวอย่างถ้ามองภาพในโลก JSON
    UserProfile = {
     name: “phamonyut”, 
     value: {
      username: {name: “username”, value: “tofu”, timestamp: 123456789},
      blog: {name: “blog”, value: “http://phamonyut.blogspot.com”, timestamp: 123456789},
      gender: {name: “gender ”, value: “male”, timestamp: 123456789}
     },
     name: “plaumkamon”,
     value: {
      username: {name: “username”, value: “plaumkamon”, timestamp: 123456789},
      blog: {name: “blog”, value: “http://plaumkamon.blogspot.com”, timestamp: 123456789},
      gender: {name: “gender ”, value: “undecided”, timestamp: 123456789},
      age: {name: “age”, value: “25”, timestamp: 123456789},
      email: {name: “email”, value: “plaumkamon@example.com”, timestamp: 123456789}
     }
    }
    
    ด้วยความที่มัน Schemaless ค่าที่เก็บในแต่ละ row จึงแตกต่างกันได้ สังเกตุว่า row ที่สองจะมีเพิ่ม Column age และ name

    Keyspace
    ข้ามมาพูดถึง Keyspace ก่อนเข้าไปที่ SuperColumnFamily ตัว Keyspace เพื่อให้มองภาพง่ายขึ้นเปรียบได้กับ Database ใน RDBMS ซึ่งเป็น “มิติ” แรกของ Cassandra hash ที่เราใช้เวลาดึงข้อมูล (คำว่ามิติแรก ก็จะเทียบได้กับ syntax get[keyspace][columnFamily][...]) โดยภายในจะประกอบด้วย ColumnFamily (ซึ่งเปรียบได้กับ Table ใน RDBMS) ก็เท่ากับเรามีทั้ง Database และ table ที่ใช้เก็บข้อมูลแล้ว ซึ่งแต่ละ application ก็จะมีแค่หนึ่ง keyspace
    แต่อย่างลืมว่า ColumnFamily ไม่ใช่ table จริงๆ และมันไม่มี Join จึงอย่าคาดหวังว่า key ของ row นึงจะต้องมีอยู่ในอีก ColumnFamily นึงด้วย
    เราจะ config Keyspace ในไฟล์ storage-conf.xml
    <keyspaces>
    <keyspace name="Blogs"> 
    ... 
    </Keyspace>
    </Keyspaces>
    
    SuperColumnFamily
    โครงสร้างจะเหมือนกับ ColumnFamily แตกต่างตรงที่ ในแต่ละ row จะเก็บ SuperColumn ตัว map คือ key ที่เป็น name ของแต่ละ SuperColumn และ value คือตัว SuperColumn มัีนเอง
    ตัวอย่างในมุมมองของ JSON
    Posts= {
     name: "Cassandra-data-model", 
     value: {
     name: "Post"
     value: {
      "title": {name: "title", value: "Cassandra data model", timestamp: 123456789},
      "body": {name: "body", value: "Blah Blah ...", timestamp: 123456789},
      "author": {name: "author", value: "Phamonyut", timestamp: 123456789},
     }, 
     name: "Tag"
     value {
      "0": {name: "0", value: "cassandra", timestamp: 123456789},
      "1": {name: "1", value: "nosql", timestamp: 123456789},
     }
     }, 
     name: "Android-programming",
     value: {
     name: "Post"
     value: {
      "title": {name: "title", value: "Android Programming", timestamp: 123456789},
      "body": {name: "body", value: "Blah Blah ...", timestamp: 123456789},
      "author": {name: "author", value: "Plaumkamon", timestamp: 123456789},
      "createdate": {name: "createdate", value: "07/07/2007", timestamp: 123456789}
     }, 
     name: "Tag"
     value {
      "0": {name: "0", value: "Android", timestamp: 123456789},
      "1": {name: "1", value: "Tutorial", timestamp: 123456789},
     }
     }
    }
    

    Cassandra Data Model #1

    [Column | SuperColumn] >- [ColumnFamily | SuperColumnFamily] >- Keyspace/Application >- Cluster

    โครงสร้างข้อมูลของ Cassandra แบ่งออกเป็นสองประเภทหลักๆ ได้แก่ Column และ SuperColumn ซึ่งสองชนิดนี้สามารถจัดกลุ่มรวมกันได้โดยใช้อีกสองประเภทคือ ColumnFamily และ SuperColumnFamily

    Schema
    Cassandra จะเป็นลักษณะของไม่มี schema (schema-less) จะเห็นภาพชัดขึ้นเมื่อพูดถึง Column Family

    Cluster
    หนึ่ง Clusters สามารถเก็บได้หลาย keyspace

    Columns
    โครงสร้างข้อมูลจะประกอบด้วยสามส่วนได้แก่ name, value และ timestamp
    public class Column {
     Byte[] name;
     Byte[] value;
     Long timestamp;
    }
    
    ถ้ามองเป็นโครงสร้างแบบ JSON จะได้รูปแบบดังนี้
    {
     "name": "emailAddress"
     "value": "foo@bar.com"
     "timestamp": 123456789
    }
    
    ค่า name และ value จะเก็บเป็น binary ถึงแม้ว่า application จะใ้ช้ UTF8 ก็ตาม และสังเกตุว่ามีการเก็บค่า timestamp ด้วย ซึ่งมันถูกใช้เพื่อแก้ปัญหาความขัดแย้งกันของข้อมูล โดยจะเกิดเมื่อเรามีข้อมูลเดียวกันสองชุด ค่านี้จะบอกเราว่าข้อมูลชุดใดเป็นเวอร์ชั่นใหม่สุด และข้อมูลเก่าจะถูกแทนที่ด้วยข้อมูลที่ใหม่กว่า
    บางครั้งเวลาเราพูดถึง column มักจะตัด timestamp และมองภาพแค่การจับคู่กันของ name กับ value
    ดังนั้นในเอกสารนี้ส่วนของ timestamp จะถูกตัดออกเพื่อให้อ่านง่ายขึ้น

    SuperColumns
    โครงสร้างข้อมูลของ SuperColumn จะเก็บ name และ value โดย value จะเก็บ map ของ key/Column โดย key จะมีค่าเหมือนกับชื่อของ column ที่มันเก็บอยู่ (มองง่ายๆ SuperColumn จะเก็บ Column อีกที ซึ่งมีมากกว่าหนึ่งก็ได้)
    สิ่งที่แตกต่างระหว่าง Column และ SuperColumn คือ Column.value เก็บชนิดข้อมูลเป็น String แต่ SuperColumn.value เก็บชนิดข้อมูลเป็น map ของ column และ SuperColumn ไม่เก็บ timestamp ซึ่งค่านี้จะเก็บอยู่ใน Column เท่านั้น
    ถ้ามองในมุม Java เราจะเขียนโครงสร้างได้หน้าตาดังนี้ ซึ่งดูแล้วง่ายกว่ามองจากรูปมาก
    public class SuperColumn {
     Byte[] name;
     Map value;
    }
    SuperColumn sc = new SuperColumn();
    sc.name = “person1”;
    sc.value.put(“firstname”, new Column(“firstname”, “Ronald”));
    sc.value.put(“lastname”, new Column(“familyname”, “Mathies”));
    
    ถ้ามองเป็นโครงสร้าง JSON จะได้ดังรูปนี้
    {
     name: “Post”
     value: {
      “title”: {name: “title”, value: “Cassandra data model”, timestamp: 123456789},
      “body”: {name: “body”, value: “Blah Blah ...”, timestamp: 123456789},
      “author”: {name: “author”, value: “Phamonyut”, timestamp: 123456789},
     }, 
     name: “Tag”
     value {
      “0”: {name: “0”, value: “cassandra”, timestamp: 123456789},
      “1”: {name: “1”, value: “nosql”, timestamp: 123456789},
     }
    }