จัดโครงสร้างฐานข้อมูลของคุณ

คู่มือนี้ครอบคลุมแนวคิดหลักบางประการเกี่ยวกับสถาปัตยกรรมข้อมูลและ แนวทางปฏิบัติในการจัดโครงสร้างข้อมูล JSON ในฐานข้อมูลเรียลไทม์ของ Firebase

การสร้างฐานข้อมูลที่มีโครงสร้างอย่างเหมาะสมต้องอาศัยความรอบคอบพอสมควร สิ่งสำคัญที่สุดคือคุณต้องวางแผน วิธีบันทึกข้อมูล และ ที่ดึงออกมาในภายหลังเพื่อให้กระบวนการดังกล่าวง่ายที่สุด

ข้อมูลมีโครงสร้างอย่างไร: เป็นแผนผัง JSON

ข้อมูลทั้งหมดของฐานข้อมูลเรียลไทม์ของ Firebase จะจัดเก็บเป็นออบเจ็กต์ JSON ลองนึกถึง ฐานข้อมูลเป็นแผนผัง JSON ที่โฮสต์บนระบบคลาวด์ ต่างจากฐานข้อมูล SQL ตรงที่ไม่มี ตารางหรือระเบียน เมื่อคุณเพิ่มข้อมูลลงในโครงสร้าง JSON ข้อมูลนั้นจะกลายเป็นโหนดใน โครงสร้าง JSON ที่มีอยู่ ด้วยคีย์ที่เชื่อมโยง คุณสามารถใส่คีย์ของตัวเอง เช่น รหัสผู้ใช้หรือชื่อความหมาย หรืออาจมีการระบุให้คุณโดยใช้ push()

หากคุณสร้างคีย์ของคุณเอง คีย์ทั้งหมดจะต้องเข้ารหัสแบบ UTF-8 และมีขีดจำกัดจำนวนสูงสุด 768 ไบต์ และต้องไม่มีการควบคุม ., $, #, [, ], / หรือ ASCII 0-31 หรือ 127 อักขระ คุณไม่สามารถใช้อักขระควบคุม ASCII ในค่าได้ ด้วยตนเอง อย่างใดอย่างหนึ่ง

ตัวอย่างเช่น พิจารณาแอปพลิเคชันสำหรับแชทที่ช่วยให้ผู้ใช้จัดเก็บ และข้อมูลติดต่อ โปรไฟล์ผู้ใช้ทั่วไปจะอยู่ที่เส้นทาง เช่น /users/$uid ผู้ใช้ alovelace อาจมีรายการฐานข้อมูลที่ ซึ่งจะมีลักษณะดังนี้

{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      "contacts": { "ghopper": true },
    },
    "ghopper": { ... },
    "eclarke": { ... }
  }
}

แม้ว่าฐานข้อมูลจะใช้โครงสร้าง JSON แต่ข้อมูลที่จัดเก็บในฐานข้อมูลสามารถ แสดงเป็นประเภทเนทีฟบางประเภทซึ่งตรงกับประเภท JSON ที่ใช้ได้ เพื่อช่วยให้คุณเขียนโค้ดที่ดูแลรักษาได้มากขึ้น

แนวทางปฏิบัติแนะนำสำหรับโครงสร้างข้อมูล

หลีกเลี่ยงการซ้อนข้อมูล

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

ลองดูตัวอย่างสาเหตุที่ข้อมูลที่ซ้อนกันไม่ถูกต้อง ดังนี้ โครงสร้างที่ซ้อนกัน:

{
  // This is a poorly nested data architecture, because iterating the children
  // of the "chats" node to get a list of conversation titles requires
  // potentially downloading hundreds of megabytes of messages
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "messages": {
        "m1": { "sender": "ghopper", "message": "Relay malfunction found. Cause: moth." },
        "m2": { ... },
        // a very long list of messages
      }
    },
    "two": { ... }
  }
}

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

แยกโครงสร้างข้อมูล

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

{
  // Chats contains only meta info about each conversation
  // stored under the chats's unique ID
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "lastMessage": "ghopper: Relay malfunction found. Cause: moth.",
      "timestamp": 1459361875666
    },
    "two": { ... },
    "three": { ... }
  },

  // Conversation members are easily accessible
  // and stored by chat conversation ID
  "members": {
    // we'll talk about indices like this below
    "one": {
      "ghopper": true,
      "alovelace": true,
      "eclarke": true
    },
    "two": { ... },
    "three": { ... }
  },

  // Messages are separate from data we may want to iterate quickly
  // but still easily paginated and queried, and organized by chat
  // conversation ID
  "messages": {
    "one": {
      "m1": {
        "name": "eclarke",
        "message": "The relay seems to be malfunctioning.",
        "timestamp": 1459361875337
      },
      "m2": { ... },
      "m3": { ... }
    },
    "two": { ... },
    "three": { ... }
  }
}

ตอนนี้คุณสามารถทำซ้ำผ่านรายการห้องแชทได้ด้วยการดาวน์โหลดเฉพาะ 2-3 ไบต์ต่อการสนทนา เรียกข้อมูลเมตาเพื่อแสดงข้อมูลหรือแสดงได้อย่างรวดเร็ว ห้องแชทใน UI ข้อความสามารถดึงข้อมูลแยกกันและแสดงผลเมื่อมาถึง ทำให้ UI ตอบสนอง และรวดเร็วยิ่งขึ้น

สร้างข้อมูลที่ปรับขนาด

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

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

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

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

// An index to track Ada's memberships
{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      // Index Ada's groups in her profile
      "groups": {
         // the value here doesn't matter, just that the key exists
         "techpioneers": true,
         "womentechmakers": true
      }
    },
    ...
  },
  "groups": {
    "techpioneers": {
      "name": "Historical Tech Pioneers",
      "members": {
        "alovelace": true,
        "ghopper": true,
        "eclarke": true
      }
    },
    ...
  }
}

คุณอาจสังเกตเห็นว่ารายการนี้ทำซ้ำข้อมูลบางอย่างโดยจัดเก็บความสัมพันธ์ ภายใต้ทั้งบันทึกของ Ada และใต้กลุ่ม ตอนนี้ alovelace ได้รับการจัดทำดัชนีภายใต้ และ techpioneers แสดงอยู่ในโปรไฟล์ของ Ada ดังนั้นการลบ Ada กลุ่มนั้นต้องมีการอัปเดตใน 2 ที่

นี่เป็นการสำรองที่จำเป็นสำหรับความสัมพันธ์แบบ 2 ทาง ซึ่งช่วยให้คุณ ดึงข้อมูลการเป็นสมาชิกของ Ada ได้อย่างรวดเร็วและมีประสิทธิภาพ แม้เวลาที่แสดงรายชื่อผู้ใช้หรือ กลุ่มขนาดใหญ่ก็ขนาดใหญ่กว่า หลักล้านหรือกฎการรักษาความปลอดภัยของ Realtime Database ป้องกันการเข้าถึงระเบียนบางอย่าง

วิธีนี้เป็นการกลับข้อมูลโดยแสดงรหัสเป็นคีย์และตั้งค่า เป็นจริง ทำให้การตรวจหาคีย์เป็นเรื่องง่ายเหมือนการอ่าน /users/$uid/groups/$group_id และตรวจสอบว่าเป็น null หรือไม่ ดัชนีเร็วขึ้น และมีประสิทธิภาพมากกว่าการค้นหาหรือสแกนหาข้อมูล

ขั้นตอนถัดไป