Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

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

คู่มือนี้ครอบคลุมแนวคิดหลักบางประการในสถาปัตยกรรมข้อมูลและแนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดโครงสร้างข้อมูล 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": { ... }
  }
}

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

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

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

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

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

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

// 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 ออกจากกลุ่ม จะต้องมีการอัปเดตในสองแห่ง

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

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

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