خواندن و نوشتن را در مقیاس درک کنید

برای تصمیم‌گیری آگاهانه در مورد معماری برنامه‌های خود برای عملکرد و قابلیت اطمینان بالا، این سند را مطالعه کنید. این سند شامل مباحث پیشرفته Cloud Firestore است. اگر تازه کار با Cloud Firestore را شروع کرده‌اید، به جای آن به راهنمای شروع سریع مراجعه کنید.

Cloud Firestore یک پایگاه داده انعطاف‌پذیر و مقیاس‌پذیر برای توسعه دستگاه‌های تلفن همراه، وب و سرور از فایربیس و Google Cloud است. شروع کار با Cloud Firestore و نوشتن برنامه‌های غنی و قدرتمند بسیار آسان است.

برای اطمینان از اینکه برنامه‌های شما با افزایش اندازه پایگاه داده و ترافیک، همچنان به خوبی عمل می‌کنند، درک مکانیسم خواندن و نوشتن در بک‌اند Cloud Firestore مفید است. همچنین باید تعامل خواندن و نوشتن خود را با لایه ذخیره‌سازی و محدودیت‌های اساسی که ممکن است بر عملکرد تأثیر بگذارند، درک کنید.

برای بهترین شیوه‌ها قبل از معماری برنامه خود، به بخش‌های زیر مراجعه کنید.

درک کامپوننت‌های سطح بالا

نمودار زیر اجزای سطح بالای دخیل در یک درخواست API Cloud Firestore را نشان می‌دهد.

اجزای سطح بالا

SDK و کتابخانه‌های کلاینت Cloud Firestore

Cloud Firestore از SDKها و کتابخانه‌های کلاینت برای پلتفرم‌های مختلف پشتیبانی می‌کند. در حالی که یک برنامه می‌تواند مستقیماً از طریق HTTP و RPC به API Cloud Firestore فراخوانی کند، کتابخانه‌های کلاینت لایه‌ای از انتزاع را برای ساده‌سازی استفاده از API و پیاده‌سازی بهترین شیوه‌ها فراهم می‌کنند. آن‌ها همچنین ممکن است ویژگی‌های اضافی مانند دسترسی آفلاین، حافظه‌های پنهان و غیره را ارائه دهند.

رابط کاربری گوگل (GFE)

این یک سرویس زیرساختی مشترک برای همه سرویس‌های ابری گوگل است. GFE درخواست‌های ورودی را می‌پذیرد و آنها را به سرویس مربوطه گوگل (در این زمینه سرویس Cloud Firestore ) ارسال می‌کند. همچنین قابلیت‌های مهم دیگری از جمله محافظت در برابر حملات انکار سرویس (Denial of Service) را نیز ارائه می‌دهد.

سرویس Cloud Firestore

سرویس Cloud Firestore بررسی‌هایی را روی درخواست API انجام می‌دهد که شامل احراز هویت، مجوز، بررسی سهمیه و قوانین امنیتی است و همچنین تراکنش‌ها را مدیریت می‌کند. این سرویس Cloud Firestore شامل یک کلاینت ذخیره‌سازی است که با لایه ذخیره‌سازی برای خواندن و نوشتن داده‌ها تعامل دارد.

لایه ذخیره‌سازی Cloud Firestore

لایه ذخیره‌سازی Cloud Firestore مسئول ذخیره‌سازی داده‌ها و فراداده‌ها و ویژگی‌های پایگاه داده مرتبط ارائه شده توسط Cloud Firestore است. بخش‌های زیر نحوه سازماندهی داده‌ها در لایه ذخیره‌سازی Cloud Firestore و نحوه مقیاس‌پذیری سیستم را شرح می‌دهند. یادگیری در مورد نحوه سازماندهی داده‌ها می‌تواند به شما در طراحی یک مدل داده مقیاس‌پذیر و درک بهتر بهترین شیوه‌ها در Cloud Firestore کمک کند.

محدوده‌های کلیدی و تقسیم‌بندی‌ها

Cloud Firestore یک پایگاه داده NoSQL و سندگرا است. شما داده‌ها را در اسناد ذخیره می‌کنید که در سلسله مراتبی از مجموعه‌ها سازماندهی شده‌اند. سلسله مراتب مجموعه و شناسه سند برای هر سند به یک کلید واحد ترجمه می‌شوند. اسناد به صورت منطقی ذخیره می‌شوند و توسط این کلید واحد از نظر لغوی مرتب می‌شوند. ما از اصطلاح محدوده کلید برای اشاره به محدوده‌ای از کلیدها که از نظر لغوی به هم پیوسته هستند، استفاده می‌کنیم.

یک پایگاه داده معمولی Cloud Firestore برای قرار گرفتن در یک ماشین فیزیکی واحد بسیار بزرگ است. همچنین سناریوهایی وجود دارد که حجم کار روی داده‌ها برای یک ماشین بسیار سنگین است. برای مدیریت حجم‌های کاری بزرگ، Cloud Firestore داده‌ها را به قطعات جداگانه‌ای تقسیم می‌کند که می‌توانند روی چندین ماشین یا سرورهای ذخیره‌سازی ذخیره و از آنها سرویس‌دهی شوند. این تقسیم‌بندی‌ها در جداول پایگاه داده در بلوک‌هایی از محدوده‌های کلیدی به نام Splits ساخته می‌شوند.

تکثیر همزمان

لازم به ذکر است که پایگاه داده همیشه به صورت خودکار و همزمان در حال تکثیر است. بخش‌های داده دارای کپی‌هایی در مناطق مختلف هستند تا حتی زمانی که یک منطقه غیرقابل دسترسی می‌شود، در دسترس باشند. تکثیر مداوم به نسخه‌های مختلف تقسیم‌بندی توسط الگوریتم Paxos برای اجماع مدیریت می‌شود. یک کپی از هر تقسیم‌بندی به عنوان رهبر Paxos انتخاب می‌شود که مسئول مدیریت نوشتن‌ها در آن تقسیم‌بندی است. تکثیر همزمان به شما این امکان را می‌دهد که همیشه بتوانید آخرین نسخه داده‌ها را از Cloud Firestore بخوانید.

نتیجه کلی این امر، یک سیستم مقیاس‌پذیر و با دسترسی‌پذیری بالا است که صرف نظر از حجم کاری سنگین و در مقیاس بسیار بزرگ، تأخیر کمی را برای خواندن و نوشتن فراهم می‌کند.

طرح داده

Cloud Firestore یک پایگاه داده سند بدون طرح است. با این حال، از نظر داخلی، داده‌ها را عمدتاً در دو جدول به سبک پایگاه داده رابطه‌ای در لایه ذخیره‌سازی خود به شرح زیر قرار می‌دهد:

  • جدول اسناد : اسناد در این جدول ذخیره می‌شوند.
  • جدول شاخص‌ها : ورودی‌های شاخص که امکان دستیابی کارآمد به نتایج را فراهم می‌کنند و بر اساس مقدار شاخص مرتب شده‌اند، در این جدول ذخیره می‌شوند.

نمودار زیر نشان می‌دهد که جداول یک پایگاه داده Cloud Firestore با تقسیم‌بندی‌ها چگونه ممکن است به نظر برسند. تقسیم‌بندی‌ها در سه منطقه مختلف تکرار می‌شوند و هر تقسیم‌بندی یک رهبر Paxos اختصاص داده شده دارد.

طرح داده

تک منطقه‌ای در مقابل چند منطقه‌ای

هنگام ایجاد یک پایگاه داده، باید یک منطقه یا چند منطقه را انتخاب کنید.

یک موقعیت منطقه‌ای واحد، یک موقعیت جغرافیایی خاص است، مانند us-west1 . همانطور که قبلاً توضیح داده شد، بخش‌های مختلف داده‌های یک پایگاه داده Cloud Firestore دارای کپی‌هایی در مناطق مختلف در منطقه انتخاب شده هستند.

یک مکان چند منطقه‌ای شامل مجموعه‌ای تعریف‌شده از مناطق است که در آن‌ها کپی‌های پایگاه داده ذخیره می‌شوند. در یک استقرار چند منطقه‌ای از Cloud Firestore ، دو منطقه دارای کپی‌های کاملی از کل داده‌ها در پایگاه داده هستند. منطقه سوم دارای یک کپی شاهد است که مجموعه کاملی از داده‌ها را نگهداری نمی‌کند، اما در تکثیر شرکت می‌کند. با تکثیر داده‌ها بین چندین منطقه، داده‌ها حتی با از دست دادن کل یک منطقه، برای نوشتن و خواندن در دسترس هستند.

برای اطلاعات بیشتر در مورد مکان‌های یک منطقه، به مکان‌های Cloud Firestore مراجعه کنید.

تک منطقه‌ای در مقابل چند منطقه‌ای

زندگی یک نویسنده در Cloud Firestore را درک کنید

یک کلاینت Cloud Firestore می‌تواند با ایجاد، به‌روزرسانی یا حذف یک سند واحد، داده‌ها را بنویسد. نوشتن در یک سند واحد مستلزم به‌روزرسانی خودکار سند و ورودی‌های شاخص مرتبط با آن در لایه ذخیره‌سازی است. Cloud Firestore همچنین از عملیات خودکار شامل خواندن و/یا نوشتن چندگانه در یک یا چند سند پشتیبانی می‌کند.

برای انواع نوشتن، Cloud Firestore ویژگی‌های ACID (اتمیک بودن، سازگاری، ایزوله بودن و دوام) پایگاه‌های داده رابطه‌ای را ارائه می‌دهد. Cloud Firestore همچنین قابلیت سریال‌سازی را فراهم می‌کند، به این معنی که همه تراکنش‌ها طوری به نظر می‌رسند که گویی به ترتیب سریالی اجرا می‌شوند.

مراحل سطح بالا در یک تراکنش نوشتن

وقتی کلاینت Cloud Firestore با استفاده از هر یک از روش‌های ذکر شده قبلی، یک تراکنش write یا commit صادر می‌کند، این تراکنش به صورت داخلی به عنوان یک تراکنش read-write پایگاه داده در لایه ذخیره‌سازی اجرا می‌شود. این تراکنش Cloud Firestore را قادر می‌سازد تا ویژگی‌های ACID ذکر شده قبلی را ارائه دهد.

به عنوان اولین مرحله از یک تراکنش، Cloud Firestore سند موجود را می‌خواند و جهش‌هایی را که باید در داده‌های جدول Documents ایجاد شود، تعیین می‌کند.

این همچنین شامل انجام به‌روزرسانی‌های لازم در جدول Indexes به شرح زیر است:

  • فیلدهایی که به اسناد اضافه می‌شوند، نیاز به درج‌های مربوطه در جدول Indexes دارند.
  • فیلدهایی که از اسناد حذف می‌شوند، نیاز به حذف‌های متناظر در جدول شاخص‌ها دارند.
  • فیلدهایی که در اسناد تغییر می‌کنند، نیاز به حذف (برای مقادیر قدیمی) و درج (برای مقادیر جدید) در جدول شاخص‌ها دارند.

برای محاسبه جهش‌های ذکر شده قبلی، Cloud Firestore پیکربندی نمایه‌سازی پایگاه داده را می‌خواند. پیکربندی نمایه‌سازی اطلاعات مربوط به نمایه‌های یک پایگاه داده را ذخیره می‌کند. Cloud Firestore از دو نوع نمایه استفاده می‌کند: تک فیلدی و مرکب. برای درک دقیق نمایه‌های ایجاد شده در Cloud Firestore ، به بخش انواع نمایه در Cloud Firestore مراجعه کنید.

پس از محاسبه جهش‌ها، Cloud Firestore آنها را درون یک تراکنش جمع‌آوری کرده و سپس آن را ثبت می‌کند.

درک تراکنش نوشتن در لایه ذخیره‌سازی

همانطور که قبلاً بحث شد، نوشتن در Cloud Firestore شامل یک تراکنش خواندن-نوشتن در لایه ذخیره‌سازی است. بسته به چیدمان داده‌ها، یک نوشتن ممکن است شامل یک یا چند تقسیم‌بندی باشد، همانطور که در چیدمان داده‌ها مشاهده می‌شود.

در نمودار زیر، پایگاه داده Cloud Firestore دارای هشت بخش (با علامت‌های ۱ تا ۸) است که در سه سرور ذخیره‌سازی مختلف در یک منطقه واحد میزبانی می‌شوند و هر بخش در ۳ (یا بیشتر) منطقه مختلف تکثیر می‌شود. هر بخش دارای یک رهبر Paxos است که ممکن است برای بخش‌های مختلف در منطقه متفاوتی باشد.

<کلاس span= تقسیم پایگاه داده Cloud Firestore">

یک پایگاه داده Cloud Firestore را در نظر بگیرید که مجموعه Restaurants را به صورت زیر دارد:

مجموعه رستوران

کلاینت Cloud Firestore با به‌روزرسانی مقدار فیلد priceCategory ، تغییر زیر را در سندی در مجموعه Restaurant درخواست می‌کند.

تغییر به یک سند در مجموعه

مراحل سطح بالای زیر، آنچه را که به عنوان بخشی از نوشتن اتفاق می‌افتد، شرح می‌دهند:

  1. یک تراکنش خواندن-نوشتن ایجاد کنید.
  2. سند restaurant1 را در مجموعه Restaurants از جدول Documents از لایه ذخیره‌سازی بخوانید.
  3. شاخص‌های سند را از جدول شاخص‌ها (Indexes) بخوانید.
  4. جهش‌هایی که باید روی داده‌ها اعمال شوند را محاسبه کنید. در این مورد، پنج جهش وجود دارد:
    • M1: سطر مربوط به restaurant1 در جدول Documents را به‌روزرسانی کن تا تغییر مقدار فیلد priceCategory را منعکس کند.
    • M2 و M3: سطرهای مربوط به مقدار قدیمی priceCategory را در جدول Indexes برای شاخص‌های نزولی و صعودی حذف کنید.
    • M4 و M5: ردیف‌های مربوط به مقدار جدید priceCategory را در جدول Indexes برای شاخص‌های نزولی و صعودی وارد کنید.
  5. این جهش‌ها را مرتکب شوید.

کلاینت ذخیره‌سازی در سرویس Cloud Firestore به دنبال تقسیم‌بندی‌هایی می‌گردد که کلیدهای ردیف‌هایی که باید تغییر کنند را در اختیار دارند. بیایید حالتی را در نظر بگیریم که تقسیم‌بندی ۳ به M1 و تقسیم‌بندی ۶ به M2-M5 سرویس می‌دهد. یک تراکنش توزیع‌شده وجود دارد که شامل همه این تقسیم‌بندی‌ها به عنوان شرکت‌کننده است. تقسیم‌بندی‌های شرکت‌کننده همچنین ممکن است شامل هر تقسیم‌بندی دیگری باشد که داده‌ها قبلاً به عنوان بخشی از تراکنش خواندن-نوشتن از آن خوانده شده‌اند.

مراحل زیر آنچه را که به عنوان بخشی از کامیت اتفاق می‌افتد، شرح می‌دهد:

  1. کلاینت ذخیره‌سازی یک کامیت صادر می‌کند. این کامیت شامل جهش‌های M1 تا M5 است.
  2. بخش‌های ۳ و ۶، شرکت‌کنندگان این تراکنش هستند. یکی از شرکت‌کنندگان به عنوان هماهنگ‌کننده انتخاب می‌شود، مانند بخش ۳. وظیفه هماهنگ‌کننده این است که مطمئن شود تراکنش به صورت خودکار در بین همه شرکت‌کنندگان انجام می‌شود یا لغو می‌گردد.
    • رهبران کپی‌شده از این تقسیم‌بندی‌ها، مسئول کارهایی هستند که توسط شرکت‌کنندگان و هماهنگ‌کنندگان انجام می‌شود.
  3. هر شرکت‌کننده و هماهنگ‌کننده، یک الگوریتم Paxos را با کپی‌های مربوط به خود اجرا می‌کند.
    • رهبر الگوریتم Paxos را با کپی‌ها اجرا می‌کند. اگر اکثر کپی‌ها با ok to commit حد نصاب حاصل می‌شود.
    • سپس هر شرکت‌کننده وقتی آماده شد ، هماهنگ‌کننده را مطلع می‌کند (مرحله اول از تایید دو مرحله‌ای). اگر هر شرکت‌کننده‌ای نتواند تراکنش را تایید کند، کل تراکنش aborts .
  4. زمانی که هماهنگ‌کننده از آمادگی همه شرکت‌کنندگان، از جمله خودش، مطلع شد، نتیجه accept تراکنش را به همه شرکت‌کنندگان اطلاع می‌دهد (مرحله دوم از تایید دو مرحله‌ای). در این مرحله، هر شرکت‌کننده تصمیم تایید را در حافظه پایدار ثبت می‌کند و تراکنش تایید می‌شود.
  5. هماهنگ‌کننده به کلاینت ذخیره‌سازی در Cloud Firestore پاسخ می‌دهد که تراکنش انجام شده است. به طور موازی، هماهنگ‌کننده و همه شرکت‌کنندگان جهش‌ها را روی داده‌ها اعمال می‌کنند.

چرخه حیات کامیت

وقتی پایگاه داده Cloud Firestore کوچک باشد، ممکن است اتفاق بیفتد که یک تقسیم واحد، تمام کلیدها را در جهش‌های M1-M5 در اختیار داشته باشد. در چنین حالتی، فقط یک شرکت‌کننده در تراکنش وجود دارد و کامیت دو مرحله‌ای که قبلاً ذکر شد، لازم نیست، بنابراین نوشتن‌ها سریع‌تر انجام می‌شوند.

در چند منطقه می‌نویسد

در یک استقرار چند منطقه‌ای، گسترش کپی‌ها در مناطق مختلف، دسترسی‌پذیری را افزایش می‌دهد، اما با هزینه عملکرد همراه است. ارتباط بین کپی‌ها در مناطق مختلف، زمان رفت و برگشت طولانی‌تری طول می‌کشد. از این رو، تأخیر پایه برای عملیات Cloud Firestore در مقایسه با استقرارهای تک منطقه‌ای کمی بیشتر است.

ما کپی‌ها را به گونه‌ای پیکربندی می‌کنیم که رهبری برای تقسیم‌بندی‌ها همیشه در منطقه اصلی باقی بماند. منطقه اصلی، منطقه‌ای است که ترافیک از آن به سرور Cloud Firestore وارد می‌شود. این تصمیم رهبری، تأخیر رفت و برگشت در ارتباط بین کلاینت ذخیره‌سازی در Cloud Firestore و رهبر کپی (یا هماهنگ‌کننده برای تراکنش‌های چند تقسیمی) را کاهش می‌دهد.

هر نوشتن در Cloud Firestore همچنین شامل تعامل با موتور بلادرنگ در Cloud Firestore است. برای اطلاعات بیشتر در مورد پرس‌وجوهای بلادرنگ، به درک پرس‌وجوهای بلادرنگ در مقیاس مراجعه کنید.

زندگی یک خواننده را در Cloud Firestore درک کنید

این بخش به بررسی خواندن‌های مستقل و غیر بلادرنگ در Cloud Firestore می‌پردازد. سرور Cloud Firestore به صورت داخلی اکثر این کوئری‌ها را در دو مرحله اصلی مدیریت می‌کند:

  1. یک اسکن تک محدوده‌ای روی جدول ایندکس‌ها
  2. جستجوهای نقطه‌ای در جدول اسناد بر اساس نتیجه اسکن قبلی
ممکن است کوئری‌های خاصی در Cloud Firestore وجود داشته باشند که به پردازش کمتر یا بیشتری نیاز داشته باشند (برای مثال، کوئری‌های IN).

خواندن داده‌ها از لایه ذخیره‌سازی به صورت داخلی با استفاده از یک تراکنش پایگاه داده انجام می‌شود تا از خواندن‌های مداوم اطمینان حاصل شود. با این حال، برخلاف تراکنش‌های مورد استفاده برای نوشتن، این تراکنش‌ها قفل نمی‌گیرند. در عوض، آنها با انتخاب یک برچسب زمانی و سپس اجرای همه خواندن‌ها در آن برچسب زمانی کار می‌کنند. از آنجایی که آنها قفلی دریافت نمی‌کنند، تراکنش‌های خواندن-نوشتن همزمان را مسدود نمی‌کنند. برای اجرای این تراکنش، کلاینت ذخیره‌سازی در Cloud Firestore یک محدوده برچسب زمانی را مشخص می‌کند که به لایه ذخیره‌سازی می‌گوید چگونه یک برچسب زمانی خواندن را انتخاب کند. نوع محدوده برچسب زمانی انتخاب شده توسط کلاینت ذخیره‌سازی در Cloud Firestore توسط گزینه‌های خواندن برای درخواست خواندن تعیین می‌شود.

درک تراکنش خواندن در لایه ذخیره‌سازی

این بخش انواع خواندن‌ها و نحوه پردازش آنها در لایه ذخیره‌سازی در Cloud Firestore را شرح می‌دهد.

خوانش‌های قوی

به طور پیش‌فرض، خواندن‌های Cloud Firestore کاملاً سازگار هستند. این سازگاری قوی به این معنی است که یک خواندن Cloud Firestore آخرین نسخه از داده‌ها را برمی‌گرداند که منعکس کننده تمام نوشتن‌هایی است که تا شروع خواندن انجام شده‌اند.

خواندن تکی و تقسیم شده

کلاینت ذخیره‌سازی در Cloud Firestore به دنبال Splitهایی می‌گردد که کلیدهای سطرهای خوانده شده را در اختیار دارند. فرض کنید که نیاز به خواندن Split 3 از بخش قبلی دارد. کلاینت درخواست خواندن را به نزدیکترین replica ارسال می‌کند تا تأخیر رفت و برگشت را کاهش دهد.

در این مرحله، بسته به کپی انتخاب شده، موارد زیر ممکن است رخ دهد:

  • درخواست خواندن به یک کپی از رهبر (منطقه A) می‌رود.
    • از آنجایی که رهبر همیشه به‌روز است، مطالعه می‌تواند مستقیماً ادامه یابد.
  • درخواست خواندن به یک کپی غیر رهبر (مانند منطقه B) می‌رود.
    • ممکن است Split 3 از طریق وضعیت داخلی خود بداند که اطلاعات کافی برای ارائه خدمات خواندن دارد و Split این کار را انجام می‌دهد.
    • Split 3 مطمئن نیست که آیا آخرین داده‌ها را دیده است یا خیر. پیامی به Leader می‌فرستد تا مهر زمانی آخرین تراکنشی را که برای ارائه خواندن نیاز دارد، درخواست کند. پس از اعمال آن تراکنش، خواندن می‌تواند ادامه یابد.

سپس Cloud Firestore پاسخ را به کلاینت خود برمی‌گرداند.

خواندن چند قسمتی

در شرایطی که خواندن‌ها باید از چندین تقسیم‌بندی انجام شوند، همین مکانیزم در تمام تقسیم‌بندی‌ها اتفاق می‌افتد. پس از بازگشت داده‌ها از تمام تقسیم‌بندی‌ها، کلاینت ذخیره‌سازی در Cloud Firestore نتایج را ترکیب می‌کند. سپس Cloud Firestore با این داده‌ها به کلاینت خود پاسخ می‌دهد.

خواندن‌های تکراری

خواندن قوی حالت پیش‌فرض در Cloud Firestore است. با این حال، به دلیل ارتباطی که ممکن است با رهبر مورد نیاز باشد، هزینه تأخیر بالقوه بالاتری دارد. اغلب برنامه Cloud Firestore شما نیازی به خواندن آخرین نسخه داده‌ها ندارد و این قابلیت با داده‌هایی که ممکن است چند ثانیه قدیمی باشند، به خوبی کار می‌کند.

در چنین حالتی، کلاینت می‌تواند با استفاده از گزینه‌های read_time read، خواندن‌های قدیمی را دریافت کند. در این حالت، خواندن‌ها به گونه‌ای انجام می‌شوند که داده‌ها در read_time بوده‌اند و به احتمال زیاد نزدیکترین کپی، از قبل وجود داده‌ها در زمان read_time مشخص شده را تأیید کرده است. برای عملکرد به طور قابل توجهی بهتر، ۱۵ ثانیه یک مقدار قدیمی بودن معقول است. حتی برای خواندن‌های قدیمی، ردیف‌های ارائه شده با یکدیگر سازگار هستند.

از نقاط داغ دوری کنید

تقسیم‌بندی‌ها در Cloud Firestore به طور خودکار به قطعات کوچک‌تری تقسیم می‌شوند تا در صورت نیاز یا زمانی که فضای کلید گسترش می‌یابد، کار ارائه ترافیک را به سرورهای ذخیره‌سازی بیشتر توزیع کنند. تقسیم‌بندی‌های ایجاد شده برای مدیریت ترافیک اضافی، حتی اگر ترافیک از بین برود، حدود ۲۴ ساعت حفظ می‌شوند. بنابراین اگر افزایش ناگهانی ترافیک وجود داشته باشد، تقسیم‌بندی‌ها حفظ می‌شوند و هر زمان که لازم باشد، تقسیم‌بندی‌های بیشتری معرفی می‌شوند. این مکانیسم‌ها به پایگاه‌های داده Cloud Firestore کمک می‌کنند تا تحت بار ترافیکی یا اندازه پایگاه داده فزاینده، به طور خودکار مقیاس‌بندی شوند. با این حال، محدودیت‌هایی وجود دارد که باید از آنها آگاه باشید، همانطور که در زیر توضیح داده شده است.

تقسیم فضای ذخیره‌سازی و بار زمان می‌برد و افزایش سریع ترافیک ممکن است باعث تأخیر زیاد یا خطاهای فراتر از مهلت شود که معمولاً به عنوان نقاط حساس شناخته می‌شوند، در حالی که سرویس در حال تنظیم است. بهترین روش این است که عملیات را در سراسر محدوده کلیدی توزیع کنید، در حالی که ترافیک را روی یک مجموعه در پایگاه داده با ۵۰۰ عملیات در ثانیه افزایش می‌دهید. پس از این افزایش تدریجی، هر پنج دقیقه ترافیک را تا ۵۰٪ افزایش دهید. این فرآیند قانون ۵۰۰/۵۰/۵ نامیده می‌شود و پایگاه داده را در موقعیت مقیاس‌پذیری بهینه برای پاسخگویی به حجم کار شما قرار می‌دهد.

اگرچه تقسیم‌بندی‌ها با افزایش بار به طور خودکار ایجاد می‌شوند، Cloud Firestore می‌تواند یک محدوده کلیدی را فقط تا زمانی که یک سند واحد را با استفاده از مجموعه‌ای اختصاصی از سرورهای ذخیره‌سازی تکثیر شده ارائه می‌دهد، تقسیم کند. در نتیجه، حجم زیاد و پایدار عملیات همزمان روی یک سند واحد ممکن است منجر به ایجاد یک نقطه اتصال (hotspot) در آن سند شود. اگر با تأخیرهای زیاد و پایدار در یک سند واحد مواجه شدید، باید اصلاح مدل داده خود را برای تقسیم یا تکثیر داده‌ها در چندین سند در نظر بگیرید.

خطاهای رقابتی زمانی اتفاق می‌افتند که چندین عملیات سعی می‌کنند همزمان یک سند را بخوانند و/یا بنویسند.

مورد خاص دیگری از هات‌اسپاتینگ زمانی اتفاق می‌افتد که یک کلید به صورت متوالی افزایش/کاهش یابنده به عنوان شناسه سند در Cloud Firestore استفاده شود و تعداد عملیات در ثانیه به طور قابل توجهی بالا باشد. ایجاد تقسیم‌بندی‌های بیشتر در اینجا کمکی نمی‌کند زیرا موج ترافیک به سادگی به تقسیم‌بندی تازه ایجاد شده منتقل می‌شود. از آنجایی که Cloud Firestore به طور خودکار تمام فیلدهای سند را به طور پیش‌فرض فهرست‌بندی می‌کند، چنین هات‌اسپات‌های متحرکی نیز می‌توانند در فضای فهرست‌بندی برای یک فیلد سند که حاوی مقداری به صورت متوالی افزایش/کاهش یابنده مانند یک مهر زمانی است، ایجاد شوند.

توجه داشته باشید که با پیروی از رویه‌های ذکر شده در بالا، Cloud Firestore می‌تواند بدون نیاز به تنظیم هیچ پیکربندی، برای ارائه حجم کاری دلخواه بزرگ، مقیاس‌پذیر باشد.

عیب‌یابی

Cloud Firestore ابزار Key Visualizer را به عنوان یک ابزار تشخیصی ارائه می‌دهد که برای تجزیه و تحلیل الگوهای استفاده و عیب‌یابی مشکلات هات‌اسپات طراحی شده است.

قدم بعدی چیست؟

،

برای تصمیم‌گیری آگاهانه در مورد معماری برنامه‌های خود برای عملکرد و قابلیت اطمینان بالا، این سند را مطالعه کنید. این سند شامل مباحث پیشرفته Cloud Firestore است. اگر تازه کار با Cloud Firestore را شروع کرده‌اید، به جای آن به راهنمای شروع سریع مراجعه کنید.

Cloud Firestore یک پایگاه داده انعطاف‌پذیر و مقیاس‌پذیر برای توسعه دستگاه‌های تلفن همراه، وب و سرور از فایربیس و Google Cloud است. شروع کار با Cloud Firestore و نوشتن برنامه‌های غنی و قدرتمند بسیار آسان است.

برای اطمینان از اینکه برنامه‌های شما با افزایش اندازه پایگاه داده و ترافیک، همچنان به خوبی عمل می‌کنند، درک مکانیسم خواندن و نوشتن در بک‌اند Cloud Firestore مفید است. همچنین باید تعامل خواندن و نوشتن خود را با لایه ذخیره‌سازی و محدودیت‌های اساسی که ممکن است بر عملکرد تأثیر بگذارند، درک کنید.

برای بهترین شیوه‌ها قبل از معماری برنامه خود، به بخش‌های زیر مراجعه کنید.

درک کامپوننت‌های سطح بالا

نمودار زیر اجزای سطح بالای دخیل در یک درخواست API Cloud Firestore را نشان می‌دهد.

اجزای سطح بالا

SDK و کتابخانه‌های کلاینت Cloud Firestore

Cloud Firestore از SDKها و کتابخانه‌های کلاینت برای پلتفرم‌های مختلف پشتیبانی می‌کند. در حالی که یک برنامه می‌تواند مستقیماً از طریق HTTP و RPC به API Cloud Firestore فراخوانی کند، کتابخانه‌های کلاینت لایه‌ای از انتزاع را برای ساده‌سازی استفاده از API و پیاده‌سازی بهترین شیوه‌ها فراهم می‌کنند. آن‌ها همچنین ممکن است ویژگی‌های اضافی مانند دسترسی آفلاین، حافظه‌های پنهان و غیره را ارائه دهند.

رابط کاربری گوگل (GFE)

این یک سرویس زیرساختی مشترک برای همه سرویس‌های ابری گوگل است. GFE درخواست‌های ورودی را می‌پذیرد و آنها را به سرویس مربوطه گوگل (در این زمینه سرویس Cloud Firestore ) ارسال می‌کند. همچنین قابلیت‌های مهم دیگری از جمله محافظت در برابر حملات انکار سرویس (Denial of Service) را نیز ارائه می‌دهد.

سرویس Cloud Firestore

سرویس Cloud Firestore بررسی‌هایی را روی درخواست API انجام می‌دهد که شامل احراز هویت، مجوز، بررسی سهمیه و قوانین امنیتی است و همچنین تراکنش‌ها را مدیریت می‌کند. این سرویس Cloud Firestore شامل یک کلاینت ذخیره‌سازی است که با لایه ذخیره‌سازی برای خواندن و نوشتن داده‌ها تعامل دارد.

لایه ذخیره‌سازی Cloud Firestore

لایه ذخیره‌سازی Cloud Firestore مسئول ذخیره‌سازی داده‌ها و فراداده‌ها و ویژگی‌های پایگاه داده مرتبط ارائه شده توسط Cloud Firestore است. بخش‌های زیر نحوه سازماندهی داده‌ها در لایه ذخیره‌سازی Cloud Firestore و نحوه مقیاس‌پذیری سیستم را شرح می‌دهند. یادگیری در مورد نحوه سازماندهی داده‌ها می‌تواند به شما در طراحی یک مدل داده مقیاس‌پذیر و درک بهتر بهترین شیوه‌ها در Cloud Firestore کمک کند.

محدوده‌های کلیدی و تقسیم‌بندی‌ها

Cloud Firestore یک پایگاه داده NoSQL و سندگرا است. شما داده‌ها را در اسناد ذخیره می‌کنید که در سلسله مراتبی از مجموعه‌ها سازماندهی شده‌اند. سلسله مراتب مجموعه و شناسه سند برای هر سند به یک کلید واحد ترجمه می‌شوند. اسناد به صورت منطقی ذخیره می‌شوند و توسط این کلید واحد از نظر لغوی مرتب می‌شوند. ما از اصطلاح محدوده کلید برای اشاره به محدوده‌ای از کلیدها که از نظر لغوی به هم پیوسته هستند، استفاده می‌کنیم.

یک پایگاه داده معمولی Cloud Firestore برای قرار گرفتن در یک ماشین فیزیکی واحد بسیار بزرگ است. همچنین سناریوهایی وجود دارد که حجم کار روی داده‌ها برای یک ماشین بسیار سنگین است. برای مدیریت حجم‌های کاری بزرگ، Cloud Firestore داده‌ها را به قطعات جداگانه‌ای تقسیم می‌کند که می‌توانند روی چندین ماشین یا سرورهای ذخیره‌سازی ذخیره و از آنها سرویس‌دهی شوند. این تقسیم‌بندی‌ها در جداول پایگاه داده در بلوک‌هایی از محدوده‌های کلیدی به نام Splits ساخته می‌شوند.

تکثیر همزمان

لازم به ذکر است که پایگاه داده همیشه به صورت خودکار و همزمان در حال تکثیر است. بخش‌های داده دارای کپی‌هایی در مناطق مختلف هستند تا حتی زمانی که یک منطقه غیرقابل دسترسی می‌شود، در دسترس باشند. تکثیر مداوم به نسخه‌های مختلف تقسیم‌بندی توسط الگوریتم Paxos برای اجماع مدیریت می‌شود. یک کپی از هر تقسیم‌بندی به عنوان رهبر Paxos انتخاب می‌شود که مسئول مدیریت نوشتن‌ها در آن تقسیم‌بندی است. تکثیر همزمان به شما این امکان را می‌دهد که همیشه بتوانید آخرین نسخه داده‌ها را از Cloud Firestore بخوانید.

نتیجه کلی این امر، یک سیستم مقیاس‌پذیر و با دسترسی‌پذیری بالا است که صرف نظر از حجم کاری سنگین و در مقیاس بسیار بزرگ، تأخیر کمی را برای خواندن و نوشتن فراهم می‌کند.

طرح داده

Cloud Firestore یک پایگاه داده سند بدون طرح است. با این حال، از نظر داخلی، داده‌ها را عمدتاً در دو جدول به سبک پایگاه داده رابطه‌ای در لایه ذخیره‌سازی خود به شرح زیر قرار می‌دهد:

  • جدول اسناد : اسناد در این جدول ذخیره می‌شوند.
  • جدول شاخص‌ها : ورودی‌های شاخص که امکان دستیابی کارآمد به نتایج را فراهم می‌کنند و بر اساس مقدار شاخص مرتب شده‌اند، در این جدول ذخیره می‌شوند.

نمودار زیر نشان می‌دهد که جداول یک پایگاه داده Cloud Firestore با تقسیم‌بندی‌ها چگونه ممکن است به نظر برسند. تقسیم‌بندی‌ها در سه منطقه مختلف تکرار می‌شوند و هر تقسیم‌بندی یک رهبر Paxos اختصاص داده شده دارد.

طرح داده

تک منطقه‌ای در مقابل چند منطقه‌ای

هنگام ایجاد یک پایگاه داده، باید یک منطقه یا چند منطقه را انتخاب کنید.

یک موقعیت منطقه‌ای واحد، یک موقعیت جغرافیایی خاص است، مانند us-west1 . همانطور که قبلاً توضیح داده شد، بخش‌های مختلف داده‌های یک پایگاه داده Cloud Firestore دارای کپی‌هایی در مناطق مختلف در منطقه انتخاب شده هستند.

یک مکان چند منطقه‌ای شامل مجموعه‌ای تعریف‌شده از مناطق است که در آن‌ها کپی‌های پایگاه داده ذخیره می‌شوند. در یک استقرار چند منطقه‌ای از Cloud Firestore ، دو منطقه دارای کپی‌های کاملی از کل داده‌ها در پایگاه داده هستند. منطقه سوم دارای یک کپی شاهد است که مجموعه کاملی از داده‌ها را نگهداری نمی‌کند، اما در تکثیر شرکت می‌کند. با تکثیر داده‌ها بین چندین منطقه، داده‌ها حتی با از دست دادن کل یک منطقه، برای نوشتن و خواندن در دسترس هستند.

برای اطلاعات بیشتر در مورد مکان‌های یک منطقه، به مکان‌های Cloud Firestore مراجعه کنید.

تک منطقه‌ای در مقابل چند منطقه‌ای

زندگی یک نویسنده در Cloud Firestore را درک کنید

یک کلاینت Cloud Firestore می‌تواند با ایجاد، به‌روزرسانی یا حذف یک سند واحد، داده‌ها را بنویسد. نوشتن در یک سند واحد مستلزم به‌روزرسانی خودکار سند و ورودی‌های شاخص مرتبط با آن در لایه ذخیره‌سازی است. Cloud Firestore همچنین از عملیات خودکار شامل خواندن و/یا نوشتن چندگانه در یک یا چند سند پشتیبانی می‌کند.

برای انواع نوشتن، Cloud Firestore ویژگی‌های ACID (اتمیک بودن، سازگاری، ایزوله بودن و دوام) پایگاه‌های داده رابطه‌ای را ارائه می‌دهد. Cloud Firestore همچنین قابلیت سریال‌سازی را فراهم می‌کند، به این معنی که همه تراکنش‌ها طوری به نظر می‌رسند که گویی به ترتیب سریالی اجرا می‌شوند.

مراحل سطح بالا در یک تراکنش نوشتن

وقتی کلاینت Cloud Firestore با استفاده از هر یک از روش‌های ذکر شده قبلی، یک تراکنش write یا commit صادر می‌کند، این تراکنش به صورت داخلی به عنوان یک تراکنش read-write پایگاه داده در لایه ذخیره‌سازی اجرا می‌شود. این تراکنش Cloud Firestore را قادر می‌سازد تا ویژگی‌های ACID ذکر شده قبلی را ارائه دهد.

به عنوان اولین مرحله از یک تراکنش، Cloud Firestore سند موجود را می‌خواند و جهش‌هایی را که باید در داده‌های جدول Documents ایجاد شود، تعیین می‌کند.

این همچنین شامل انجام به‌روزرسانی‌های لازم در جدول Indexes به شرح زیر است:

  • فیلدهایی که به اسناد اضافه می‌شوند، نیاز به درج‌های مربوطه در جدول Indexes دارند.
  • فیلدهایی که از اسناد حذف می‌شوند، نیاز به حذف‌های متناظر در جدول شاخص‌ها دارند.
  • فیلدهایی که در اسناد تغییر می‌کنند، نیاز به حذف (برای مقادیر قدیمی) و درج (برای مقادیر جدید) در جدول شاخص‌ها دارند.

برای محاسبه جهش‌های ذکر شده قبلی، Cloud Firestore پیکربندی نمایه‌سازی پایگاه داده را می‌خواند. پیکربندی نمایه‌سازی اطلاعات مربوط به نمایه‌های یک پایگاه داده را ذخیره می‌کند. Cloud Firestore از دو نوع نمایه استفاده می‌کند: تک فیلدی و مرکب. برای درک دقیق نمایه‌های ایجاد شده در Cloud Firestore ، به بخش انواع نمایه در Cloud Firestore مراجعه کنید.

پس از محاسبه جهش‌ها، Cloud Firestore آنها را درون یک تراکنش جمع‌آوری کرده و سپس آن را ثبت می‌کند.

درک تراکنش نوشتن در لایه ذخیره‌سازی

همانطور که قبلاً بحث شد، نوشتن در Cloud Firestore شامل یک تراکنش خواندن-نوشتن در لایه ذخیره‌سازی است. بسته به چیدمان داده‌ها، یک نوشتن ممکن است شامل یک یا چند تقسیم‌بندی باشد، همانطور که در چیدمان داده‌ها مشاهده می‌شود.

در نمودار زیر، پایگاه داده Cloud Firestore دارای هشت بخش (با علامت‌های ۱ تا ۸) است که در سه سرور ذخیره‌سازی مختلف در یک منطقه واحد میزبانی می‌شوند و هر بخش در ۳ (یا بیشتر) منطقه مختلف تکثیر می‌شود. هر بخش دارای یک رهبر Paxos است که ممکن است برای بخش‌های مختلف در منطقه متفاوتی باشد.

<کلاس span= تقسیم پایگاه داده Cloud Firestore">

یک پایگاه داده Cloud Firestore را در نظر بگیرید که مجموعه Restaurants را به صورت زیر دارد:

مجموعه رستوران

کلاینت Cloud Firestore با به‌روزرسانی مقدار فیلد priceCategory ، تغییر زیر را در سندی در مجموعه Restaurant درخواست می‌کند.

تغییر به یک سند در مجموعه

مراحل سطح بالای زیر، آنچه را که به عنوان بخشی از نوشتن اتفاق می‌افتد، شرح می‌دهند:

  1. یک تراکنش خواندن-نوشتن ایجاد کنید.
  2. سند restaurant1 را در مجموعه Restaurants از جدول Documents از لایه ذخیره‌سازی بخوانید.
  3. شاخص‌های سند را از جدول شاخص‌ها (Indexes) بخوانید.
  4. Compute the mutations to be made to the data. In this case, there are five mutations:
    • M1: Update the row for restaurant1 in the Documents table to reflect the change in value of the priceCategory field.
    • M2 and M3: Delete the rows for the old value of priceCategory in the Indexes table for descending and ascending indexes.
    • M4 and M5: Insert the rows for the new value of priceCategory in the Indexes table for descending and ascending indexes.
  5. Commit these mutations.

The storage client in the Cloud Firestore service looks up the splits that owns the keys of the rows to be changed. Let's consider a case where Split 3 serves M1, and Split 6 serves M2-M5. There is a distributed transaction, involving all these splits as participants . The participant splits may also include any other split from which data was read earlier as part of the read-write transaction.

The following steps describe what happens as part of the commit:

  1. The storage client issues a commit. The commit contains the mutations M1-M5.
  2. Splits 3 and 6 are the participants in this transaction. One of the participants is chosen as the coordinator , such as Split 3. The job of the coordinator is to make sure the transaction either commits or aborts atomically across all participants.
    • The leader replicas of these splits are responsible for work done by the participants and coordinators.
  3. Each participant and coordinator runs a Paxos algorithm with their respective replicas.
    • The leader runs a Paxos algorithm with the replicas. Quorum is achieved if most of the replicas reply with an ok to commit response to the leader.
    • Each participant then notifies the coordinator when they are prepared (first phase of two-phase commit). If any participant cannot commit the transaction, the whole transaction aborts .
  4. Once the coordinator knows all participants, including itself, are prepared, it communicates the accept transaction outcome to all the participants (second phase of two-phase commit). In this phase, each participant records the commit decision to stable storage and the transaction is committed.
  5. The coordinator responds to the storage client in Cloud Firestore that the transaction has been committed. In parallel, the coordinator and all the participants apply the mutations to the data.

Commit lifecycle

When the Cloud Firestore database is small, it may happen that a single split owns all the keys in the mutations M1-M5. In such a case, there is only one participant in the transaction and the two-phase commit mentioned earlier is not required, thus making the writes faster.

Writes in multi-region

In a multi-region deployment, the spread of replicas across regions increases availability, but comes with a performance cost. The communication between replicas in different regions takes longer round trip times. Hence, the baseline latency for Cloud Firestore operations is slightly more compared to single region deployments.

We configure the replicas in a way that leadership for splits always stays in the primary region. The primary region is the one from which traffic is incoming to the Cloud Firestore server. This decision of leadership reduces the round-trip delay in communication between the storage client in Cloud Firestore and the replica leader (or coordinator for multi-split transactions).

Each write in Cloud Firestore also involves some interaction with the real-time engine in Cloud Firestore . For more information about real-time queries, see Understand real-time queries at scale .

Understand the life of a read in Cloud Firestore

This section delves into standalone, non-realtime reads in Cloud Firestore . Internally, the Cloud Firestore server handles most of these queries in two major stages:

  1. A single range scan over the Indexes table
  2. Point lookups in the Documents table based on the result of the earlier scan
There may be certain queries that require less processing or more processing (for example, IN queries) in Cloud Firestore .

The data reads from the storage layer are internally done by using a database transaction to ensure consistent reads. However, unlike the transactions used for writes, these transactions do not take locks. Instead, they work by choosing a timestamp, then executing all reads at that timestamp. Since they do not acquire locks, they do not block concurrent read-write transactions. To execute this transaction, the storage client in Cloud Firestore specifies a timestamp bound, which tells the storage layer how to choose a read timestamp. The type of timestamp bound chosen by the storage client in Cloud Firestore is determined by the read options for the Read request.

Understand a read transaction in the storage layer

This section describes the types of reads and how they are processed in the storage layer in Cloud Firestore .

Strong reads

By default, Cloud Firestore reads are strongly consistent . This strong consistency means that a Cloud Firestore read returns the latest version of the data that reflects all writes that have been committed up until the start of the read.

Single Split read

The storage client in Cloud Firestore looks up the splits that own the keys of the rows to be read. Let's assume that it needs to do a read from Split 3 from the earlier section . The client sends the read request to the nearest replica to reduce round trip latency.

At this point, the following cases might happen depending on the chosen replica:

  • Read request goes to a leader replica (Zone A).
    • As the leader is always up-to-date, the read can proceed directly.
  • Read request goes to a non-leader replica (such as, Zone B)
    • Split 3 may know by its internal state that it has enough information to serve the read and the split does so.
    • Split 3 is unsure if it has seen the latest data. It sends a message to the leader to ask for the timestamp of the last transaction it needs to apply to serve the read. Once that transaction is applied, the read can proceed.

Cloud Firestore then returns the response to its client.

Multi-split read

In the situation where the reads have to be done from multiple splits, the same mechanism happens across all the splits. Once the data has been returned from all the splits, the storage client in Cloud Firestore combines the results. Cloud Firestore then responds to its client with this data.

Stale reads

Strong reads are the default mode in Cloud Firestore . However, it comes at a cost of potential higher latency due to the communication that may be required with the leader. Often your Cloud Firestore application doesn't need to read the latest version of the data and the functionality works well with data that may be a few seconds stale.

In such a case, the client may opt to receive stale reads by using the read_time read options. In this case, reads are done as the data was at read_time , and the closest replica is highly likely to already have verified it has data at the specified read_time . For noticeably better performance, 15 seconds is a reasonable staleness value. Even for stale reads, rows yielded are consistent with each other.

Avoid hotspots

The splits in Cloud Firestore are automatically broken into smaller pieces to distribute the work of serving traffic to more storage servers when needed or when the key space expands. Splits created to handle excess traffic are retained for around ~24 hours even if the traffic goes away. So if there are recurring traffic spikes, the splits are maintained and more splits are introduced whenever required. These mechanisms help Cloud Firestore databases to autoscale under increasing traffic load or database size. However, there are some limitations to be aware of as explained below.

Splitting storage and load takes time, and ramping up traffic too fast may cause high latency or deadline exceeded errors, commonly referred to as hotspots , while the service adjusts. The best practice is to distribute operations across the key range, while ramping up traffic on a collection in a database with 500 operations per second. After this gradual ramp up, increase the traffic by up to 50% every five minutes. This process is called the 500/50/5 rule and positions the database to optimally scale to meet your workload.

Though splits are created automatically with increasing load, Cloud Firestore can split a key range only until it's serving a single document using a dedicated set of replicated storage servers. As a result, high and sustained volumes of concurrent operations on a single document may lead to a hotspot on that document. If you encounter sustained high latencies on a single document, you should consider modifying your data model to split or replicate the data across multiple documents.

Contention errors happen when multiple operations try to read and/or write the same document simultaneously.

Another special case of hotspotting happens when a sequentially increasing/decreasing key is used as the document ID in Cloud Firestore , and there is a considerably high number of operations per second. Creating more splits doesn't help here since the surge of traffic simply moves to the newly created split. Since Cloud Firestore automatically indexes all fields in the document by default, such moving hotspots may also be created on the index space for a document field that contains a sequentially increasing/decreasing value like a timestamp.

Note that by following the practices outlined above, Cloud Firestore can scale to serve arbitrarily large workloads without you having to adjust any configuration.

عیب‌یابی

Cloud Firestore provides the Key Visualizer as a diagnostic tool designed to analyze usage patterns and troubleshoot hotspotting issues.

قدم بعدی چیست؟

،

Read this document to make informed decisions on architecting your applications for high performance and reliability. This document includes advanced Cloud Firestore topics. If you're just starting out with Cloud Firestore , see the quickstart guide instead.

Cloud Firestore is a flexible, scalable database for mobile device, web, and server development from Firebase and Google Cloud . It's very easy to get started with Cloud Firestore and write rich and powerful applications.

To make sure that your applications continue to perform well as your database size and traffic increase, it helps to understand the mechanics of reads and writes in the Cloud Firestore backend. You must also understand the interaction of your read and writes with the storage layer and the underlying constraints that may affect performance.

See the following sections for best practices before architecting your application.

Understand the high level components

The following diagram shows the high level components involved in a Cloud Firestore API request.

High level components

Cloud Firestore SDK and client libraries

Cloud Firestore supports SDKs and client libraries for different platforms. While an app can make direct HTTP and RPC calls to the Cloud Firestore API, the client libraries provide a layer of abstraction to simplify API usage and implement best practices. They may also provide additional features such as offline access, caches, and so on.

Google Front End (GFE)

This is an infrastructure service common to all Google cloud services. The GFE accepts incoming requests and forwards them to the relevant Google service ( Cloud Firestore service in this context). It also provides other important functionalities, including protection against Denial of Service attacks.

Cloud Firestore service

The Cloud Firestore service performs checks on the API request, which includes authentication, authorization, quota checks, and security rules, and also manages transactions. This Cloud Firestore service includes a storage client that interacts with the storage layer for the data reads and writes.

Cloud Firestore storage layer

The Cloud Firestore storage layer is responsible for storing both the data and metadata, and the associated database features provided by Cloud Firestore . The following sections describe how data is organized in the Cloud Firestore storage layer and how the system scales. Learning about how data is organized can help you design a scalable data model and better understand the best practices in Cloud Firestore .

Key Ranges and Splits

Cloud Firestore is a NoSQL, document-oriented database. You store data in documents , which are organized in hierarchies of collections . The collection hierarchy and document ID are translated to a single key for each document. Documents are logically stored and ordered lexicographically by this single key. We use the term key range to refer to a lexicographically contiguous range of keys.

A typical Cloud Firestore database is too large to fit on a single physical machine. There are also scenarios where the workload on the data is too heavy for one machine to handle. To handle large workloads, Cloud Firestore partitions the data into separate pieces that can be stored on and served from multiple machines or storage servers . These partitions are made on the database tables in blocks of key ranges called splits.

Synchronous Replication

It is important to note that the database is always being replicated automatically and synchronously. The splits of data have replicas in different zones to keep them available even when a zone becomes inaccessible. Consistent replication to the different copies of the split is managed by the Paxos algorithm for consensus. One replica of each split is elected to act as the Paxos leader, which is responsible for handling writes to that split. The synchronous replication gives you the ability to always be able to read the latest version of data from Cloud Firestore .

The overall result of this is a scalable and highly available system that provides low latencies for both reads and writes, irrespective of heavy workloads and at very large scale.

Data layout

Cloud Firestore is a schemaless document database. However, internally it lays out the data primarily in two relational database-style tables in its storage layer as follows:

  • Documents table: Documents are stored in this table.
  • Indexes table: Index entries that make it possible to get results efficiently and sorted by index value are stored in this table.

The following diagram shows how the tables for a Cloud Firestore database might look like with the splits. The splits are replicated in three different zones and each split has an assigned Paxos leader.

Data layout

Single Region versus Multi-Region

When you create a database, you must select a region or multi-region .

A single regional location is a specific geographic location, like us-west1 . The splits of data of a Cloud Firestore database have replicas in different zones within the selected region, as explained earlier.

A multi-region location consists of a defined set of regions where replicas of the database are stored. In a multi-region deployment of Cloud Firestore , two of the regions have full replicas of the entire data in the database. A third region has a witness replica that does not maintain a full set of data, but participates in replication. By replicating the data between multiple regions, data is available to be written and read even with the loss of an entire region.

For more information about the locations of a region, see Cloud Firestore locations .

Single region versus multi-region

Understand life of a write in Cloud Firestore

A Cloud Firestore client can write data by creating, updating, or deleting a single document. A write to a single document requires updating both the document and its associated index entries atomically in the storage layer. Cloud Firestore also supports atomic operations consisting of multiple reads and/or writes to one or more documents.

For all kinds of writes, Cloud Firestore provides the ACID properties (atomicity, consistency, isolation, and durability) of relational databases. Cloud Firestore also provides serializability , which means that all transactions appear as if executed in a serial order.

High-level steps in a write transaction

When the Cloud Firestore client issues a write or commits a transaction, using any of the methods mentioned earlier, internally this is executed as a database read-write transaction in the storage layer. The transaction enables Cloud Firestore to provide the ACID properties mentioned earlier.

As the first step of a transaction, Cloud Firestore reads the existing document, and determines the mutations to be made to the data in the Documents table.

This also includes making necessary updates to the Indexes table as follows:

  • Fields that are being added to the documents need corresponding inserts in the Indexes table.
  • Fields that are being removed from the documents need corresponding deletes in the Indexes table.
  • Fields that are being modified in the documents, need both deletes (for old values) and inserts (for new values) in the Indexes table.

To calculate the mutations mentioned earlier, Cloud Firestore reads the indexing configuration for the database. The indexing configuration stores information about the indexes for a database. Cloud Firestore uses two types of indexes: single-field and composite. For a detailed understanding of the indexes created in Cloud Firestore , see Index types in Cloud Firestore .

Once the mutations are calculated, Cloud Firestore collects them inside a transaction and then commits it.

Understand a write transaction in the storage layer

As discussed earlier, a write in Cloud Firestore involves a read-write transaction in the storage layer. Depending on the layout of data, a write might involve one or more splits as seen in the data layout .

In the following diagram, the Cloud Firestore database has eight splits (marked 1-8) hosted on three different storage servers in a single zone, and each split is replicated in 3(or more) different zones. Each split has a Paxos leader, which might be in a different zone for different splits.

<span class= Cloud Firestore database split">

Consider a Cloud Firestore database that has the Restaurants collection as follows:

Restaurant collection

The Cloud Firestore client requests the following change to a document in the Restaurant collection by updating the value of the priceCategory field.

Change to a document in collection

The following high-level steps describe what happens as part of the write:

  1. Create a read-write transaction.
  2. Read the restaurant1 document in the Restaurants collection from the Documents table from the storage layer.
  3. Read the indexes for the document from the Indexes table.
  4. Compute the mutations to be made to the data. In this case, there are five mutations:
    • M1: Update the row for restaurant1 in the Documents table to reflect the change in value of the priceCategory field.
    • M2 and M3: Delete the rows for the old value of priceCategory in the Indexes table for descending and ascending indexes.
    • M4 and M5: Insert the rows for the new value of priceCategory in the Indexes table for descending and ascending indexes.
  5. Commit these mutations.

The storage client in the Cloud Firestore service looks up the splits that owns the keys of the rows to be changed. Let's consider a case where Split 3 serves M1, and Split 6 serves M2-M5. There is a distributed transaction, involving all these splits as participants . The participant splits may also include any other split from which data was read earlier as part of the read-write transaction.

The following steps describe what happens as part of the commit:

  1. The storage client issues a commit. The commit contains the mutations M1-M5.
  2. Splits 3 and 6 are the participants in this transaction. One of the participants is chosen as the coordinator , such as Split 3. The job of the coordinator is to make sure the transaction either commits or aborts atomically across all participants.
    • The leader replicas of these splits are responsible for work done by the participants and coordinators.
  3. Each participant and coordinator runs a Paxos algorithm with their respective replicas.
    • The leader runs a Paxos algorithm with the replicas. Quorum is achieved if most of the replicas reply with an ok to commit response to the leader.
    • Each participant then notifies the coordinator when they are prepared (first phase of two-phase commit). If any participant cannot commit the transaction, the whole transaction aborts .
  4. Once the coordinator knows all participants, including itself, are prepared, it communicates the accept transaction outcome to all the participants (second phase of two-phase commit). In this phase, each participant records the commit decision to stable storage and the transaction is committed.
  5. The coordinator responds to the storage client in Cloud Firestore that the transaction has been committed. In parallel, the coordinator and all the participants apply the mutations to the data.

Commit lifecycle

When the Cloud Firestore database is small, it may happen that a single split owns all the keys in the mutations M1-M5. In such a case, there is only one participant in the transaction and the two-phase commit mentioned earlier is not required, thus making the writes faster.

Writes in multi-region

In a multi-region deployment, the spread of replicas across regions increases availability, but comes with a performance cost. The communication between replicas in different regions takes longer round trip times. Hence, the baseline latency for Cloud Firestore operations is slightly more compared to single region deployments.

We configure the replicas in a way that leadership for splits always stays in the primary region. The primary region is the one from which traffic is incoming to the Cloud Firestore server. This decision of leadership reduces the round-trip delay in communication between the storage client in Cloud Firestore and the replica leader (or coordinator for multi-split transactions).

Each write in Cloud Firestore also involves some interaction with the real-time engine in Cloud Firestore . For more information about real-time queries, see Understand real-time queries at scale .

Understand the life of a read in Cloud Firestore

This section delves into standalone, non-realtime reads in Cloud Firestore . Internally, the Cloud Firestore server handles most of these queries in two major stages:

  1. A single range scan over the Indexes table
  2. Point lookups in the Documents table based on the result of the earlier scan
There may be certain queries that require less processing or more processing (for example, IN queries) in Cloud Firestore .

The data reads from the storage layer are internally done by using a database transaction to ensure consistent reads. However, unlike the transactions used for writes, these transactions do not take locks. Instead, they work by choosing a timestamp, then executing all reads at that timestamp. Since they do not acquire locks, they do not block concurrent read-write transactions. To execute this transaction, the storage client in Cloud Firestore specifies a timestamp bound, which tells the storage layer how to choose a read timestamp. The type of timestamp bound chosen by the storage client in Cloud Firestore is determined by the read options for the Read request.

Understand a read transaction in the storage layer

This section describes the types of reads and how they are processed in the storage layer in Cloud Firestore .

Strong reads

By default, Cloud Firestore reads are strongly consistent . This strong consistency means that a Cloud Firestore read returns the latest version of the data that reflects all writes that have been committed up until the start of the read.

Single Split read

The storage client in Cloud Firestore looks up the splits that own the keys of the rows to be read. Let's assume that it needs to do a read from Split 3 from the earlier section . The client sends the read request to the nearest replica to reduce round trip latency.

At this point, the following cases might happen depending on the chosen replica:

  • Read request goes to a leader replica (Zone A).
    • As the leader is always up-to-date, the read can proceed directly.
  • Read request goes to a non-leader replica (such as, Zone B)
    • Split 3 may know by its internal state that it has enough information to serve the read and the split does so.
    • Split 3 is unsure if it has seen the latest data. It sends a message to the leader to ask for the timestamp of the last transaction it needs to apply to serve the read. Once that transaction is applied, the read can proceed.

Cloud Firestore then returns the response to its client.

Multi-split read

In the situation where the reads have to be done from multiple splits, the same mechanism happens across all the splits. Once the data has been returned from all the splits, the storage client in Cloud Firestore combines the results. Cloud Firestore then responds to its client with this data.

Stale reads

Strong reads are the default mode in Cloud Firestore . However, it comes at a cost of potential higher latency due to the communication that may be required with the leader. Often your Cloud Firestore application doesn't need to read the latest version of the data and the functionality works well with data that may be a few seconds stale.

In such a case, the client may opt to receive stale reads by using the read_time read options. In this case, reads are done as the data was at read_time , and the closest replica is highly likely to already have verified it has data at the specified read_time . For noticeably better performance, 15 seconds is a reasonable staleness value. Even for stale reads, rows yielded are consistent with each other.

Avoid hotspots

The splits in Cloud Firestore are automatically broken into smaller pieces to distribute the work of serving traffic to more storage servers when needed or when the key space expands. Splits created to handle excess traffic are retained for around ~24 hours even if the traffic goes away. So if there are recurring traffic spikes, the splits are maintained and more splits are introduced whenever required. These mechanisms help Cloud Firestore databases to autoscale under increasing traffic load or database size. However, there are some limitations to be aware of as explained below.

Splitting storage and load takes time, and ramping up traffic too fast may cause high latency or deadline exceeded errors, commonly referred to as hotspots , while the service adjusts. The best practice is to distribute operations across the key range, while ramping up traffic on a collection in a database with 500 operations per second. After this gradual ramp up, increase the traffic by up to 50% every five minutes. This process is called the 500/50/5 rule and positions the database to optimally scale to meet your workload.

Though splits are created automatically with increasing load, Cloud Firestore can split a key range only until it's serving a single document using a dedicated set of replicated storage servers. As a result, high and sustained volumes of concurrent operations on a single document may lead to a hotspot on that document. If you encounter sustained high latencies on a single document, you should consider modifying your data model to split or replicate the data across multiple documents.

Contention errors happen when multiple operations try to read and/or write the same document simultaneously.

Another special case of hotspotting happens when a sequentially increasing/decreasing key is used as the document ID in Cloud Firestore , and there is a considerably high number of operations per second. Creating more splits doesn't help here since the surge of traffic simply moves to the newly created split. Since Cloud Firestore automatically indexes all fields in the document by default, such moving hotspots may also be created on the index space for a document field that contains a sequentially increasing/decreasing value like a timestamp.

Note that by following the practices outlined above, Cloud Firestore can scale to serve arbitrarily large workloads without you having to adjust any configuration.

عیب‌یابی

Cloud Firestore provides the Key Visualizer as a diagnostic tool designed to analyze usage patterns and troubleshoot hotspotting issues.

قدم بعدی چیست؟