DDIA·مطالعهSTUDY

نقشه راهRoadmap/ فصل ۷Chapter 7

تقسیم (Sharding)Sharding

وقتی یک محل برای همهٔ داده یا بار کافی نیست، داده را به شاردها تقسیم می‌کنیم. انتخابِ کلیدِ تقسیم، جابه‌جاییِ شارد و مسیرِ جست‌وجو تعیین می‌کنند این تقسیم چقدر مفید و قابل‌مدیریت باشد.When data or writes outgrow one machine, you must break data into smaller pieces and place each piece on a machine: a shard or partition. The goal is simple — even spread of data and load — but load skew, hot keys, moving shards as the cluster grows, and secondary indexes make the game serious. Two main families — key-range and hash sharding — each pay a different price. With 9 interactive diagrams (6 Archify stages + 3 concept sketches), a teacher narration for each diagram, and an interview question pack.

۹9 نمودار تعاملیinteractive diagrams· محور: key range · hash sharding · hot spots · request routing · secondary indexesTheme: key range · hash sharding · hot spots · request routing · secondary indexes· زمان مطالعه: حدود ۶۰ دقیقهReading time: ~60 min

۰0 نقشهٔ راه فصلChapter roadmap

ایدهٔ کلیدی فصلChapter thesis

وقتی یک محل برای همهٔ داده یا بار کافی نیست، داده را به شاردها تقسیم می‌کنیم. انتخابِ کلیدِ تقسیم، جابه‌جاییِ شارد و مسیرِ جست‌وجو تعیین می‌کنند این تقسیم چقدر مفید و قابل‌مدیریت باشد.

مثال فنی: تقسیم داده با تکثیر فرق دارد

وضعیت اولیه: کلیدهای 1…1000 بین shard A و B نصف شده‌اند.

اجرای مرحله‌به‌مرحله: برای هر shard سه replica داریم؛ جمعاً شش کپی فیزیکی داریم، ولی A و B دو نیمهٔ متفاوت داده را نگه می‌دارند.

نتیجه و دلیل: sharding ظرفیت را تقسیم می‌کند؛ replication نسخه‌های همان بخش را برای خرابی و خواندن فراهم می‌کند.

مثال‌های این فصل سناریوهای آموزشیِ برگرفته از کارهای روزمره‌اند. عددهای انتخاب‌شده برای توضیح‌اند؛ معماریِ داخلیِ یک شرکت مشخص را توصیف نمی‌کنند.

Sharding means breaking data into smaller subsets and placing each subset on a different node; so each record sits in exactly one shard (and usually that same shard has several copies too — sharding combines with replication). The goal is spreading data and query load evenly across machines and avoiding hot spots.

For that you must pick a partition key and own an algorithm telling each record where it lives. Two main families: key-range sharding (keys are ordered and each shard holds one contiguous range; range queries are fast but hotspot risk) and hash sharding (hashing the key spreads it; order is lost but load is more even). And when nodes join or leave, rebalancing must move the least data.

پنج ایده‌ای که این فصل روی آن‌ها می‌چرخد:

Five ideas this chapter revolves around:

  • الگوریتمِ نگاشت: از partition key به shard و از shard به node — و اینکه چرا hash % N هنگامِ تغییرِ N فاجعه است.
  • Rebalancing: جابه‌جاییِ کلِ shard، شکافتنِ بازه‌ها، یا consistent hashing — هرکدام بهای خودش را دارد.
  • Skew و hotspot: حتی بهترین hash هم بارِ واقعیِ یک کلیدِ داغ را پخش نمی‌کند.
  • Request routing: کلاینت چطور بفهمد کدام node مالکِ کدام کلید است — و هنگامِ جابه‌جاییِ shard چه اتفاقی می‌افتد.
  • Secondary indexes: ایندکسِ ثانویه هم باید shard شود؛ local (document-partitioned) یا global (term-partitioned).
  • The mapping algorithm: from partition key to shard and from shard to node — and why hash % N is a disaster when N changes.
  • Rebalancing: moving whole shards, splitting ranges, or consistent hashing — each with its own price.
  • Skew and hotspots: even the best hash doesn't spread one hot key's real load.
  • Request routing: how a client learns which node owns which key — and what happens during shard moves.
  • Secondary indexes: a secondary index must be sharded too; local (document-partitioned) or global (term-partitioned).
دامِ اصطلاح: partition ≠ network partitionTerm trap: partition ≠ network partition

«Partition» در این فصل یعنی تکه‌ای از داده؛ اما network partition (netsplit) یعنی قطعیِ ارتباط بین nodeها — یک نوع خرابی که در فصل ۹ بررسی می‌شود. این دو فقط در نام شبیه‌اند و هیچ ربطی به هم ندارند.

“Partition” in this chapter means a piece of data; but a network partition (netsplit) means severed communication between nodes — a failure type covered in Chapter 9. The two only share a name and have nothing to do with each other.

نکتهٔ مصاحبهInterview tip

جملهٔ طلایی این فصل: «sharding یک راه‌حلِ سنگین برای مقیاسِ بزرگ است». اگر یک ماشین از پسِ داده و نوشتن برمی‌آید — و امروزه یک ماشین خیلی کار می‌کند! — sharding نکن؛ چون انتخابِ partition key سخت است، تغییرش گران است، و join یا نوشتنِ چندشیءِ مرتبط به distributed transaction نیاز پیدا می‌کند.

This chapter's golden line: “sharding is a heavy solution for large scale”. If one machine handles the data and writes — and today one machine does a lot! — don't shard; because picking the partition key is hard, changing it is expensive, and joining or writing related objects across shards needs a distributed transaction.

۱1 sharding چرا و چیست؟Why and what is sharding?

شاردینگ یعنی تقسیم مجموعهٔ داده به بخش‌هایی که هرکدام بخشی از رکوردها را دارند. تکثیر، از همان بخش نسخهٔ دیگر می‌سازد؛ این دو کار متفاوت‌اند و می‌توانند هم‌زمان استفاده شوند.

مثال فنی: نقطهٔ شروعِ تقسیم

وضعیت اولیه: داده 4 TB است و ظرفیت قابل‌استفادهٔ هر ماشین 1 TB فرض شده است.

اجرای مرحله‌به‌مرحله: کلیدها با یک طرح مشخص بین چند shard پخش می‌شوند؛ query باید shard مربوط را پیدا کند و عملیات چندبخشی مسیر جدا دارد.

نتیجه و دلیل: sharding محدودیت ظرفیت را حل می‌کند، اما مسیریابی، تعادل و هماهنگی را به سیستم اضافه می‌کند.

A distributed database usually spreads data across nodes in two ways:

  • Replication (فصل ۶): نسخه‌ای از همان داده روی چند node — برای تحملِ خرابی و خواندنِ بیشتر.
  • Sharding: وقتی داده یا نوشتن بیش از توانِ یک node است، داده به shardهای کوچک‌تر شکسته می‌شود و هر shard روی node متفاوت می‌رود.
  • Replication (Chapter 6): a copy of the same data on several nodes — for failure tolerance and more reads.
  • Sharding: when data or writes exceed one node's capacity, data is broken into smaller shards and each shard goes to a different node.

هر رکورد دقیقاً به یک shard تعلق دارد؛ در عمل هر shard یک پایگاه دادهِ کوچکِ مستقل است (هرچند بعضی سیستم‌ها عملیاتِ چندشیئی دارند). sharding معمولاً با replication ترکیب می‌شود: هر shard چند نسخه دارد و leader هر shard روی یک node و followerهایش روی nodeهای دیگرند — و هر node می‌تواند هم‌زمان leaderِ بعضی shardها و followerِ بعضی دیگر باشد.

Each record belongs to exactly one shard; in practice each shard is a small independent database (though some systems have multi-object operations). Sharding usually combines with replication: each shard has several copies, each shard's leader sits on one node and its followers on others — and each node can simultaneously lead some shards and follow others.

۱.۱ نمودار ۱ + روایت مدرس1.1 Diagram 1 + teacher narration

تصویرِ کلانِ «sharding + replication»: چهار node که هرکدام یک shard را lead می‌کنند و shard دیگری را follow می‌کنند، و برنامه که نوشتن را به leader و خواندن را به هر replica می‌فرستد:

The big picture of “sharding + replication”: four nodes each leading one shard and following another, with the application sending writes to the leader and reads to any replica:

نمودار ۱ · Architecture — ترکیب sharding و replication در یک خوشهDiagram 1 · Architecture — combining sharding and replication in one cluster باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار ۱Teacher narration of diagram 1

مثال فنی برای خواندنِ نمودار: تقسیم داده با تکثیر فرق دارد

وضعیت اولیه: کلیدهای 1…1000 بین shard A و B نصف شده‌اند.

اجرای مرحله‌به‌مرحله: برای هر shard سه replica داریم؛ جمعاً شش کپی فیزیکی داریم، ولی A و B دو نیمهٔ متفاوت داده را نگه می‌دارند.

نتیجه و دلیل: sharding ظرفیت را تقسیم می‌کند؛ replication نسخه‌های همان بخش را برای خرابی و خواندن فراهم می‌کند.

در سمتِ راستِ شکل یک قابِ بزرگ می‌بینی با این نوشته: «Each record belongs to exactly one shard · each shard is replicated on several nodes · one node holds many shards». داخلِ قاب، چهار جعبهٔ پایگاه دادهِ ۲×۲ نشسته‌اند: در ردیفِ بالا «Node 1» و «Node 2»، در ردیفِ پایین «Node 3» و «Node 4». زیرنویسِ Node 1 می‌گوید «leader of shard 1» و برچسبِ بالای جعبه می‌گوید «follower of shard 4» — همین الگو برای بقیه تکرار شده: Node 2 رهبرِ shard 2 و followerِ shard 1، Node 3 رهبرِ shard 3 و followerِ shard 4، Node 4 رهبرِ shard 4 و followerِ shard 1. یعنی هر node هم «صاحب» است و هم «پیرو».

در چپِ شکل جعبه‌ای به نام «Application» (زیرنویس: «reads and writes records») نشسته است. فلشِ پررنگِ «write to the shard leader» از آن به Node 1 می‌رود و فلشِ معمولیِ «read from any replica» به Node 4 — دو مسیرِ متفاوت برای نوشتن و خواندن.

حالا فلش‌های افقی و عمودیِ بینِ nodeها را دنبال کن. فلشِ افقیِ بالا، «shard 1: leader copy to follower»، از Node 1 به Node 2 می‌رود: کپیِ shard 1 از رهبر به پیرو. در ردیفِ پایین، فلشِ «shard 4: leader copy to follower» از Node 4 به Node 3 همین کار را برای shard 4 می‌کند. دو فلشِ عمودیِ چین‌چین هم رابطهٔ دیگر را نشان می‌دهند: «this node also follows shard 4» (از Node 1 به Node 4) و «this node also follows shard 3» (از Node 2 به Node 3).

درسِ شکل: با چهار node و چهار shard، هر node دقیقاً leaderِ یکی و followerِ دیگری است؛ نه leader همه‌چیز است و نه هیچ‌کس. جمع‌بندیِ شکل در کارت‌ها آمده: نام‌های مختلفِ shard در سیستم‌های مختلف (کارتِ آبی)، دلیل و بهای sharding (کارتِ سبز)، و کاربردِ آن در چندمستاجری یا multitenancy (کارتِ صورتی).

On the right of the figure you see a large frame reading: “Each record belongs to exactly one shard · each shard is replicated on several nodes · one node holds many shards”. Inside the frame sit four database boxes in a 2×2 grid: “Node 1” and “Node 2” on the top row, “Node 3” and “Node 4” below. Node 1's caption says “leader of shard 1” and its top label “follower of shard 4” — the same pattern repeats: Node 2 leads shard 2 and follows shard 1, Node 3 leads shard 3 and follows shard 4, Node 4 leads shard 4 and follows shard 1. Each node is both “owner” and “follower”.

On the left sits a box named “Application” (caption: “reads and writes records”). The bold “write to the shard leader” arrow runs from it to Node 1, and the plain “read from any replica” arrow to Node 4 — two different paths for writes and reads.

Now follow the horizontal and vertical arrows between nodes. The top horizontal arrow, “shard 1: leader copy to follower”, runs from Node 1 to Node 2: shard 1's copy from leader to follower. On the bottom row, “shard 4: leader copy to follower” from Node 4 to Node 3 does the same for shard 4. Two dashed vertical arrows show the other relation: “this node also follows shard 4” (Node 1 to Node 4) and “this node also follows shard 3” (Node 2 to Node 3).

The diagram's lesson: with four nodes and four shards, each node leads exactly one and follows another; it leads nothing-everything and nobody leads nothing. The figure's cards summarize: shards' different names across systems (blue card), sharding's reason and price (green card), and its use in multitenancy (pink card).

۱.۲ مزایا، هزینه‌ها و چندمستاجری1.2 Pros, costs and multitenancy

چرا sharding کنیم؟Why shard?

  • مقیاس‌پذیری: حجمِ داده یا نوشتن بیش از توانِ یک node است.
  • Scale-out: به‌جای ماشینِ بزرگ‌تر، ماشینِ بیشتر — اگر بار یکنواخت تقسیم شود، ۱۰ node ده‌برابر ظرفیت می‌دهند.
  • اگر فقط خواندن زیاد است، لازم نیست sharding کنی؛ read scaling با replication حل می‌شود (فصل ۶).
  • Scalability: data volume or writes exceed one node's capacity.
  • Scale-out: more machines instead of a bigger machine — if load splits evenly, 10 nodes give ten times the capacity.
  • If only reads are heavy, you don't need sharding; read scaling is solved by replication (Chapter 6).

چرا sharding نکنیم؟Why not shard?

  • انتخابِ partition key همه‌چیز را تعیین می‌کند و تغییرِ طرحِ sharding بسیار گران است.
  • بدانی رکورد کجاست سریع است؛ ندانی باید بین همهٔ shardها بگردی.
  • داده‌های رابطه‌ای سخت‌ترند: جست‌وجو با ایندکسِ ثانویه و join ممکن است بین shardها پخش شود.
  • نوشتنِ چند رکوردِ مرتبط در چند shard به distributed transaction نیاز دارد — کندتر و گاهی گلوگاهِ کل سیستم.
  • Picking the partition key determines everything, and changing the sharding scheme is very expensive.
  • Knowing where a record lives is fast; not knowing means searching across all shards.
  • Relational data is harder: secondary-index searches and joins may scatter across shards.
  • Writing several related records across shards needs a distributed transaction — slower and sometimes the whole system's bottleneck.
نکتهٔ عمیق: sharding حتی روی یک ماشینDeep note: sharding even on one machine

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

مثال فنی: هزینهٔ تقسیمِ زودهنگام

وضعیت اولیه: کل مجموعه 20 GB است و یک سرور هدف بار را تأمین می‌کند.

اجرای مرحله‌به‌مرحله: پس از تقسیم به چهار shard، گزارشِ همهٔ سفارش‌ها باید چهار پاسخ را جمع کند و مهاجرت داده هم پیچیده‌تر می‌شود.

نتیجه و دلیل: تا محدودیت مشخصی وجود ندارد، شاردینگ ممکن است فقط مسیر ساده را پرهزینه کند؛ replica نیاز مستقلِ تحمل خرابی است.

Some systems shard on a single machine too: one single-threaded process per CPU core, to exploit CPU parallelism or NUMA architecture (memory nearer to some CPUs). Redis, VoltDB and FoundationDB use exactly this pattern. So a shard doesn't necessarily mean several machines; it means “an independent unit of load”.

sharding برای چندمستاجری (Multitenancy)Sharding for multitenancy

در محصول چندمستاجری، دادهٔ چند سازمان در یک سامانه نگه داشته می‌شود. تقسیم بر اساس سازمان می‌تواند جداسازی و جابه‌جایی را آسان کند، ولی اندازهٔ مشتری‌ها یکسان نیست.

مثال فنی: یک tenant، یک محل

وضعیت اولیه: کلید مشتری سازمانی tenant_id=17 روی shard A قرار دارد.

اجرای مرحله‌به‌مرحله: بیشتر queryهای همین tenant محلی‌اند؛ اگر یک tenant ۶۰٪ بار را بسازد، جابه‌جایی آن یا شکستن داده‌اش لازم می‌شود.

نتیجه و دلیل: tenant کلید طبیعی جداسازی است، اما اندازه و بار tenantها مساوی نیست و «یک tenant یک shard» همیشه متعادل نمی‌ماند.

In SaaS products, each customer (tenant) has separate, self-contained data. Sometimes sharding serves exactly that: one shard per tenant, or several small tenants in one shard. Advantages:

  • جداسازیِ منابع: عملیاتِ سنگینِ یک tenant، performance بقیه را خراب نمی‌کند.
  • جداسازیِ دسترسی: باگ در کنترلِ دسترسی، دادهٔ tenant دیگر را لو نمی‌دهد (جدا بودنِ فیزیکیِ داده‌ها).
  • Cell-based architecture: سرویس‌ها و storage یک دسته از tenantها در یک «cell» خودبسنده جمع می‌شوند؛ خرابیِ هر cell محدود به خودش می‌ماند.
  • بکاپ و بازیابیِ جداگانه: بازگرداندنِ یک tenant بدونِ اثر روی بقیه — مثلاً بعد از حذفِ اشتباهیِ داده.
  • سازگاریِ قانونی: قوانینِ GDPR و CCPA حقِ «دسترسی و حذفِ دادهٔ شخصی» می‌دهند؛ وقتی دادهٔ هر شخص در shard خودش باشد، export و delete ساده می‌شود.
  • Data residence: اگر دادهٔ tenant باید در حوزهٔ قضایی مشخصی بماند، shard او را به همان region می‌چسبانند.
  • عرضهٔ تدریجیِ schema: migrationها یک‌باره برای همه اجرا نمی‌شوند؛ tenantبه‌tenant جلو می‌روی و مشکل را زودتر می‌بینی.
  • Resource isolation: one tenant's heavy operations don't ruin the rest's performance.
  • Access isolation: an access-control bug doesn't leak another tenant's data (physically separate data).
  • Cell-based architecture: one tenant group's services and storage gather in a self-contained “cell”; each cell's failure stays inside itself.
  • Separate backup and recovery: restoring one tenant without touching the rest — e.g. after a mistaken data deletion.
  • Legal compatibility: GDPR and CCPA grant “access and erase personal data” rights; when each person's data sits in its own shard, export and delete get simple.
  • Data residence: if a tenant's data must stay in a given jurisdiction, pin its shard to that region.
  • Gradual schema rollout: migrations don't run for everyone at once; you advance tenant by tenant and spot problems earlier.

چالش‌ها: فرضِ «هر tenant در یک ماشین جا می‌شود» (tenant غول‌پیکر باید داخلِ خودش هم shard شود)، تعدادِ زیادِ tenantهای کوچک (overheadِ shardِ جداگانه و جابه‌جاییِ آن‌ها هنگام رشد)، و سخت‌شدنِ قابلیت‌هایی که دادهٔ چند tenant را به هم ربط می‌دهند.

Challenges: the “each tenant fits on one machine” assumption (a giant tenant must be sharded inside itself too), many small tenants (per-shard overhead and moving them as they grow), and harder features spanning several tenants' data.

۲2 طرح‌های sharding برای دادهٔ key-valueSharding schemes for key-value data

کلیدِ تقسیم مشخص می‌کند هر رکورد کجا برود. انتخاب خوب باید هم پخشِ بار و هم پرس‌وجوهای رایج را در نظر بگیرد؛ یکنواختیِ تعداد رکوردها تنها معیار نیست.

مثال فنی: کلید تعیین‌کنندهٔ مسیر

وضعیت اولیه: درخواست GET customer:17 و پرس‌وجوی همهٔ مشتریان یک شهر داریم.

اجرای مرحله‌به‌مرحله: تقسیم بر customer_id مسیر اول را به یک shard می‌فرستد؛ query شهر بدون ایندکس مناسب ممکن است همهٔ shardها را بخواند.

نتیجه و دلیل: انتخاب کلید فقط توزیع نوشتن نیست؛ تعداد مقصدهای پرس‌وجوهای مهم را هم تعیین می‌کند.

The core question: with all this data, where do we put which record? The goal is spreading data and query load evenly across nodes. Unfair splitting is called skew: some shards hold more data or queries and sharding goes ineffective — extremely, all load lands on one shard while 9 of 10 nodes idle. Such a shard is a hot shard and an overloaded key a hot key (e.g. a celebrity's account on a social network).

الگوریتمِ نگاشت باید ورودی‌اش partition key باشد و خروجی‌اش shard؛ در key-value store معمولاً کلید (یا بخشِ اولِ کلید) و در مدلِ رابطه‌ای یک ستونِ جدول (نه لزوماً کلید اصلی). ضمناً الگوریتم باید برای rebalancing — برای رهایی از hotspot — منعطف باشد.

The mapping algorithm's input must be the partition key and its output the shard; in a key-value store usually the key (or the key's first part), in the relational model one table column (not necessarily the primary key). The algorithm must also stay flexible for rebalancing — for escaping hotspots.

۲.۱ تقسیم بر اساس بازهٔ کلید (Key-Range Sharding) + نمودار ۲2.1 Key-range sharding + diagram 2

در تقسیم بازه‌ای، هر شارد بازه‌ای پیوسته از کلیدهای مرتب را نگه می‌دارد. خواندنِ یک بازه آسان است، اما ورودِ دادهٔ جدید ممکن است روی آخرین بازه متمرکز شود.

مثال فنی: بازهٔ زمانی و shard داغ

وضعیت اولیه: رخدادهای ساعت ۱۰ تا ۱۱ روی shard آخر ذخیره می‌شوند و همهٔ رخدادهای تازه زمان فعلی دارند.

اجرای مرحله‌به‌مرحله: بازه‌های قدیمی آرام‌اند ولی shardِ بازهٔ جاری تمام نوشتن را می‌گیرد؛ تقسیمِ بیشترِ بازهٔ جاری یا ترکیب کلید با bucket فشار را پخش می‌کند.

نتیجه و دلیل: range sharding اسکن زمان را آسان می‌کند، ولی کلیدِ پیوسته می‌تواند نوشتن را یک‌جا جمع کند.

Here each shard holds a contiguous range of sorted keys — like volumes of a printed encyclopedia: volume 1 words A and B, volume 12 words T to Z. Boundaries needn't be evenly spaced, because data isn't uniform; boundaries must adapt to the data.

  • مزیت: داخلِ هر shard کلیدها مرتب‌اند (B-tree یا SSTable — فصل ۴)، پس range scan سریع است و کلیدِ ترکیبی مثل ایندکسِ چندستونی کار می‌کند.
  • مثال: دادهٔ شبکه‌ای از سنسورها با کلیدِ timestamp — بدونِ زحمت همهٔ خوانش‌های یک ماه را برمی‌داری.
  • خطر: نوشتنِ متمرکز روی کلیدهای نزدیک، یک shard را داغ می‌کند. اگر کلید فقط timestamp باشد، همهٔ نوشته‌های «این ماه» به shard همین ماه می‌روند و بقیه بیکارند.
  • راه‌حلِ رایج: پیشوندِ کلید با شناسهٔ سنسور (یا tenant) تا بار بین shardها پخش شود — بها: برای بازهٔ زمانیِ چند سنسور باید چند کوئریِ جدا بزنی.
  • مرزها: دستی (مثل Vitess) یا خودکار (Bigtable، HBase، حالتِ range در MongoDB، CockroachDB، FoundationDB).
  • Rebalancing: pre-splitting در شروع (بدونِ دانستنِ توزیعِ کلیدها خطرناک است)، و بعد split/merge مثل بالای B-tree؛ آستانهٔ رایجِ split در HBase حدود ۱۰GB یا عبورِ مداومِ نوشتن از آستانه.
  • گرانیِ split: همهٔ داده باید در فایل‌های جدید بازنویسی شود (مثل compaction) — و دقیقاً shardِ داغ که زیرِ فشار است split می‌شود و فشارش بیشتر می‌شود.
  • Advantage: inside each shard keys are ordered (B-tree or SSTable — Chapter 4), so range scans are fast and a composite key works like a multi-column index.
  • Example: sensor-network data keyed by timestamp — fetching a month of readings costs no effort.
  • Risk: concentrated writes on nearby keys heat one shard. If the key is only a timestamp, all of “this month's” writes go to this month's shard while the rest idle.
  • Common fix: prefixing the key with the sensor (or tenant) ID to spread load across shards — price: a multi-sensor time range needs several separate queries.
  • Boundaries: manual (like Vitess) or automatic (Bigtable, HBase, range mode in MongoDB, CockroachDB, FoundationDB).
  • Rebalancing: pre-splitting at the start (dangerous without knowing the key distribution), then split/merge like the top of a B-tree; HBase's common split threshold is ~10GB or writes persistently crossing a threshold.
  • Split cost: all data must be rewritten into new files (like compaction) — and exactly the hot shard under pressure splits, raising its pressure further.

حالا از خودت بپرس کدام طرح به دردِ بارِ تو می‌خورد؛ تصمیم‌گیری را گام‌به‌گام جلو می‌بریم:

Now ask yourself which scheme fits your load; let's walk the decision step by step:

نمودار ۲ · Workflow — انتخاب طرحِ sharding: بازهٔ کلید، تعدادِ ثابتِ shard، بازه‌های hashDiagram 2 · Workflow — choosing a sharding scheme: key range, fixed shard count, hash ranges باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار ۲Teacher narration of diagram 2

مثال فنی برای خواندنِ نمودار: بازهٔ زمانی و shard داغ

وضعیت اولیه: رخدادهای ساعت ۱۰ تا ۱۱ روی shard آخر ذخیره می‌شوند و همهٔ رخدادهای تازه زمان فعلی دارند.

اجرای مرحله‌به‌مرحله: بازه‌های قدیمی آرام‌اند ولی shardِ بازهٔ جاری تمام نوشتن را می‌گیرد؛ تقسیمِ بیشترِ بازهٔ جاری یا ترکیب کلید با bucket فشار را پخش می‌کند.

نتیجه و دلیل: range sharding اسکن زمان را آسان می‌کند، ولی کلیدِ پیوسته می‌تواند نوشتن را یک‌جا جمع کند.

این شکل یک درختِ تصمیم است که از راست به چپ (در مسیرِ اصلی) جلو می‌رود و چهار خط (lane) دارد: خطِ بالا «Decision» که پرسش‌ها روی آن نشسته‌اند، و سه خطِ پایین‌تر برای سه نتیجه: «Key Range»، «Hash — Fixed Count» و «Hash — Adaptive». شروع در جعبه‌ی «Shard a KV Dataset» (زیرنویس: «spread data and load»، برچسبِ «goal: no hot spots») است.

فلشِ پررنگِ «have a large dataset» به اولین پرسش می‌برد: «Range Scans on Keys?» (زیرنویس: «queries over key ranges»). اگر «yes»، شاخه به پایین می‌پرد به جعبه‌ی «Key-Range Sharding» (زیرنویس: «one contiguous range each»، برچسبِ «range scans stay fast»).

اگر «no»، مسیرِ اصلی به پرسشِ دوم می‌رسد: «Shard Count Fixed OK?» (زیرنویس: «volume predictable»). جوابِ «yes» یعنی «Fixed Number of Shards» (زیرنویس: «hash(key) % 1000»، برچسبِ «move whole shards»). جوابِ «no» به پرسشِ سوم می‌رسد: «Split Shards on Growth?» (زیرنویس: «subranges, not new keys»)؛ اگر «yes»، «Hash-Range Sharding» (زیرنویس: «ranges of hash values»، برچسبِ «split when too big»)؛ و اگر «no»، انتهای مسیرِ اصلی: «Consistent Hashing» (زیرنویس: «reassign scattered keys»، برچسبِ «rendezvous · jump hash»).

درسِ شکل: هر پرسش یک ویژگیِ بار را می‌سنجد — نیاز به range scan، قابلیتِ پیش‌بینیِ حجم، و روشِ رشد — و به ازای هر ترکیب، یک طرحِ معین وجود دارد. کارت‌های پایینِ شکل هم جزئیاتِ هر خانواده و تله‌های hotspot و rebalancing را جمع کرده‌اند.

This figure is a decision tree running right to left (on the main path) with four lanes: the top lane “Decision” holding the questions, and three lower lanes for three outcomes: “Key Range”, “Hash — Fixed Count” and “Hash — Adaptive”. The start is the “Shard a KV Dataset” box (caption: “spread data and load”, label: “goal: no hot spots”).

The bold “have a large dataset” arrow leads to the first question: “Range Scans on Keys?” (caption: “queries over key ranges”). If “yes”, the branch drops down to the “Key-Range Sharding” box (caption: “one contiguous range each”, label: “range scans stay fast”).

If “no”, the main path reaches the second question: “Shard Count Fixed OK?” (caption: “volume predictable”). A “yes” means “Fixed Number of Shards” (caption: “hash(key) % 1000”, label: “move whole shards”). A “no” reaches the third question: “Split Shards on Growth?” (caption: “subranges, not new keys”); if “yes”, “Hash-Range Sharding” (caption: “ranges of hash values”, label: “split when too big”); and if “no”, the main path's end: “Consistent Hashing” (caption: “reassign scattered keys”, label: “rendezvous · jump hash”).

The figure's lesson: each question measures one load property — range-scan need, volume predictability, and growth style — and each combination maps to a definite scheme. The bottom cards gather each family's details and the hotspot and rebalancing traps.

۲.۲ تقسیم بر اساس hash و تلهٔ mod N + نمودار ۳2.2 Hash sharding and the mod-N trap + diagram 3

در تقسیم هش‌محور، خروجیِ تابع هش مقصد رکورد را تعیین می‌کند. استفادهٔ مستقیم از باقی‌ماندهٔ تقسیم بر تعداد سرورها، با تغییر تعداد سرورها می‌تواند دادهٔ زیادی را جابه‌جا کند.

مثال فنی: تلهٔ تغییرِ مقسوم‌علیه

وضعیت اولیه: مقدار hash یک کلید 10 است؛ با سه سرور مقصد 10 % 3 = 1 می‌شود.

اجرای مرحله‌به‌مرحله: افزودن سرور چهارم آن را به 10 % 4 = 2 می‌برد؛ بسیاری از کلیدهای دیگر هم جابه‌جا می‌شوند.

نتیجه و دلیل: جدا کردن shard منطقی از سرور فیزیکی، تغییر تعداد ماشین‌ها را از بازتقسیم همهٔ کلیدها مستقل‌تر می‌کند.

If key closeness doesn't matter to you (e.g. keys are tenant IDs), the most common move is first hashing the partition key. A good hash evens out skewed data: output looks random but identical input always gives identical output.

  • hash لازم نیست رمزنگارانه باشد: MongoDB از MD5 و Cassandra/ScyllaDB از Murmur3 استفاده می‌کنند.
  • hash توابعِ زبان‌های برنامه‌نویسی ممکن است بین پروسه‌ها متفاوت باشند (Object.hashCode() در Java، Object#hash در Ruby) و برای sharding نامناسبند.
  • تلهٔ hash(key) % N: به‌ظاهر ساده است، اما با تغییرِ N تقریباً همهٔ کلیدها جابه‌جا می‌شوند (وقتی از ۳ به ۴ node می‌روی، بیشترِ کلیدها node عوض می‌کنند). rebalancing باید کمترین جابه‌جایی را بکند، پس mod N مردود است.
  • تعدادِ ثابتِ shard: مثلاً ۱۰۰۰ shard از ابتدا، ۱۰۰ تا روی هر node از ۱۰ node؛ کلید به shardِ hash(key) % 1000 می‌رود و فقط «نگاشتِ shard به node» تغییر می‌کند. با اضافه‌شدنِ node، چند shard کامل جابه‌جا می‌شوند — ارزان‌تر از split. تعدادِ shard را با ضریبِ قابل‌تقسیمِ زیاد انتخاب کن و به nodeهای قوی‌تر shard بیشتر بده (Citus، Riak، Elasticsearch، Couchbase).
  • بهای تعدادِ ثابت: اگر تعدادِ shard کم باشد و به node بیشتری نیاز شود، reshardingِ گران لازم است (شکافتنِ هر shard و بازنویسیِ فایل‌ها؛ بعضی سیستم‌ها حینِ نوشتن اجازه نمی‌دهند).
  • بازه‌های hash: ترکیبِ key-range با hash — هر shard یک بازه از مقادیرِ hash را دارد (مثلاً ۰ تا ۱۶۳۸۳ برای shard صفرم). کلیدهای نزدیک (مثل timestampهای پیاپی) پخش می‌شوند و shard بزرگ مثل key-range split می‌شود؛ فقط range query روی خودِ کلیدِ partition گران می‌شود. ترفندِ کلیدِ ترکیبی (partition key در ستونِ اول، ترتیب با بقیهٔ ستون‌ها) همچنان بازه‌خوانیِ درون‌shard را نگه می‌دارد. این طرح در YugabyteDB و DynamoDB هست و در MongoDB یک گزینه است.
  • The hash needn't be cryptographic: MongoDB uses MD5 and Cassandra/ScyllaDB use Murmur3.
  • Programming-language hash functions may differ between processes (Object.hashCode() in Java, Object#hash in Ruby) and don't fit sharding.
  • The hash(key) % N trap: seemingly simple, but when N changes nearly all keys move (going from 3 to 4 nodes, most keys switch nodes). Good rebalancing moves the least data, so mod N is rejected.
  • Fixed shard count: e.g. 1000 shards from the start, 100 on each of 10 nodes; a key goes to shard hash(key) % 1000 and only the “shard-to-node map” changes. When a node joins, a few whole shards move — cheaper than splitting. Pick the shard count with many divisors and give stronger nodes more shards (Citus, Riak, Elasticsearch, Couchbase).
  • Fixed-count price: if the shard count comes out small and more nodes are needed, expensive resharding is required (splitting each shard and rewriting files; some systems forbid it during writes).
  • Hash ranges: key-range combined with hashing — each shard holds a range of hash values (e.g. 0–16383 for shard zero). Nearby keys (consecutive timestamps) scatter and big shards split like key-range; only range queries on the partition key itself get expensive. The composite-key trick (partition key in the first column, order from the rest) still preserves in-shard range reads. This scheme lives in YugabyteDB and DynamoDB and is one MongoDB option.

مسیرِ کاملِ «از کلید تا node» و دو شاخهٔ اصلی‌اش را در یک نگاه ببین:

See the full “key to node” path and its two main branches at a glance:

نمودار ۳ · Dataflow — از partition key تا node: مسیرهای hash shardingDiagram 3 · Dataflow — from partition key to node: hash-sharding paths باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار ۳Teacher narration of diagram 3

مثال فنی برای خواندنِ نمودار: تلهٔ تغییرِ مقسوم‌علیه

وضعیت اولیه: مقدار hash یک کلید 10 است؛ با سه سرور مقصد 10 % 3 = 1 می‌شود.

اجرای مرحله‌به‌مرحله: افزودن سرور چهارم آن را به 10 % 4 = 2 می‌برد؛ بسیاری از کلیدهای دیگر هم جابه‌جا می‌شوند.

نتیجه و دلیل: جدا کردن shard منطقی از سرور فیزیکی، تغییر تعداد ماشین‌ها را از بازتقسیم همهٔ کلیدها مستقل‌تر می‌کند.

شکل دو خطِ موازی در چهار مرحله دارد: «Partition Key»، «Hash»، «Shard Mapping» و «Rebalancing». خطِ بالا مسیرِ «کلیدِ عادی» است و خطِ پایین مسیرِ «کلیدهای خوشه‌ای».

در مرحلهٔ اولِ خطِ بالا، گرهِ «Partition Key» (زیرنویس: «key or key prefix»، برچسبِ «skewed input ok») نشسته است. فلشِ پررنگِ «hash the partition key» به گرهِ «Hash Function» می‌رود (زیرنویس: «Murmur3 or MD5»، برچسبِ «not cryptographic»). بعد فلشِ «map hash to a shard» به «Fixed Shards» (زیرنویسِ «hash(key) % 1000»، برچسبِ «count fixed up front») و سپس فلشِ «node joins: move shards» به «Move Whole Shards» (زیرنویس: «shard to node map»، برچسبِ «cheap rebalancing»).

خطِ پایین همان مسیر را برای داده‌ای که کلیدهایش در زمان خوشه می‌کنند باز می‌کند: گرهِ «Clustered Keys» (زیرنویس: «nearby keys cluster»، برچسبِ هشدارِ «hot shard risk») با فلشِ «nearby keys scatter apart» به «Uniform Spread» (زیرنویس: «ordering is lost»، برچسبِ «no key range scans») می‌رود؛ بعد «assign ranges of hashes» به «Hash Ranges» (زیرنویس: «ranges of hash values»، برچسبِ «count adapts to data») و سرانجام «shard too big: split it» به «Split and Merge» (زیرنویس: «rewrite to new files»، برچسبِ «expensive operation»).

درسِ شکل: دو تفاوتِ محوری در یک نگاه — «تعدادِ ثابت، جابه‌جاییِ کلِ shardها» در برابر «تعدادِ متغیر، شکافتن و بازنویسی». و هشدارِ دو خطِ پایین: hash یکنواخت‌شده می‌کند اما ترتیب را می‌سوزاند و hotspotِ کلیدِ داغ را هم درمان نمی‌کند.

The figure has two parallel lines in four stages: “Partition Key”, “Hash”, “Shard Mapping” and “Rebalancing”. The upper line is the “plain key” path and the lower the “clustered keys” path.

In the upper line's first stage sits the “Partition Key” node (caption: “key or key prefix”, label: “skewed input ok”). The bold “hash the partition key” arrow goes to the “Hash Function” node (caption: “Murmur3 or MD5”, label: “not cryptographic”). Then the “map hash to a shard” arrow to “Fixed Shards” (caption: “hash(key) % 1000”, label: “count fixed up front”) and then the “node joins: move shards” arrow to “Move Whole Shards” (caption: “shard to node map”, label: “cheap rebalancing”).

The lower line replays the same path for data whose keys cluster in time: the “Clustered Keys” node (caption: “nearby keys cluster”, warning label: “hot shard risk”) goes via “nearby keys scatter apart” to “Uniform Spread” (caption: “ordering is lost”, label: “no key range scans”); then “assign ranges of hashes” to “Hash Ranges” (caption: “ranges of hash values”, label: “count adapts to data”) and finally “shard too big: split it” to “Split and Merge” (caption: “rewrite to new files”, label: “expensive operation”).

The figure's lesson: two pivotal differences at a glance — “fixed count, moving whole shards” versus “variable count, splitting and rewriting”. And the two lower lines' warning: hashing evens out but burns order, and doesn't cure a hot key's hotspot either.

کد: تلهٔ mod N در برابر تعدادِ ثابتِ shardCode: the mod-N trap vs fixed shard count
# — نکن: با تغییرِ N بیشترِ کلیدها جابه‌جا می‌شوند
shard = hash(key) % N_nodes

# — بکن: نگاشتِ کلید به shard ثابت است؛ فقط shard→node تغییر می‌کند
N_SHARDS = 1000
shard_id = hash(key) % N_SHARDS          # هرگز تغییر نمی‌کند
node_of  = assign_shards_to_nodes(N_SHARDS, nodes)   # با اضافه شدنِ node، چند shard کامل منتقل می‌شود

تفاوت در یک جمله: در حالتِ دوم هنگامِ رشدِ خوشه، فقط «مالکیتِ shard» عوض می‌شود، نه «جایِ کلیدها».

# — don't: when N changes, most keys move
shard = hash(key) % N_nodes

# — do: key-to-shard mapping is fixed; only shard→node changes
N_SHARDS = 1000
shard_id = hash(key) % N_SHARDS          # never changes
node_of  = assign_shards_to_nodes(N_SHARDS, nodes)   # a few whole shards move when a node joins

The difference in one sentence: in the second case, cluster growth only changes “shard ownership”, not “where keys live”.

۲.۳ consistent hashing و انباره‌های داده2.3 Consistent hashing and data warehouses

در هش سازگار، فضای هش به بازه‌هایی میان گره‌ها واگذار می‌شود. اضافه شدن گره معمولاً فقط بخشی از بازه‌ها را جابه‌جا می‌کند؛ توازن همچنان به اندازه و تعداد بازه‌ها بستگی دارد.

مثال فنی: دایرهٔ hash و مالک جدید

وضعیت اولیه: توکن‌های A و B روی حلقه‌اند و هر کلید به مالک بعدی می‌رود.

اجرای مرحله‌به‌مرحله: با افزودن C فقط بازه‌هایی که C مالکشان می‌شود جابه‌جا می‌شوند؛ virtual nodeها می‌توانند تقسیم ظرفیت را ریزتر کنند.

نتیجه و دلیل: consistent یعنی پایداری نسبی نگاشت هنگام تغییر اعضا؛ به‌معنی تضمین consistency خواندن و نوشتن نیست.

Cassandra and ScyllaDB split the hash-value range (e.g. 0–1024) into contiguous ranges with random boundaries and give each node several ranges (default: 16 ranges per node in Cassandra, 256 in ScyllaDB). Some ranges are bigger but several ranges per node compensate the inequalities. When a node joins, boundaries shift and parts of ranges move — with no more transfer than needed.

Consistent hashing یک الگوریتمِ نگاشت با دو ویژگی است: (۱) تعدادِ کلیدهای هر shard تقریباً برابر باشد، (۲) با تغییرِ تعدادِ shard، کمترین تعدادِ کلید جابه‌جا شود. دقت کن «consistent» اینجا ربطی به consistencyِ replica (فصل ۶) یا ACID (فصل ۸) ندارد؛ یعنی «تمایلِ کلید به ماندن در جای خودش». خانواده‌های دیگر: rendezvous (highest random weight) و jump consistent hashing که به‌جای شکافتنِ بازه‌ها، کلیدهای پراکنده را به node تازه می‌دهند.

Consistent hashing is a mapping algorithm with two properties: (1) each shard holds roughly as many keys, (2) when the shard count changes, the fewest keys move. Note “consistent” here has nothing to do with replica consistency (Chapter 6) or ACID (Chapter 8); it means “a key's tendency to stay in its place”. Other families: rendezvous (highest random weight) and jump consistent hashing, which hand scattered keys to the fresh node instead of splitting ranges.

انباره‌های داده (Data Warehouses) هم همین مسئله را دارندData warehouses have the same problem

در BigQuery، partition key تعیین می‌کند رکورد در کدام partition باشد و cluster columns ترتیبِ درونِ partition را. Snowflake رکوردها را در micro-partitionهای خودکار می‌گذارد و cluster key به کاربر می‌دهد؛ Delta Lake هر دو را پشتیبانی می‌کند. خوشه‌بندی فقط range scan را بهتر نمی‌کند؛ فشرده‌سازی و filter را هم.

In BigQuery, the partition key decides which partition a record lands in and cluster columns order inside the partition. Snowflake places records in automatic micro-partitions and gives users a cluster key; Delta Lake supports both. Clustering doesn't just speed range scans; it improves compression and filtering too.

۳3 بارِ کج، کلیدِ داغ و rebalancingSkewed load, hot keys and rebalancing

پخشِ مساویِ تعدادِ رکوردها به‌معنی پخشِ مساویِ ترافیک نیست. باید حجم، نرخِ درخواست و هزینهٔ پردازشِ هر شارد را جدا ببینیم.

مثال فنی: کلید داغ با حجم کم

وضعیت اولیه: کلیدِ پست p9 فقط چند کیلوبایت است، ولی نیمی از درخواست‌ها شمارندهٔ آن را تغییر می‌دهند.

اجرای مرحله‌به‌مرحله: تقسیم داده بر hash همهٔ عملیات همین کلید را همچنان به یک محل می‌برد.

نتیجه و دلیل: توازن تعداد بایت یا کلید، توازن بار را تضمین نمی‌کند؛ یک hot key به روش مخصوص بار خودش نیاز دارد.

۳.۱ خنثی‌سازیِ hotspotها3.1 Neutralizing hotspots

کلید داغ یعنی یک کلید سهم زیادی از درخواست‌ها را بگیرد. هش، همان کلید را همچنان به یک مقصد می‌فرستد؛ برای توزیع بار آن باید طراحیِ داده یا خواندن را تغییر داد.

مثال فنی: شکستن شمارندهٔ داغ

وضعیت اولیه: شمارندهٔ بازدید p9 روی یک کلید به گلوگاه تبدیل شده است.

اجرای مرحله‌به‌مرحله: نوشتن‌ها به ۱۶ کلید p9:0…15 پخش می‌شوند؛ خواندن مجموع ۱۶ مقدار را حساب می‌کند.

نتیجه و دلیل: بار نوشتن پخش می‌شود، ولی خواندن و تضمینِ snapshot مشترک سخت‌تر می‌شود؛ برای موجودی سخت‌گیرانه همین الگو را بی‌بررسی به‌کار نمی‌بریم.

Consistent hashing spreads keys evenly, but that doesn't guarantee real load is even. If query load concentrates on some keys (a celebrity's post with millions of followers causing an activity storm), nodes still break under load while the rest idle.

  • جداسازیِ کلیدِ داغ: در طرح‌های مبتنی بر بازه (کلید یا hash)، می‌توان کلیدِ داغ را تنها در یک shard گذاشت و حتی ماشینِ اختصاصی به آن داد.
  • پیشوندِ تصادفی: افزودنِ دو رقمِ تصادفی به ابتدا یا انتهای کلید، نوشتنِ یک کلید را بین ۱۰۰ کلید پخش می‌کند. بها: خواندن‌ها باید ۱۰۰ کلید را بخوانند و ادغام کنند؛ فقط نوشتن پخش می‌شود، نه خواندن. ضمناً باید بدانی کدام کلیدها «داغ» شده‌اند و فرایندی برای تبدیلِ کلیدِ عادی به کلیدِ مدیریت‌شده داشته باشی.
  • تغییر در طولِ زمان: پستِ ویروسی چند روز داغ است و بعد آرام می‌شود؛ بعضی کلیدها نوشتن‌داغ‌اند و بعضی خواندن‌داغ — استراتژیِ هرکدام متفاوت است.
  • سیستم‌های ابری: راهکارهای خودکارِ «heat management» یا «adaptive capacity» (مثل DynamoDB) بار را جابه‌جا می‌کنند؛ جزئیاتشان خارج از کتاب است.
  • Isolating the hot key: in range-based schemes (key or hash), the hot key can sit alone in one shard and even get a dedicated machine.
  • Random prefix: adding two random digits to the key's start or end spreads one key's writes across 100 keys. Price: reads must fetch all 100 keys and merge; only writes spread, not reads. You must also know which keys turned “hot” and own a process for converting a plain key into a managed one.
  • Changing over time: a viral post is hot for days then calms; some keys are write-hot and some read-hot — each needs a different strategy.
  • Cloud systems: automatic “heat management” or “adaptive capacity” fixes (like DynamoDB) move load around; their details are outside the book.
نمودار مفهومی · مقایسه — سه راه خنثی‌سازی hotspotConcept sketch · Comparison — three hotspot fixes باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار مفهومیTeacher narration of the concept sketch

مثال فنی برای خواندنِ نمودار: شکستن شمارندهٔ داغ

وضعیت اولیه: شمارندهٔ بازدید p9 روی یک کلید به گلوگاه تبدیل شده است.

اجرای مرحله‌به‌مرحله: نوشتن‌ها به ۱۶ کلید p9:0…15 پخش می‌شوند؛ خواندن مجموع ۱۶ مقدار را حساب می‌کند.

نتیجه و دلیل: بار نوشتن پخش می‌شود، ولی خواندن و تضمینِ snapshot مشترک سخت‌تر می‌شود؛ برای موجودی سخت‌گیرانه همین الگو را بی‌بررسی به‌کار نمی‌بریم.

سه ستون، سه پاسخ به «بار یک کلید داغ را کجا ببریم؟» است. ستون اول: کلید داغ را تنها در یک shard (حتی یک ماشین اختصاصی) بگذار — ساده است اما باید کلیدهای داغ را پیدا کنی و گرما با زمان جابه‌جا می‌شود. ستون وسط: با پسوند تصادفی، نوشتنِ یک کلید را بین ۱۰۰ کلید پخش کن — اما فقط نوشتن پخش می‌شود و خواندن باید هر ۱۰۰ تا را بخواند و ادغام کند.

ستون سوم: مدیریت تطبیقیِ ابری (مثل DynamoDB) بار را خودش جابه‌جا می‌کند — بی‌زحمت اما کم‌شفافیت. درس شکل در پانوشت است: hash کلیدها را پخش می‌کند، نه بار را؛ و «داغِ نوشتنی» با «داغِ خواندنی» دو درمان متفاوت می‌خواهد.

Three columns answer “where do we put one hot key's load?” First column: place the hot key alone in one shard (even a dedicated machine) — simple, but you must find hot keys, and heat moves over time. Middle column: with a random suffix, spread one key's writes across 100 keys — but only writes spread, and reads must fetch and merge all 100.

Third column: adaptive cloud management (like DynamoDB) moves load itself — effortless but opaque. The diagram's lesson is in the footer: hashing spreads keys, not load; and “write-hot” vs “read-hot” need different cures.

کد: پیشوندِ تصادفی برای کلیدِ داغCode: random prefix for a hot key
# نوشتنِ یک کلیدِ بسیار داغ — پخش بین ۱۰۰ کلید
suffix = random_digits(2)                 # 00..99 — فقط برای کلیدهای داغ
key    = f"{user_id}:{suffix}"
put(key, value)

# خواندن — ترکیبِ همهٔ نسخه‌ها (فقط نوشتن پخش شد، نه خواندن!)
values = [get(f"{user_id}:{s}") for s in range(100)]
result = merge(values)                    # مثلاً جمعِ شمارنده‌ها
# writing a very hot key — spread across 100 keys
suffix = random_digits(2)                 # 00..99 — only for hot keys
key    = f"{user_id}:{suffix}"
put(key, value)

# reading — combining all versions (only writes spread, not reads!)
values = [get(f"{user_id}:{s}") for s in range(100)]
result = merge(values)                    # e.g. summing counters

۳.۲ rebalancing خودکار یا دستی؟3.2 Automatic or manual rebalancing?

بازمتوازن‌سازی یعنی انتقال بخشی از داده و مسئولیت میان گره‌ها. خودکار بودن واکنش را سریع‌تر می‌کند، اما انتقالِ زیاد هنگام فشار می‌تواند اوضاع را بدتر کند.

مثال فنی: مهاجرت در وقت نامناسب

وضعیت اولیه: یک نود کند شده و کنترل‌گر می‌خواهد 200 GB دادهٔ آن را جابه‌جا کند.

اجرای مرحله‌به‌مرحله: انتقال شبکه و دیسک مصرف می‌کند و ممکن است کندی را تشدید کند؛ محدودیت نرخ و بررسی علت کندی لازم است.

نتیجه و دلیل: کندی موقت همیشه نیاز به rebalance نیست؛ حلقهٔ خودکار بدون مهار می‌تواند ازدحام را بیشتر کند.

Whenever a node joins or leaves, or data grows unbalanced, data must be respread (rebalancing). Three approaches exist:

رویکردمزیتخطر
کاملاً خودکار کارِ عملیاتیِ کمتر؛ بعضی سیستم‌های ابری (مثل DynamoDB) در چند دقیقه shard اضافه/کم می‌کنند و autoscale می‌شوند غیرقابل‌پیش‌بینی: rebalancing گران است و ممکن است شبکه یا nodeها را زیرِ بار ببرد؛ اگر سیستم نزدیکِ حداکثرِ نوشتن باشد، split از ورودی عقب می‌افتد
میانه (پیشنهاد خودکار، تأیید انسان) مثل Couchbase و Riak: طرحِ پیشنهادی خودکار ساخته می‌شود اما ادمین باید commit کند کندتر، ولی از «شگفتیِ عملیاتی» جلوگیری می‌کند
دستی کنترلِ کامل؛ مناسبِ rebalancingِ پیشگیرانه قبل از رویدادهایی مثل فروشِ جمعهٔ سیاه یا بلیتِ جام جهانی کارِ انسانی و کُند در بحران
ApproachAdvantageRisk
Fully automaticLess operational work; some cloud systems (like DynamoDB) add/remove shards in minutes and autoscaleUnpredictable: rebalancing is expensive and may swamp the network or nodes; if the system is near max writes, splits fall behind input
Middle (automatic proposal, human commit)Like Couchbase and Riak: the proposal is built automatically but an admin must commit itSlower, but avoids “operational surprises”
ManualFull control; fits preventive rebalancing before events like Black Friday sales or World Cup ticketsHuman work, and slow in a crisis
ترکیبِ خطرناک: rebalancing خودکار + تشخیصِ خودکارِ خرابیDangerous mix: automatic rebalancing + automatic failure detection

اگر nodeی فقط کند شده باشد و بقیه گمان کنند مرده، خوشه خودکار بار را از او می‌گیرد و به بقیه می‌دهد — یعنی دقیقاً به سیستمِ زیرِ فشار، فشارِ بیشتر تحمیل می‌شود. نتیجه می‌تواند خرابیِ آبشاری باشد: nodeهای بعدی هم زیرِ بار بیفتند و به‌غلط مرده اعلام شوند. به همین دلیل «انسان در حلقه» برای rebalancing اغلب امن‌تر است.

If a node is merely slow and the rest deem it dead, the cluster automatically takes its load and gives it to the rest — imposing more pressure on exactly the system under pressure. The result can be a cascading failure: the next nodes fall under load too and are wrongly declared dead. That is why a “human in the loop” is often safer for rebalancing.

نمودار مفهومی · مقایسه — سه حالت rebalancingConcept sketch · Comparison — three rebalancing modes باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار مفهومیTeacher narration of the concept sketch

مثال فنی برای خواندنِ نمودار: مهاجرت در وقت نامناسب

وضعیت اولیه: یک نود کند شده و کنترل‌گر می‌خواهد 200 GB دادهٔ آن را جابه‌جا کند.

اجرای مرحله‌به‌مرحله: انتقال شبکه و دیسک مصرف می‌کند و ممکن است کندی را تشدید کند؛ محدودیت نرخ و بررسی علت کندی لازم است.

نتیجه و دلیل: کندی موقت همیشه نیاز به rebalance نیست؛ حلقهٔ خودکار بدون مهار می‌تواند ازدحام را بیشتر کند.

سه ستون از «بی‌دخالت‌ترین» تا «پردخالت‌ترین» مرتب‌اند: کاملاً خودکار (بی‌زحمت اما غیرقابل‌پیش‌بینی)، پیشنهاد خودکار با تأیید انسان (پیش‌فرض امن‌تر: مثل Couchbase و Riak) و دستی (کنترل کامل برای rebalancing پیشگیرانه پیش از رویدادهای پربار). سطرهای «مزیت» و «بها» را ستون‌به‌ستون مقایسه کن تا بفهمی هر حالت چه می‌خرد و چه می‌فروشد.

نوار صورتی پایین، هشدار اصلی بخش است: ترکیب «rebalancing خودکار» با «تشخیص خودکار خرابی» می‌تواند خرابی آبشاری بسازد — node کند «مرده» اعلام می‌شود، خوشه بارش را جابه‌جا می‌کند و فشار روی فشار می‌نشیند. برای همین در عمل، «انسان در حلقه» اغلب امن‌تر است.

Three columns run from “most hands-off” to “most hands-on”: fully automatic (effortless but unpredictable), automatic proposal with human commit (the safer default: like Couchbase and Riak), and manual (full control for preventive rebalancing before heavy events). Compare the “plus” and “minus” rows column by column to see what each mode buys and sells.

The pink bar at the bottom is the section's main warning: mixing “automatic rebalancing” with “automatic failure detection” can build a cascading failure — a slow node is declared “dead”, the cluster moves its load, and pressure piles on pressure. That is why in practice a “human in the loop” is usually safer.

۴4 مسیریابیِ درخواست‌ها (Request Routing)Request routing

مسیریاب باید بداند هر کلید در کدام شارد است. این آگاهی ممکن است در کلاینت، لایهٔ مسیریابی یا گرهی باشد که درخواست را به مقصد درست می‌فرستد.

مثال فنی: سه راه پیدا کردن shard

وضعیت اولیه: نقشه می‌گوید کلید 17 اکنون روی B است.

اجرای مرحله‌به‌مرحله: کلاینتِ آگاه مستقیم به B می‌رود؛ proxy درخواست را هدایت می‌کند؛ یا نود نخست آن را forward می‌کند.

نتیجه و دلیل: هر راه یک هزینه و محل نگهداری نقشه دارد؛ service discovery فقط پیدا کردن instance است و لزوماً مالک کلید را نمی‌گوید.

Now shards have found their places. Next question: to read or write a given key, which node — which IP and port — do you connect to? That job is request routing, very like service discovery (the “load balancers and service meshes” section in Chapter 5). The big difference: application services are usually stateless and any instance can answer any request; but a key's request must only go to a node holding a replica of that key's owning shard.

۴.۱ نمودار ۴ + روایت مدرس4.1 Diagram 4 + teacher narration

سه رویکردِ معروف: (۱) درخواست به هر node برسد و آن node forward کند، (۲) یک routing tier که خودش درخواست را اجرا نمی‌کند و فقط load balancerِ shard-aware است، (۳) کلاینتِ shard-aware که مستقیم به node درست وصل می‌شود. نمودارِ زیر رویکردِ دوم را با هماهنگ‌سازیِ ZooKeeper و گرهِ گذرای جابه‌جاییِ shard نشان می‌دهد:

Three famous approaches: (1) a request reaches any node and that node forwards it, (2) a routing tier that executes no requests itself and is only a shard-aware load balancer, (3) a shard-aware client connecting straight to the right node. The diagram below shows the second approach with ZooKeeper coordination and a transient shard-move node:

نمودار ۴ · Sequence — مسیریابیِ درخواست، جابه‌جاییِ shard و پنجرهٔ cutoverDiagram 4 · Sequence — request routing, shard moves and the cutover window باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار ۴Teacher narration of diagram 4

مثال فنی برای خواندنِ نمودار: سه راه پیدا کردن shard

وضعیت اولیه: نقشه می‌گوید کلید 17 اکنون روی B است.

اجرای مرحله‌به‌مرحله: کلاینتِ آگاه مستقیم به B می‌رود؛ proxy درخواست را هدایت می‌کند؛ یا نود نخست آن را forward می‌کند.

نتیجه و دلیل: هر راه یک هزینه و محل نگهداری نقشه دارد؛ service discovery فقط پیدا کردن instance است و لزوماً مالک کلید را نمی‌گوید.

پنج ستونِ زمانی داریم: «Client»، «Routing Tier» (زیرنویس: «shard-aware balancer»)، «ZooKeeper / etcd» (زیرنویس: «authoritative shard map»)، «Node A» (زیرنویس: «owner of shard 5») و «Node B» (زیرنویس: «new owner of shard 5»). سه بخشِ جدا با عنوان بالای صفحه داریم.

بخشِ اول، «Route a request»: پیامِ «read or write key K» از Client به Routing Tier می‌رود؛ Routing Tier با «which node owns shard 5?» (یادداشت: «routing must know keys and shards») از ZooKeeper می‌پرسد و پاسخِ برگشتیِ «Node A — authoritative map» را می‌گیرد؛ سپس فلشِ پررنگِ «forward to the owning node» درخواست را به Node A می‌رساند.

بخشِ دوم، «Shard 5 moves to Node B»: فلشِ چین‌چینِ «stream the shard 5 data» از Node A به Node B (یادداشتِ «transfer takes time»)؛ بعد فلشِ پررنگِ «take ownership of shard 5» از Node B به ZooKeeper؛ و اعلانِ پررنگِ «notify: shard 5 → Node B» از ZooKeeper به Routing Tier (یادداشت: «subscribers keep routing up to date»).

بخشِ سوم، «Cutover and in-flight requests»: درخواستی که هنوز به مالکِ قدیمی در پرواز است — «request still in flight to the old owner» از Client به Node A — و پاسخِ پررنگِ «forward or reject during cutover» از Node A به Node B، و در نهایت «reply from the new owner» به Client. این دقیقاً همان پنجرهٔ خطرناکِ جابه‌جایی است.

درسِ شکل: مسیریابی یعنی «دانستنِ نگاشتِ کلید→shard→node» و «به‌روز نگه‌داشتنِ آن»؛ و جابه‌جاییِ shard فقط کپیِ داده نیست، یک cutover با درخواست‌های در پرواز دارد که باید forward یا رد شوند. کارت‌ها سه روشِ مسیریابی، نقشِ سرویسِ هماهنگی، و پرسش‌های سخت (split brain، DNS، کوئری‌های تحلیلی) را جمع کرده‌اند.

We have five timelines: “Client”, “Routing Tier” (caption: “shard-aware balancer”), “ZooKeeper / etcd” (caption: “authoritative shard map”), “Node A” (caption: “owner of shard 5”) and “Node B” (caption: “new owner of shard 5”). The page has three titled sections.

Section one, “Route a request”: the “read or write key K” message runs from Client to Routing Tier; the Routing Tier asks ZooKeeper “which node owns shard 5?” (note: “routing must know keys and shards”) and takes back the answer “Node A — authoritative map”; then the bold “forward to the owning node” arrow delivers the request to Node A.

Section two, “Shard 5 moves to Node B”: the dashed “stream the shard 5 data” arrow from Node A to Node B (note: “transfer takes time”); then the bold “take ownership of shard 5” arrow from Node B to ZooKeeper; and the bold “notify: shard 5 → Node B” announcement from ZooKeeper to the Routing Tier (note: “subscribers keep routing up to date”).

Section three, “Cutover and in-flight requests”: a request still in flight to the old owner — “request still in flight to the old owner” from Client to Node A — and the bold reply “forward or reject during cutover” from Node A to Node B, and finally “reply from the new owner” to the Client. That is exactly the move's dangerous window.

The figure's lesson: routing means “knowing the key→shard→node map” and “keeping it fresh”; and moving a shard is not just copying data — it has a cutover with in-flight requests that must be forwarded or rejected. The cards gather the three routing styles, the coordination service's role, and the hard questions (split brain, DNS, analytical queries).

۴.۲ سرویسِ هماهنگی و خطرِ split brain4.2 Coordination services and split-brain risk

  • چه کسی تصمیم می‌گیرد؟ ساده‌ترین حالت یک coordinator؛ اما اگر آن node بیفتد چه؟ و اگر نقش failover شود، چطور جلوی split brain (دو coordinator با تصمیم‌های متضاد) گرفته شود؟
  • چه کسی از تغییرات باخبر می‌شود؟ routing tier یا کلاینت باید از تغییرِ مالکیتِ shard مطلع شوند.
  • درخواست‌های در پرواز حینِ cutover: مالکِ تازه کار را تحویل گرفته، اما درخواست‌هایی هنوز به مالکِ قدیمی می‌رسند.
  • Who decides? The simplest case is one coordinator; but what if that node fails? And if the role fails over, how is split brain (two coordinators with conflicting decisions) prevented?
  • Who learns of changes? The routing tier or client must be told of shard-ownership changes.
  • In-flight requests at cutover: the fresh owner has taken over, but requests still reach the old owner.

سرویس هماهنگی محل مشترکی برای نگاشت‌ها و مالکیت‌هاست. اگر دو گره هم‌زمان مالک یک شارد فرض شوند، ممکن است هر دو تغییرهای ناسازگار بپذیرند.

مثال فنی: مالکیت با epoch

وضعیت اولیه: نقشهٔ قدیمی shard S را با epoch ۷ روی A و نقشهٔ تازه با epoch ۸ روی B نشان می‌دهد.

اجرای مرحله‌به‌مرحله: مقصد باید نوشتنِ متعلق به مالکیت منقضی را رد یا هدایت کند؛ دو نسخهٔ نقشه نباید دو نویسندهٔ معتبر بسازند.

نتیجه و دلیل: سرویس هماهنگی نقشه را هماهنگ می‌کند، اما اعمال fencing در مسیر داده هم لازم است.

Many systems keep a separate coordination service like ZooKeeper or etcd: each node registers itself, the authoritative shard→node map lives there, and the routing tier or shard-aware client subscribe to it. Every ownership change notifies them. These services are shielded against failure and split brain by a consensus algorithm (Chapter 10).

سیستممکانیزمِ هماهنگی
HBase، SolrCloudZooKeeper
Kubernetesetcd برای دانستنِ محلِ هر instance سرویس
MongoDBconfig serverهای اختصاصی + mongos به‌عنوان routing tier
Kafka، YugabyteDB، TiDB، ScyllaDBپیاده‌سازیِ درونیِ Raft برای همین هماهنگی
Riakgossip بین nodeها — سازگاریِ بسیار ضعیف‌تر و امکانِ split brain (که پایگاه دادهِ بدون‌رهبر تحمل می‌کند)
SystemCoordination mechanism
HBase, SolrCloudZooKeeper
Kubernetesetcd for knowing where each service instance lives
MongoDBDedicated config servers + mongos as the routing tier
Kafka, YugabyteDB, TiDB, ScyllaDBIn-house Raft implementation for that same coordination
Riakgossip between nodes — far weaker consistency and possible split brain (which a leaderless database tolerates)

آدرس‌های IP سرورها کُندتر تغییر می‌کنند، پس برای یافتنِ خودِ nodeها معمولاً DNS کافی است. و نکتهٔ آخر: این بحث دربارهٔ یافتنِ shardِ یک کلید بود (مناسبِ OLTP)؛ در پایگاه‌داده‌های تحلیلی، یک کوئری معمولاً باید دادهٔ بسیاری از shardها را موازی جمع و join کند — مسئله‌ای متفاوت که در فصل ۱۱ می‌آید.

Server IP addresses change more slowly, so DNS usually suffices for finding the nodes themselves. And a last note: this discussion was about finding one key's shard (fit for OLTP); in analytical databases one query usually must gather and join many shards' data in parallel — a different problem arriving in Chapter 11.

۴.۳ چرخهٔ حیاتِ جابه‌جاییِ shard + نمودار ۶4.3 The shard-move lifecycle + diagram 6

انتقال شارد معمولاً شامل کپی اولیه، دریافت تغییرهای جدید، عوض کردن مقصد و جمع‌کردن نسخهٔ قدیمی است. نقطهٔ تغییرِ مالکیت باید مشخص باشد.

مثال فنی: Cutover بدون گم‌شدنِ نوشتن

وضعیت اولیه: shard از A به B منتقل می‌شود؛ snapshot تا موقعیت 500 کپی شده است.

اجرای مرحله‌به‌مرحله: تغییرهای بعدی تا 560 می‌رسند؛ با سازوکار پروتکل، نوشتن نهایی همگام و مالکیت به epoch تازه منتقل می‌شود.

نتیجه و دلیل: اگر نقشه پیش از رساندن تغییرها عوض شود، B می‌تواند دادهٔ قدیمی نشان دهد؛ اگر دو طرف بنویسند، تعارض می‌سازند.

Moving one shard from node A to node B is no “single-step command”; it is a state machine. Data must be copied, writes arriving mid-copy must be followed, and ownership flips at only one defined instant. If we name each moment of that path a “state” and make transitions explicit, we understand what failure at each stage means and what the system must decide then — instead of picturing the move as one vague, irreversible process.

حسّاس‌ترین لحظه، پنجرهٔ cutover است: همان بازهٔ کوتاهی که نگاشتِ shard سوییچ می‌شود و هنوز درخواست‌هایی به مالکِ قدیمی در پروازند. در این پنجره دو نوع مشتری داریم: آن‌هایی که درخواست‌شان هنوز به node قدیمی می‌رسد (و باید forward یا رد شود) و آن‌هایی که نگاشتِ قدیمی را کش کرده‌اند و اصلاً خبر ندارند مالک عوض شده — و نوشتن‌هایشان می‌تواند در جای اشتباه بنشیند.

The most sensitive moment is the cutover window: that short span when the shard map switches while requests still fly to the old owner. In that window there are two customer kinds: those whose requests still reach the old node (and must be forwarded or rejected) and those who cached the old map and never heard the owner changed — whose writes may land in the wrong place.

و نکتهٔ آرام‌شبخشِ این چرخه: اگر جابه‌جایی وسطِ راه شکست بخورد، هیچ داده‌ای گم نمی‌شود. تا وقتی نگاشتِ shard به‌روز نشده، مالکِ هنوز A است و کپیِ ناقصِ روی B فقط داده‌ای اضافی است که می‌توان دور ریخت. نمودارِ زیر این چرخه را به‌صورتِ UML-style نشان می‌دهد — با مسیرِ اصلی، مسیرِ سقوط و مسیرِ گیرافتادنِ نوشتن‌ها:

And this cycle's reassuring note: if the move fails midway, no data is lost. Until the shard map updates, the owner is still A, and the incomplete copy on B is just extra data to discard. The diagram below shows that cycle UML-style — with the main path, the fall path and the stranded-writes path:

نمودار ۶ · Lifecycle — چرخهٔ جابه‌جاییِ shard تا cutoverDiagram 6 · Lifecycle — the shard-move cycle to cutover باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار ۶Teacher narration of diagram 6

مثال فنی برای خواندنِ نمودار: Cutover بدون گم‌شدنِ نوشتن

وضعیت اولیه: shard از A به B منتقل می‌شود؛ snapshot تا موقعیت 500 کپی شده است.

اجرای مرحله‌به‌مرحله: تغییرهای بعدی تا 560 می‌رسند؛ با سازوکار پروتکل، نوشتن نهایی همگام و مالکیت به epoch تازه منتقل می‌شود.

نتیجه و دلیل: اگر نقشه پیش از رساندن تغییرها عوض شود، B می‌تواند دادهٔ قدیمی نشان دهد؛ اگر دو طرف بنویسند، تعارض می‌سازند.

این نمودار برخلافِ نمودارِ ۴ که یک sequence بود، یک lifecycle است: چهار lane افقی داریم که هر lane مسیرِ متفاوتی از همین داستان را نگه می‌دارد — «Ownership» در بالا (مسیرِ اصلیِ مالکیت)، «In-flight requests» (درخواست‌های در پرواز)، «Abort path» (مسیرِ خرابی) و «Terminal» (حالت‌های پایانی). stateهای مسیرِ اصلی شمارهٔ گام دارند، از 01 تا 05؛ پس روایت را از گامِ اول شروع می‌کنیم.

گامِ 01، stateیِ «Serving on A»: nodeای که فعلاً shard را سرو می‌کند. زیرنویسش «node A owns the shard» است و برچسبِ گوشه‌اش «stable assignment» — یعنی در این حالت نگاشتِ shard آرام و معتبر است. فلشِ گذارِ خروجی «node joins» نام دارد: همان لحظه‌ای که node تازه‌ای به خوشه اضافه می‌شود و rebalancing تصمیم می‌گیرد shard را جابه‌جا کند. می‌رسیم به گامِ 02، «Copying Data» با زیرنویسِ «stream the shard to B» و برچسبِ «takes time» — تأکید همین است: کپی کُند است و دنیا منتظرش نمی‌ماند. گذارِ بعدی «bulk copy done» است و می‌رسیم به گامِ 03، «Catch-Up» با زیرنویسِ «forward or buffer writes» و برچسبِ «lag must approach zero»: نوشتن‌هایی که حینِ کپی رسیده‌اند باید دنبال شوند تا عقب‌افتادگی تقریباً صفر شود.

وقتی عقب‌افتادگی کم شد، فلشِ «lag near zero» ما را به گامِ 04 می‌رساند: «Cutover» با زیرنویسِ «shard map updated» و برچسبِ «ownership switches». این state شکلش متفاوت است (یک stateِ تصمیم) چون تنها لحظه‌ای است که دنیای بیرون باید تغییر کند: نگاشتِ shard در همین‌جا به‌صورتِ atomic عوض می‌شود. فلشِ پررنگِ خروجی «map: shard to B» (فلشِ تأکیدشده) به گامِ 05 می‌رسد: «Serving on B» با زیرنویسِ «node B owns the shard» و برچسبِ «new assignment» — و مقایسه کنید با برچسبِ گامِ اولِ «stable assignment»: چرخه بسته شد، فقط مالک عوض شده است.

حالا پنجرهٔ cutover. از stateیِ «Cutover» یک فلشِ چین‌چین به پایین می‌آید: «requests still in flight» به stateیِ «Old Requests» در laneِ «In-flight requests» — با زیرنویسِ «still heading for A» و برچسبِ «in the cutover window». این state دو راهِ خروج دارد و سرنوشتشان زمین تا آسمان فرق دارد. راهِ اول فلشِ «forward or reject» است که به «Serving on B» برمی‌گردد — یعنی مالکِ قدیمی درخواستِ دیررسیده را به مالکِ تازه forward می‌کند یا مؤدبانه رد می‌کند و کلاینت دوباره تلاش می‌کند. راهِ دوم فلشِ قرمزِ «client cached old map» است که مستقیم به laneِ پایانی می‌ریزد: stateیِ شکستِ «Stranded Writes» با زیرنویسِ «stale routing table» و برچسبِ «writes to the wrong node». یعنی کلاینتِ نگاشتِ قدیمی هنوز به node قدیمی می‌نویسد و نوشتنِ او در جایی می‌نشیند که دیگر مالک نیست — دقیقاً همان فاجعه‌ای که cutover باید جلویش را بگیرد.

و مسیرِ سوم، مسیرِ سقوط. از stateیِ «Copying Data» یک فلشِ قرمز پایین می‌رود: «network or disk fails» به stateیِ «Move Aborted» در laneِ «Abort path» — با زیرنویسِ «map still says A» و برچسبِ «no data lost». و نکتهٔ کلیدی همین فلشِ برگشتیِ پررنگ است: «A still owns it» ما را به «Serving on A» برمی‌گرداند. چرا هیچ‌چیز گم نشده؟ چون نگاشتِ shard هیچ‌وقت به‌روز نشده بود؛ پس A هنوز مالکِ معتبر است، کپیِ ناقصِ روی B فقط داده‌ای قابلِ حذف است، و سیستم به همان حالتِ پایدارِ اول برمی‌گردد.

درسِ این شکل در یک جمله: نگاشتِ shard تنها مرجعِ مالکیت است. تا وقتی نگاشت نگفته مالک عوض شده، مالکِ قدیمی همچنان صاحبِ تصمیم است و هر جابه‌جاییِ ناتمامی قابلِ لغو است — این همان چیزی است که «Move Aborted» را به یک حالتِ امن بدل می‌کند نه یک فاجعه. از آن طرف، تمامِ خطرِ «Stranded Writes» از یک جا می‌آید: کسی به نگاشتِ قدیمی تکیه کرده است؛ پس یا باید در پنجرهٔ cutover درخواست‌ها را forward یا رد کرد، یا کشِ نگاشتِ کلاینت‌ها را کوتاه و باطل‌کردنی نگه داشت.

و قانونِ عملیِ آخر: تا تأییدِ مالکِ تازه، چیزی را از مبدأ پاک نکن. دادهٔ روی A را فقط بعد از آن می‌توان حذف کرد که نگاشتِ جدید ثبت شده، B سرویس‌دهی‌اش را تأیید کرده، و هیچ درخواستِ در پروازِ دیگری باقی نمانده باشد. حذفِ زودهنگام دقیقاً همان چیزی است که یک abortِ بی‌ضرر را به «Stranded Writes»‌ای دائمی تبدیل می‌کند.

Unlike diagram 4, a sequence, this diagram is a lifecycle: four horizontal lanes each holding a different path of the same story — “Ownership” on top (the main ownership path), “In-flight requests” (in-flight requests), “Abort path” (the failure path) and “Terminal” (terminal states). Main-path states carry step numbers, 01 to 05; so we start the story at step one.

Step 01, the “Serving on A” state: the node currently serving the shard. Its caption is “node A owns the shard” and its corner label “stable assignment” — in this state the shard map is calm and authoritative. The outgoing transition arrow is named “node joins”: the moment a fresh node joins the cluster and rebalancing decides to move the shard. We reach step 02, “Copying Data” captioned “stream the shard to B” and labelled “takes time” — the emphasis is just that: copying is slow and the world doesn't wait. The next transition “bulk copy done” brings step 03, “Catch-Up” captioned “forward or buffer writes” and labelled “lag must approach zero”: writes that arrived mid-copy must be followed until the lag is near zero.

When the lag shrinks, the “lag near zero” arrow carries us to step 04: “Cutover” captioned “shard map updated” and labelled “ownership switches”. This state's shape differs (a decision state) because it is the only instant the outside world must change: the shard map flips atomically right here. The bold outgoing “map: shard to B” arrow (the emphasized arrow) reaches step 05: “Serving on B” captioned “node B owns the shard” and labelled “new assignment” — compare with step one's “stable assignment” label: the cycle closed, only the owner changed.

Now the cutover window. A dashed arrow drops from the “Cutover” state: “requests still in flight” to the “Old Requests” state in the “In-flight requests” lane — captioned “still heading for A”, labelled “in the cutover window”. That state has two exits with utterly different fates. The first is the “forward or reject” arrow back to “Serving on B” — the old owner forwards the late request to the fresh owner or politely rejects it for the client to retry. The second is the red “client cached old map” arrow pouring straight into the terminal lane: the failed “Stranded Writes” state captioned “stale routing table” and labelled “writes to the wrong node”. An old-mapped client still writes to the old node, and its write lands where nobody owns — exactly the disaster cutover must prevent.

And the third path, the fall path. A red arrow drops from the “Copying Data” state: “network or disk fails” to the “Move Aborted” state in the “Abort path” lane — captioned “map still says A”, labelled “no data lost”. And the key point is that bold return arrow: “A still owns it” carries us back to “Serving on A”. Why was nothing lost? Because the shard map never updated; so A is still the authoritative owner, the incomplete copy on B is just deletable data, and the system returns to that same stable first state.

This figure's lesson in one sentence: the shard map is the only ownership authority. Until the map says the owner changed, the old owner still owns decisions, and any unfinished move is cancellable — which turns “Move Aborted” into a safe state, not a disaster. Conversely, all of “Stranded Writes”' danger comes from one place: someone relied on an old map; so either requests must be forwarded or rejected in the cutover window, or clients' map caches kept short and invalidatable. And the final practical rule: delete nothing from the source until the fresh owner confirms. Data on A may be removed only after the new map is registered, B confirmed serving, and no in-flight request remains. Early deletion is exactly what turns a harmless abort into permanent “Stranded Writes”.

۵5 ایندکس‌های ثانویه در دنیای shardهاSecondary indexes in a sharded world

وقتی کلید جست‌وجو با کلید تقسیم فرق دارد، پیدا کردن رکورد ممکن است چند شارد را درگیر کند. ایندکس ثانویه باید این فاصله را پوشش دهد.

مثال فنی: query با کلیدِ متفاوت

وضعیت اولیه: کالاها بر product_id تقسیم شده‌اند، اما کاربر color=red را می‌خواهد.

اجرای مرحله‌به‌مرحله: این شرط shard کالا را معلوم نمی‌کند؛ یا هر shard ایندکس محلی خود را می‌خواند یا ایندکس جهانی مقصدها را معرفی می‌کند.

نتیجه و دلیل: ایندکس ثانویه به طرح جدا نیاز دارد، چون کلید جست‌وجو با کلید تقسیم یکی نیست.

All the schemes above assume the client knows the record's partition key (in a key-value store, the primary key). But a secondary index doesn't identify a record; it is a search path: “all of user 123's actions”, “all articles containing hogwash”, “all red cars”. The problem: a secondary index doesn't stick to shards — two fixes: local and global.

۵.۱ نمودار ۵ + روایت مدرس5.1 Diagram 5 + teacher narration

مقایسهٔ دو مسیرِ نوشتن و خواندن — از ثبتِ رکورد تا جوابِ کوئری:

Comparing the two write and read paths — from record registration to query answer:

نمودار ۵ · Dataflow — ایندکسِ ثانویهٔ local در برابر globalDiagram 5 · Dataflow — local vs global secondary indexes باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار ۵Teacher narration of diagram 5

مثال فنی برای خواندنِ نمودار: محلی یا جهانی برای رنگ

وضعیت اولیه: کالای P7 روی A قرمز است و کالای P9 روی B قرمز است.

اجرای مرحله‌به‌مرحله: ایندکس محلی برای query قرمز نیاز به خواندن A و B دارد؛ ایندکس جهانی قرمز هر دو شناسه را کنار هم نگه می‌دارد.

نتیجه و دلیل: جهانی خواندن را متمرکزتر می‌کند، اما تغییر رنگ یا حذف کالا باید ایندکسِ احتمالاً دوردست را هم اصلاح کند.

شکل دو خطِ موازی در چهار مرحله دارد: «Write»، «Index Maintenance»، «Query» و «Results». خطِ بالا مسیرِ ایندکسِ local و خطِ پایین مسیرِ global است.

خطِ بالا با گرهِ «Write a Record» (زیرنویس: «partition key known»، برچسبِ «one shard touched») شروع می‌شود؛ فلشِ پررنگِ «update only this shard» به «Local Index» (زیرنویس: «per-shard postings»، برچسبِ «document-partitioned») می‌رود. بعد فلشِ پررنگِ «query fans out to all» به «Scatter Query» (زیرنویس: «ask every shard»، برچسبِ «no partition key») و سرانجام «merge partial answers» به «Merge Results» (زیرنویس: «tail latency grows»، برچسبِ «query throughput flat»).

خطِ پایین متفاوت است: گرهِ «Write a Record» این‌بار زیرنویسِ «terms span all shards» و برچسبِ «many index writes» دارد؛ فلشِ «each term may live elsewhere» به «Global Index» (زیرنویس: «sharded by term»، برچسبِ «term-partitioned») می‌رود. خواندن با فلشِ «postings in a single shard» به «Term Lookup» (زیرنویس: «one shard per term»، برچسبِ «postings from 1 shard») و بعد «IDs to owning shards» به «Fetch by ID» (زیرنویس: «set intersection»، برچسبِ «records from N shards»).

درسِ شکل: local نوشتن را ارزان و خواندن را گران می‌کند (پراکنده‌خوانی به همهٔ shardها)؛ global برعکس — خواندنِ لیستِ posting از یک shard می‌آید اما نوشتنِ یک رکورد ممکن است چند shard ایندکس را درگیر کند. انتخابِ بین این دو یعنی انتخابِ سمتِ گرانِ ماجرا.

The figure has two parallel lines in four stages: “Write”, “Index Maintenance”, “Query” and “Results”. The upper line is the local-index path, the lower the global path.

The upper line starts with the “Write a Record” node (caption: “partition key known”, label: “one shard touched”); the bold “update only this shard” arrow goes to “Local Index” (caption: “per-shard postings”, label: “document-partitioned”). Then the bold “query fans out to all” arrow to “Scatter Query” (caption: “ask every shard”, label: “no partition key”) and finally “merge partial answers” to “Merge Results” (caption: “tail latency grows”, label: “query throughput flat”).

The lower line differs: the “Write a Record” node now carries the caption “terms span all shards” and the label “many index writes”; the “each term may live elsewhere” arrow goes to “Global Index” (caption: “sharded by term”, label: “term-partitioned”). Reads go via “postings in a single shard” to “Term Lookup” (caption: “one shard per term”, label: “postings from 1 shard”) and then “IDs to owning shards” to “Fetch by ID” (caption: “set intersection”, label: “records from N shards”).

The figure's lesson: local makes writes cheap and reads expensive (scatter reads to all shards); global is the reverse — a postings list reads from one shard, but writing one record may engage several index shards. Choosing between them means choosing which side gets expensive.

۵.۲ local در برابر global + جدول مقایسه5.2 Local vs global + comparison table

ایندکس محلی فقط دادهٔ همان شارد را پوشش می‌دهد؛ ایندکس سراسری دادهٔ چند شارد را پوشش می‌دهد و خودش هم می‌تواند تقسیم شود. سادگی نوشتن و هزینهٔ خواندن میان این دو فرق دارد.

مثال فنی: محلی یا جهانی برای رنگ

وضعیت اولیه: کالای P7 روی A قرمز است و کالای P9 روی B قرمز است.

اجرای مرحله‌به‌مرحله: ایندکس محلی برای query قرمز نیاز به خواندن A و B دارد؛ ایندکس جهانی قرمز هر دو شناسه را کنار هم نگه می‌دارد.

نتیجه و دلیل: جهانی خواندن را متمرکزتر می‌کند، اما تغییر رنگ یا حذف کالا باید ایندکسِ احتمالاً دوردست را هم اصلاح کند.

Local secondary index (in information retrieval: document-partitioned): each shard indexes only its own records. Writing (add, delete, update) engages only that record's shard. The used-car-sale example with the ad's unique ID as partition key: adding one red car, its own shard appends the ID to the color:red postings list.

  • اگر partition key را بدانی، جست‌وجو فقط در همان shard انجام می‌شود؛ اگر فقط «چند نتیجه» می‌خواهی، هر shardی کافی است.
  • اگر همهٔ نتایج را بخواهی و partition key را ندانی، باید کوئری به همهٔ shardها برود و جواب‌ها ادغام شوند (ماشین‌های قرمز ممکن است در shard صفرم و یکم هر دو باشند).
  • پراکنده‌خوانی، tail latency را تقویت می‌کند و مقیاس‌پذیری را محدود: shard بیشتر یعنی فضای بیشتر، نه throughputِ کوئریِ بیشتر.
  • با این حال بسیار رایج است: MongoDB، Riak، Cassandra، Elasticsearch، SolrCloud و VoltDB.
  • خطر: اگر در کدِ برنامه خودت ایندکسِ value→ID بسازی، race condition و نوشتنِ ناقص، ایندکس را از داده جدا می‌کند؛ برای همین به multi-object transaction نیاز داری (فصل ۸).
  • If you know the partition key, the search runs in that same shard; if you want only “a few results”, any shard suffices.
  • If you want all results without knowing the partition key, the query must go to all shards and answers merge (red cars may sit in both shard zero and one).
  • Scatter reads amplify tail latency and bound scalability: more shards mean more space, not more query throughput.
  • Still very common: MongoDB, Riak, Cassandra, Elasticsearch, SolrCloud and VoltDB.
  • Risk: if you build a value→ID index yourself in application code, race conditions and partial writes detach the index from the data; hence the need for multi-object transactions (Chapter 8).

Global secondary index (یا term-partitioned): ایندکس روی همهٔ shardهاست و خودش جداگانه shard می‌شود — مثلاً رنگ‌های a تا r در یک shard و s تا z در shard دیگر. چون «term» (هر مقدارِ قابلِ جست‌وجو) کلیدِ partition است، می‌دانی کدام shard را بخوانی.

Global secondary index (or term-partitioned): the index spans all shards and is sharded separately itself — e.g. colors a–r in one shard and s–z in another. Because a “term” (each searchable value) is the partition key, you know which shard to read.

  • کوئریِ تک‌شرط (color = red) postings list را از یک shard می‌خواند؛ اما گرفتنِ خودِ رکوردها هنوز از چند shard است.
  • شرط‌های چندگانه (رنگ + برند) احتمالاً به shardهای مختلف می‌افتند؛ AND یعنی اشتراکِ دو postings list — برای لیست‌های بلند کُند است.
  • نوشتن پیچیده‌تر است: یک رکورد ممکن است چند shard ایندکس را به‌روز کند (هر term ممکن است shard خودش را داشته باشد) و این به distributed transaction نیاز دارد.
  • CockroachDB، TiDB و YugabyteDB ایندکسِ global دارند؛ DynamoDB به‌روزرسانی‌اش را ناهمزمان انجام می‌دهد، پس خواندنِ آن ممکن است کهنه باشد.
  • A single-condition query (color = red) reads the postings list from one shard; but fetching the records themselves still spans shards.
  • Multi-condition queries (color + brand) likely land on different shards; AND means intersecting two postings lists — slow for long lists.
  • Writing is more complex: one record may update several index shards (each term may own its shard), needing distributed transactions.
  • CockroachDB, TiDB and YugabyteDB have global indexes; DynamoDB updates its own asynchronously, so reading it may be stale.
معیارLocal (document-partitioned)Global (term-partitioned)
نوشتنِ رکوردفقط یک shardچند shard ایندکس (هر term یک shard)
خواندنِ postings listهمهٔ shardها (scatter-gather)یک shard
گرفتنِ رکوردهاهمان shardهاshardهای مالکِ هر ID
چند شرطی (AND)ادغامِ نتایجِ کاملاشتراکِ postings listها روی شبکه
tail latencyتقویت می‌شود (کندترین shard)بهتر برای تک‌شرط
throughput با افزودنِ shardرشد نمی‌کند (هر کوئری همه‌جا می‌رود)برای خواندن بهتر رشد می‌کند
سازگاریِ نوشتنساده (تک‌شیئی)نیازمندِ تراکنشِ چندشیئی
نمونه‌هاMongoDB، Cassandra، Elasticsearch، VoltDBCockroachDB، TiDB، YugabyteDB، DynamoDB (ناهمزمان)
CriterionLocal (document-partitioned)Global (term-partitioned)
Record writesJust one shardSeveral index shards (one shard per term)
Postings-list readsAll shards (scatter-gather)One shard
Fetching recordsThose same shardsEach ID's owning shards
Multi-condition (AND)Merging complete resultsIntersecting postings lists over the network
Tail latencyAmplified (slowest shard)Better for single-condition
Throughput as shards growDoesn't grow (each query goes everywhere)Grows better for reads
Write consistencySimple (single-object)Needs multi-object transactions
ExamplesMongoDB, Cassandra, Elasticsearch, VoltDBCockroachDB, TiDB, YugabyteDB, DynamoDB (async)
آخرین نکتهٔ فصل: استقلالِ shardها و نوشتنِ چندشیئیThe chapter's last note: shard independence and multi-object writes

طراحیِ sharding طوری است که هر shard تقریباً مستقل کار می‌کند — همین، مقیاس‌پذیری را ممکن می‌سازد. اما عملیاتی که باید چند shard را بنویسد مشکل‌ساز است: اگر نوشتنِ یک shard موفق و دیگری ناموفق شود چه؟ این پرسش را فصل‌های بعد (تراکنش‌های توزیع‌شده) جواب می‌دهند.

Sharding is designed so each shard works nearly independently — that is what makes scalability possible. But operations that must write several shards are problematic: if one shard's write succeeds and another's fails, what then? The coming chapters (distributed transactions) answer that question.

نقشه فصل · همه مفاهیم sharding در یک نگاهChapter map · all of sharding at a glance باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نقشه فصلTeacher narration of the chapter map

مثال فنی برای خواندنِ نمودار: محلی یا جهانی برای رنگ

وضعیت اولیه: کالای P7 روی A قرمز است و کالای P9 روی B قرمز است.

اجرای مرحله‌به‌مرحله: ایندکس محلی برای query قرمز نیاز به خواندن A و B دارد؛ ایندکس جهانی قرمز هر دو شناسه را کنار هم نگه می‌دارد.

نتیجه و دلیل: جهانی خواندن را متمرکزتر می‌کند، اما تغییر رنگ یا حذف کالا باید ایندکسِ احتمالاً دوردست را هم اصلاح کند.

گره مرکزی (Sharding) به شش مفهوم وصل است: بازهٔ کلید با «بازه‌های مرتب»، hash با «پخش یکنواخت»، consistent hashing با «کمترین جابه‌جایی»، hotspot با «مراقب بار باش»، مسیریابی با «نگاشت کلید به node» و ایندکس ثانویه با «local در برابر global». هر یال، قرارداد همان مفهوم را در دو کلمه خلاصه می‌کند.

مثل نقشهٔ فصل قبل، این را چک‌لیست مرور بدان: برای هر یال یک پاراگراف (تعریف + مثال + بها) بگو؛ هر یالی که سکوتت کرد، همان بخش را دوباره بخوان و بعد به بستهٔ مصاحبه برو.

The central node (Sharding) connects to six concepts: key-range with “ordered ranges”, hashing with “even spread”, consistent hashing with “minimal moves”, hotspots with “watch the load”, routing with “the key-to-node map”, and secondary indexes with “local vs global”. Each edge compresses its concept's contract into two words.

Like the previous chapter's map, treat this as a revision checklist: speak one paragraph (definition + example + price) per edge; whichever edge silences you, re-read that section, then head to the interview pack.

۶6 بستهٔ مصاحبهInterview pack

۶.۱ همه‌چیز در یک نگاه6.1 Everything at a glance

اصطلاحتعریف یک‌خطیکلیدواژهٔ مصاحبه
Sharding / Partitioningشکستنِ داده به تکه‌ها و قرار دادنِ هر تکه روی node متفاوتهر رکورد دقیقاً در یک shard
Shardواحدِ مستقلِ داده که یک پایگاه دادهِ کوچک استpartition، range، region، vBucket، vnode، tablet
Partition Keyورودیِ الگوریتمِ نگاشتِ رکورد به shardستونی که همه‌چیز را تعیین می‌کند
Skewتقسیمِ ناعادلانهٔ داده یا بار بین shardهاsharding را بی‌اثر می‌کند
Hot Shard / Hot Keyshard یا کلیدی با بارِ بسیار بیشتر از بقیهcelebrity problem
Key-Range Shardingهر shard یک بازهٔ پیوسته از کلیدهای مرتبrange scan خوب، خطرِ hotspot زمانی
Pre-splittingتعریفِ اولیهٔ مرزهای shard روی پایگاه دادهِ خالینیازمندِ حدسِ توزیعِ کلید
Hash Shardingنگاشتِ کلید به shard پس از hashتوزیعِ یکنواخت، از دست رفتنِ ترتیب
Modulo Nhash(key) % N_nodesتله: با تغییرِ N بیشترِ کلیدها می‌روند
Fixed Number of Shardsتعدادِ ثابتِ shard (مثلاً ۱۰۰۰) روی متغیر از nodeهاجابه‌جاییِ کلِ shard، ارزان
Hash-Range Shardingهر shard یک بازه از مقادیرِ hashsplit/merge بر اساسِ رشدِ داده
Consistent Hashingنگاشت با حداقلِ جابه‌جایی هنگامِ تغییرِ shardrendezvous، jump hash؛ ربطی به consistency ندارد
Rebalancingپخشِ دوبارهٔ داده هنگامِ اضافه/کم شدنِ nodemove whole shards، split، یا reassign keys
Reshardingتغییرِ خودِ تعداد/مرزهای shardبسیار گران؛ گاهی بدونِ downtime ممکن نیست
Request Routingیافتنِ nodeِ مالکِ یک کلیدforward، routing tier، یا کلاینتِ shard-aware
Coordination Serviceنگهدارِ نگاشتِ معتبرِ shard→node با اعلانِ تغییراتZooKeeper، etcd، Raft داخلی
Cutoverپنجرهٔ جابه‌جاییِ مالکیتِ shard با درخواست‌های در پروازforward یا reject درخواست‌های قدیمی
Secondary Indexایندکسِ جست‌وجویی که رکورد را یکتا نمی‌کندpostings list
Local Indexهر shard فقط رکوردهای خودش را ایندکس می‌کندdocument-partitioned، scatter-gather
Global Indexایندکس سراسری و جداگانه shardشده بر اساسِ termterm-partitioned، نوشتنِ چندshardی
Multitenancyچند مشتری با داده‌های جدا روی یک سرویسshard per tenant، cell-based architecture
Heat Managementجابه‌جاییِ خودکارِ بار برای کلید/shard داغadaptive capacity در DynamoDB
TermOne-line definitionInterview keyword
Sharding / PartitioningBreaking data into pieces, each on a different nodeEach record in exactly one shard
ShardAn independent unit of data that is a small databasepartition, range, region, vBucket, vnode, tablet
Partition KeyThe record-to-shard mapping algorithm's inputThe column determining everything
SkewUnfair splitting of data or load across shardsMakes sharding ineffective
Hot Shard / Hot KeyA shard or key with far more load than the restcelebrity problem
Key-Range ShardingEach shard holds one contiguous range of ordered keysGood range scans, temporal hotspot risk
Pre-splittingDefining initial shard boundaries on an empty databaseNeeds guessing the key distribution
Hash ShardingMapping keys to shards after hashingEven distribution, lost order
Modulo Nhash(key) % N_nodesTrap: when N changes, most keys move
Fixed Number of ShardsA fixed shard count (e.g. 1000) over varying nodesMoving whole shards, cheap
Hash-Range ShardingEach shard holds a range of hash valuesSplit/merge as data grows
Consistent HashingMapping with minimal moves when shards changerendezvous, jump hash; nothing to do with consistency
RebalancingRespreading data when nodes join/leavemove whole shards, split, or reassign keys
ReshardingChanging the shard count/boundaries themselvesVery expensive; sometimes impossible without downtime
Request RoutingFinding a key's owning nodeforward, routing tier, or shard-aware client
Coordination ServiceKeeper of the authoritative shard→node map with change notificationsZooKeeper, etcd, in-house Raft
CutoverThe shard-ownership switch window with in-flight requestsForward or reject old requests
Secondary IndexA search index that doesn't identify recordspostings list
Local IndexEach shard indexes only its own recordsdocument-partitioned, scatter-gather
Global IndexA global index sharded separately by termterm-partitioned, multi-shard writes
MultitenancySeveral customers with separate data on one serviceshard per tenant, cell-based architecture
Heat ManagementAutomatically moving load for hot keys/shardsadaptive capacity in DynamoDB

۶.۲ سوالات پرتکرار مصاحبه (برای دیدن جواب کلیک کن)6.2 Frequent interview questions (click for answers)

۱. sharding با replication چه تفاوتی دارد و چگونه با هم ترکیب می‌شوند؟1. How do sharding and replication differ, and how do they combine?

تکثیر چند نسخه از همان داده می‌سازد؛ شاردینگ داده را به بخش‌های متفاوت تقسیم می‌کند. می‌توان برای هر شارد، چند نسخهٔ جدا نگه داشت تا هم ظرفیت و هم تحملِ خرابی بیشتر شود.

مثال فنی: تقسیم داده با تکثیر فرق دارد

وضعیت اولیه: کلیدهای 1…1000 بین shard A و B نصف شده‌اند.

اجرای مرحله‌به‌مرحله: برای هر shard سه replica داریم؛ جمعاً شش کپی فیزیکی داریم، ولی A و B دو نیمهٔ متفاوت داده را نگه می‌دارند.

نتیجه و دلیل: sharding ظرفیت را تقسیم می‌کند؛ replication نسخه‌های همان بخش را برای خرابی و خواندن فراهم می‌کند.

Replication means a copy of the same data on several nodes (failure tolerance and read scaling); sharding means splitting data into different pieces with each piece on a different node (more writes and volume). Combined, each shard has several copies; each shard's leader sits on one node and followers on others, and each node can lead some shards and follow others.

۲. چه زمانی نباید sharding کرد؟2. When should you not shard?

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

مثال فنی: هزینهٔ تقسیمِ زودهنگام

وضعیت اولیه: کل مجموعه 20 GB است و یک سرور هدف بار را تأمین می‌کند.

اجرای مرحله‌به‌مرحله: پس از تقسیم به چهار shard، گزارشِ همهٔ سفارش‌ها باید چهار پاسخ را جمع کند و مهاجرت داده هم پیچیده‌تر می‌شود.

نتیجه و دلیل: تا محدودیت مشخصی وجود ندارد، شاردینگ ممکن است فقط مسیر ساده را پرهزینه کند؛ replica نیاز مستقلِ تحمل خرابی است.

When one machine handles the data and writes — and one machine today does a lot. Sharding brings complexity: picking the partition key, hard scheme changes, costlier joins and multi-object writes (distributed transactions). If only reads are heavy, replication suffices.

۳. key-range sharding چه مزیت و چه خطری دارد؟ چطور hotspot زمانی را درمان می‌کنند؟3. Key-range sharding's advantage and risk? How are temporal hotspots cured?

بازهٔ کلید خواندنِ مرتب را آسان می‌کند، ولی کلیدهای تازهٔ نزدیک می‌توانند یک شارد را داغ کنند. افزودنِ شناسهٔ منبع به ابتدای کلید بار را پخش می‌کند و خواندنِ مشترک را گران‌تر می‌کند.

مثال فنی: بازهٔ زمانی و shard داغ

وضعیت اولیه: رخدادهای ساعت ۱۰ تا ۱۱ روی shard آخر ذخیره می‌شوند و همهٔ رخدادهای تازه زمان فعلی دارند.

اجرای مرحله‌به‌مرحله: بازه‌های قدیمی آرام‌اند ولی shardِ بازهٔ جاری تمام نوشتن را می‌گیرد؛ تقسیمِ بیشترِ بازهٔ جاری یا ترکیب کلید با bucket فشار را پخش می‌کند.

نتیجه و دلیل: range sharding اسکن زمان را آسان می‌کند، ولی کلیدِ پیوسته می‌تواند نوشتن را یک‌جا جمع کند.

Advantage: ordered keys per shard, so fast range scans and composite indexes. Risk: nearby-key writes (like timestamps) heat one shard. Cure: prefixing keys with a sensor or tenant ID to spread writes — price: a multi-sensor range needs several separate queries.

۴. چرا hash(key) % N برای sharding بد است؟4. Why is hash(key) % N bad for sharding?

در hash(key) % N، تغییرِ تعدادِ گره‌ها مقصدِ بسیاری از کلیدها را عوض می‌کند. واگذاریِ شاردهای ثابت یا بازه‌های مستقل، تغییرِ سرورها را از کلیدها جدا می‌کند.

مثال فنی: تلهٔ تغییرِ مقسوم‌علیه

وضعیت اولیه: مقدار hash یک کلید 10 است؛ با سه سرور مقصد 10 % 3 = 1 می‌شود.

اجرای مرحله‌به‌مرحله: افزودن سرور چهارم آن را به 10 % 4 = 2 می‌برد؛ بسیاری از کلیدهای دیگر هم جابه‌جا می‌شوند.

نتیجه و دلیل: جدا کردن shard منطقی از سرور فیزیکی، تغییر تعداد ماشین‌ها را از بازتقسیم همهٔ کلیدها مستقل‌تر می‌کند.

Because N is the node count, and adding or removing one node changes every key's division — nearly all records must move. Good rebalancing means minimal moves; so key-to-shard mapping must be independent of node count (fixed shard count, hash ranges, or consistent hashing).

۵. طرحِ «تعدادِ ثابتِ shard» چطور کار می‌کند و بهایش چیست؟5. How does the “fixed shard count” scheme work, and its price?

کلید ابتدا به یکی از شاردهای ثابت نگاشت می‌شود و هر گره چند شارد می‌گیرد. افزودنِ گره، محلِ بعضی شاردها را تغییر می‌دهد؛ تعدادِ ثابت، ریزدانگی و بارِ نامتوازن محدودیت‌اند.

مثال فنی: شارد منطقی ثابت

وضعیت اولیه: ۱۰۲۴ bucket منطقی داریم که میان چهار ماشین توزیع شده‌اند.

اجرای مرحله‌به‌مرحله: با افزودن ماشین پنجم، بخشی از bucketها منتقل می‌شوند و تابع تعیین bucket تغییر نمی‌کند.

نتیجه و دلیل: جابه‌جایی محدودتر می‌شود؛ تعداد و اندازهٔ bucketها، نقشهٔ مالکیت و hot key همچنان باید مدیریت شوند.

E.g. 1000 shards are created up front and several reach each node; a key goes to shard hash(key) % 1000 and the shard→node map is kept separately. Cluster changes move only a few whole shards. Price: if the shard count comes out small (more nodes than shards), expensive resharding is required; and picking the “right” shard count is hard when data volume varies wildly.

۶. hash-range sharding چه تفاوتی با key-range دارد؟6. How does hash-range sharding differ from key-range?

در بازهٔ کلید، مقدارهای نزدیک کنارِ هم‌اند؛ در بازهٔ هش، خروجیِ تابعِ هش تقسیم می‌شود و نزدیکیِ کلیدهای اصلی حفظ نمی‌شود.

مثال فنی: range روی hash، نه روی نام

وضعیت اولیه: نام‌های Ali و Sara ابتدا hash می‌شوند و بازه‌های hash به shardها تعلق می‌گیرد.

اجرای مرحله‌به‌مرحله: lookup هر نام به یک مقصد می‌رود؛ جست‌وجوی نام‌های شروع‌شونده با Sa در hash کنار هم نیست.

نتیجه و دلیل: hash-range برای پخش بار مناسب‌تر است، ولی نظمِ کلید اصلی و اسکن prefix را حفظ نمی‌کند.

Instead of a key range, each shard gets a range of hash values; so nearby keys (consecutive timestamps) scatter and temporal hotspots vanish. Growth splits/merges like key-range. Price: range queries on the partition key itself go inefficient; the composite-key trick (partition in the first column) rescues in-shard range reads.

۷. consistent hashing چه دو ویژگی‌ای دارد و «consistent» به چه معناست؟7. Consistent hashing's two properties, and what does “consistent” mean?

هشِ سازگار جابه‌جاییِ کلیدها را هنگامِ تغییرِ عضویت محدود می‌کند و با طراحیِ مناسب توزیعِ متعادل‌تری می‌دهد. «سازگار» در این نام، تضمینِ تازگیِ replica یا ACID نیست؛ توازنِ دقیق هم خودکار نیست.

مثال فنی: دایرهٔ hash و مالک جدید

وضعیت اولیه: توکن‌های A و B روی حلقه‌اند و هر کلید به مالک بعدی می‌رود.

اجرای مرحله‌به‌مرحله: با افزودن C فقط بازه‌هایی که C مالکشان می‌شود جابه‌جا می‌شوند؛ virtual nodeها می‌توانند تقسیم ظرفیت را ریزتر کنند.

نتیجه و دلیل: consistent یعنی پایداری نسبی نگاشت هنگام تغییر اعضا؛ به‌معنی تضمین consistency خواندن و نوشتن نیست.

1) Each shard holds roughly as many keys; 2) when the shard count changes, the fewest keys move. “Consistent” means a key's tendency to stay in its own shard — not replica consistency, not ACID. Its family members: rendezvous hashing and jump consistent hashing. Cassandra and ScyllaDB hold randomly bounded ranges with several ranges per node (16 and 256).

۸. rebalancing چیست و چهار روشِ رایجِ انجامش کدامند؟8. What is rebalancing, and four common ways to do it?

بازمتوازن‌سازی پخشِ داده یا مسئولیت را تغییر می‌دهد. چهار شکلِ رایج: انتقالِ شاردِ ثابت، شکستن یا ادغامِ بازه، تغییرِ تخصیصِ کلیدها با عضویت و بازتقسیمِ کلیِ داده.

مثال فنی: چهار راه rebalance

وضعیت اولیه: مجموعهٔ کلید و سرورهای مقصد باید بین اعضا دوباره تقسیم شوند.

اجرای مرحله‌به‌مرحله: می‌توان shardهای ثابت را منتقل کرد، بازه‌های بزرگ را شکست، تعداد بخش‌ها را متناسب با نودها نگه داشت یا توکن‌های حلقه را جابه‌جا کرد.

نتیجه و دلیل: هر طرح اندازهٔ واحد انتقال و میزان تغییر نقشهٔ کلاینت را عوض می‌کند؛ «خودکار» نام الگوریتم تقسیم نیست.

Respreading data when the cluster changes. Ways: moving whole shards between nodes (fixed shard count), splitting/merging ranges (key-range or hash-range), reassigning scattered keys to the fresh node (consistent hashing), and full resharding (changing the shard count — very expensive, sometimes needing write pauses).

۹. چرا rebalancing کاملاً خودکار می‌تواند خطرناک باشد؟9. Why can fully automatic rebalancing be dangerous?

انتقالِ داده شبکه و دیسک مصرف می‌کند. اگر کندیِ موقت به‌اشتباه کمبودِ دائمی تشخیص داده شود، انتقال‌های تازه می‌توانند همان فشار را شدیدتر کنند.

مثال فنی: مهاجرت در وقت نامناسب

وضعیت اولیه: یک نود کند شده و کنترل‌گر می‌خواهد 200 GB دادهٔ آن را جابه‌جا کند.

اجرای مرحله‌به‌مرحله: انتقال شبکه و دیسک مصرف می‌کند و ممکن است کندی را تشدید کند؛ محدودیت نرخ و بررسی علت کندی لازم است.

نتیجه و دلیل: کندی موقت همیشه نیاز به rebalance نیست؛ حلقهٔ خودکار بدون مهار می‌تواند ازدحام را بیشتر کند.

Rebalancing is expensive (rerouting requests and moving lots of data) and may swamp the network or nodes. The worst mix is automatic failure detection: a slow node is declared “dead”, the cluster moves load, and the extra pressure builds a cascading failure. Hence often “human in the loop” or preventive rebalancing (before heavy events) is advised.

۱۰. یک hotspot (کلیدِ داغ) را با چه راه‌هایی می‌توان خنثی کرد؟10. How can one neutralize a hotspot (hot key)?

بسته به مسئله می‌توان خواندن را کش کرد، بخشِ داغ را جدا کرد یا یک کلید را به چند جزء شکست. تقسیمِ یک کلید، معنای جمع‌کردن و قواعدِ اتمیک را تغییر می‌دهد.

مثال فنی: شکستن شمارندهٔ داغ

وضعیت اولیه: شمارندهٔ بازدید p9 روی یک کلید به گلوگاه تبدیل شده است.

اجرای مرحله‌به‌مرحله: نوشتن‌ها به ۱۶ کلید p9:0…15 پخش می‌شوند؛ خواندن مجموع ۱۶ مقدار را حساب می‌کند.

نتیجه و دلیل: بار نوشتن پخش می‌شود، ولی خواندن و تضمینِ snapshot مشترک سخت‌تر می‌شود؛ برای موجودی سخت‌گیرانه همین الگو را بی‌بررسی به‌کار نمی‌بریم.

In range-based schemes, put the hot key in a dedicated shard or even a dedicated machine. At application level, adding a random prefix/suffix (two digits → 100 keys) spreads writes but reads must merge them all — and only writes spread. You must also know which keys are hot and manage their lifecycle. Hot keys change over time, and “write-hot” differs from “read-hot”.

۱۱. request routing چیست و چه تفاوتی با service discovery دارد؟11. What is request routing, and how does it differ from service discovery?

مسیریابیِ داده باید مالکِ کلید را پیدا کند. کشفِ سرویس نمونه‌های موجودِ یک سرویس را معرفی می‌کند؛ در سرویسِ بدون حالت شاید هر نمونه پاسخ دهد، اما شاردِ اشتباه دادهٔ لازم را ندارد.

مثال فنی: سه راه پیدا کردن shard

وضعیت اولیه: نقشه می‌گوید کلید 17 اکنون روی B است.

اجرای مرحله‌به‌مرحله: کلاینتِ آگاه مستقیم به B می‌رود؛ proxy درخواست را هدایت می‌کند؛ یا نود نخست آن را forward می‌کند.

نتیجه و دلیل: هر راه یک هزینه و محل نگهداری نقشه دارد؛ service discovery فقط پیدا کردن instance است و لزوماً مالک کلید را نمی‌گوید.

Finding a key's owning node. In service discovery, service instances are stateless and any answers; in sharding, only the owning shard's replica nodes can. So routing must know and track the key→shard and shard→node maps.

۱۲. سه روشِ مسیریابیِ درخواست را مقایسه کن.12. Compare three request-routing styles.

سه روش: هر گره درخواستِ بی‌ربط را جلو بفرستد؛ یک مسیریاب مقصد را تعیین کند؛ یا کلاینت نگاشت را بداند. تفاوت در تعدادِ پرش، پیچیدگیِ کلاینت و مدیریتِ نگاشتِ قدیمی است.

مثال فنی: سه راه پیدا کردن shard

وضعیت اولیه: نقشه می‌گوید کلید 17 اکنون روی B است.

اجرای مرحله‌به‌مرحله: کلاینتِ آگاه مستقیم به B می‌رود؛ proxy درخواست را هدایت می‌کند؛ یا نود نخست آن را forward می‌کند.

نتیجه و دلیل: هر راه یک هزینه و محل نگهداری نقشه دارد؛ service discovery فقط پیدا کردن instance است و لزوماً مالک کلید را نمی‌گوید.

1) Any node: a request reaches any node and, if not the owner, is forwarded with the answer returned — simple, no extra component. 2) Routing tier: a component that only routes (a shard-aware load balancer) — separated duties, but more components. 3) Shard-aware client: straight to the right node — fastest, but every client must know and refresh the map.

۱۳. نقشِ ZooKeeper/etcd در مسیریابی چیست و جایگزینِ Riak چه فرقی دارد؟13. What is ZooKeeper/etcd's routing role, and how does Riak's alternative differ?

سرویسِ هماهنگی نگاشت و تغییرِ مالکیت را طبقِ قواعدِ مشترک نگه می‌دارد. در انتشارِ gossip، گره‌ها اطلاعاتِ عضویت را میان خود پخش می‌کنند و ممکن است مدتی دیدِ متفاوت داشته باشند.

مثال فنی: مالکیت با epoch

وضعیت اولیه: نقشهٔ قدیمی shard S را با epoch ۷ روی A و نقشهٔ تازه با epoch ۸ روی B نشان می‌دهد.

اجرای مرحله‌به‌مرحله: مقصد باید نوشتنِ متعلق به مالکیت منقضی را رد یا هدایت کند؛ دو نسخهٔ نقشه نباید دو نویسندهٔ معتبر بسازند.

نتیجه و دلیل: سرویس هماهنگی نقشه را هماهنگ می‌کند، اما اعمال fencing در مسیر داده هم لازم است.

These services keep the authoritative shard→node map, nodes register themselves, and every ownership change notifies subscribers; their consensus shields against split brain. Riak instead gossips between nodes: weaker consistency and possible split brain — acceptable for a leaderless database since it promises weaker guarantees anyway.

۱۴. هنگامِ جابه‌جاییِ shard، «پنجرهٔ cutover» چه مشکلی دارد؟14. When moving a shard, what's wrong with the “cutover window”?

Cutover نقطهٔ تغییرِ مالک و مسیر است. مقصد باید کپیِ اولیه و تغییرهای جدید را گرفته باشد؛ درخواستِ درراه و مجوزِ نوشتنِ مبدأ هم باید مدیریت شوند.

مثال فنی: Cutover بدون گم‌شدنِ نوشتن

وضعیت اولیه: shard از A به B منتقل می‌شود؛ snapshot تا موقعیت 500 کپی شده است.

اجرای مرحله‌به‌مرحله: تغییرهای بعدی تا 560 می‌رسند؛ با سازوکار پروتکل، نوشتن نهایی همگام و مالکیت به epoch تازه منتقل می‌شود.

نتیجه و دلیل: اگر نقشه پیش از رساندن تغییرها عوض شود، B می‌تواند دادهٔ قدیمی نشان دهد؛ اگر دو طرف بنویسند، تعارض می‌سازند.

Data transfer takes time, and during transfer the old map must serve. When the fresh owner takes over, requests still fly to the old owner; they must be forwarded or rejected so clients get a definite answer. Mismanaging that window builds cryptic “lost write” errors.

۱۵. چرا ایندکسِ ثانویه در دادهٔ شاردشده به طراحیِ جدا نیاز دارد؟15. Why is a secondary index incompatible with sharding?

ایندکسِ ثانویه می‌تواند با کلیدی متفاوت از کلیدِ تقسیم جست‌وجو کند. رکوردهای مطابق ممکن است در چند شارد باشند؛ بنابراین ایندکسِ محلی یا سراسری و هزینهٔ هرکدام باید طراحی شوند.

مثال فنی: query با کلیدِ متفاوت

وضعیت اولیه: کالاها بر product_id تقسیم شده‌اند، اما کاربر color=red را می‌خواهد.

اجرای مرحله‌به‌مرحله: این شرط shard کالا را معلوم نمی‌کند؛ یا هر shard ایندکس محلی خود را می‌خواند یا ایندکس جهانی مقصدها را معرفی می‌کند.

نتیجه و دلیل: ایندکس ثانویه به طرح جدا نیاز دارد، چون کلید جست‌وجو با کلید تقسیم یکی نیست.

Because it doesn't identify via the partition key, and matching records may scatter across all shards. The “value→shard” map isn't as simple as the primary key; hence two fix families: local (document-partitioned) and global (term-partitioned).

۱۶. local secondary index چگونه کار می‌کند و چه بهایی دارد؟16. How does a local secondary index work, and its price?

ایندکسِ محلی فقط دادهٔ همان شارد را پوشش می‌دهد. نوشتن محلی‌تر می‌ماند، ولی سؤالِ بدونِ کلیدِ تقسیم معمولاً به چند شارد و ترکیبِ نتایج نیاز دارد.

مثال فنی: محلی یا جهانی برای رنگ

وضعیت اولیه: کالای P7 روی A قرمز است و کالای P9 روی B قرمز است.

اجرای مرحله‌به‌مرحله: ایندکس محلی برای query قرمز نیاز به خواندن A و B دارد؛ ایندکس جهانی قرمز هر دو شناسه را کنار هم نگه می‌دارد.

نتیجه و دلیل: جهانی خواندن را متمرکزتر می‌کند، اما تغییر رنگ یا حذف کالا باید ایندکسِ احتمالاً دوردست را هم اصلاح کند.

Each shard indexes only its own records; writes engage just one shard. Keyless reads must go to all shards with merging answers (scatter-gather), amplifying tail latency; with more shards, query throughput doesn't grow. MongoDB, Cassandra, Elasticsearch and VoltDB all use this.

۱۷. global secondary index چگونه کار می‌کند و چه بهایی دارد؟17. How does a global secondary index work, and its price?

ایندکسِ سراسری شرطِ جست‌وجو را به رکوردهای همهٔ شاردها وصل می‌کند و خودش هم می‌تواند تقسیم شود. خواندن متمرکزتر می‌شود، ولی نوشتن و تازگیِ ایندکس هماهنگی می‌خواهند.

مثال فنی: محلی یا جهانی برای رنگ

وضعیت اولیه: کالای P7 روی A قرمز است و کالای P9 روی B قرمز است.

اجرای مرحله‌به‌مرحله: ایندکس محلی برای query قرمز نیاز به خواندن A و B دارد؛ ایندکس جهانی قرمز هر دو شناسه را کنار هم نگه می‌دارد.

نتیجه و دلیل: جهانی خواندن را متمرکزتر می‌کند، اما تغییر رنگ یا حذف کالا باید ایندکسِ احتمالاً دوردست را هم اصلاح کند.

The index is global and sharded separately by term; reading one condition's postings list wants just one shard. Price: fetching records still spans shards; a multi-term AND means intersecting long lists over the network; and writing one record may update several index shards (needing multi-object transactions). DynamoDB updates that index asynchronously, so reading it may be stale.

۱۸. چرا پیاده‌سازیِ دستیِ ایندکسِ ثانویه در کدِ برنامه خطرناک است؟18. Why is hand-rolling a secondary index in application code dangerous?

تغییرِ داده و ایندکس نباید مستقل و بی‌قرارداد باشند. خطا میانِ دو نوشتن یا ترتیبِ متفاوت می‌تواند رکورد را از جست‌وجو پنهان یا نتیجهٔ قدیمی ایجاد کند.

مثال فنی: یک تغییر، ایندکس جا‌مانده

وضعیت اولیه: برنامه رنگ P7 را در مرجع از قرمز به آبی تغییر می‌دهد و پیش از نوشتن ایندکس crash می‌کند.

اجرای مرحله‌به‌مرحله: جست‌وجو هنوز P7 را قرمز می‌داند؛ log تغییر یا تراکنشِ مناسب باید مسیر همگام‌سازی و ترمیم را بدهد.

نتیجه و دلیل: دو نوشتن مستقل در کد برنامه، atomicity یا ترتیب یکسانِ ایندکس و مرجع را تضمین نمی‌کنند.

Because the index (the value-to-ID map) must always stay consistent with the primary data; race conditions and partial writes (part stored, part not) easily corrupt it. Without multi-object transactions (Chapter 8) consistency can't be guaranteed.

۱۹. چرا «هر tenant یک shard» در چندمستاجری جذاب است و چه محدودیتی دارد؟19. Why is “one shard per tenant” attractive in multitenancy, and its limits?

تقسیم بر اساسِ سازمان، جداسازیِ عملیات و انتقال و بازیابیِ مشتری را ساده‌تر می‌کند. سازمان‌ها اندازه و بارِ یکسان ندارند؛ یک مشتریِ بزرگ شاید به چند شارد نیاز داشته باشد.

مثال فنی: یک tenant، یک محل

وضعیت اولیه: کلید مشتری سازمانی tenant_id=17 روی shard A قرار دارد.

اجرای مرحله‌به‌مرحله: بیشتر queryهای همین tenant محلی‌اند؛ اگر یک tenant ۶۰٪ بار را بسازد، جابه‌جایی آن یا شکستن داده‌اش لازم می‌شود.

نتیجه و دلیل: tenant کلید طبیعی جداسازی است، اما اندازه و بار tenantها مساوی نیست و «یک tenant یک shard» همیشه متعادل نمی‌ماند.

Separate resources and access, cell-based fault isolation, separate backup/recovery, GDPR/CCPA compatibility (simple export and delete), data residence, and gradual schema rollout. Limits: each tenant must fit on one machine (a giant tenant must shard inside itself), many small tenants carry overhead, and cross-tenant features get hard.

۲۰. در انباره‌های داده، partition و cluster columns چه می‌کنند؟20. In data warehouses, what do partition and cluster columns do?

Partition column بخش‌های اصلیِ داده را تعیین می‌کند؛ clustering معمولاً نظمِ داخلِ آن بخش‌ها را شکل می‌دهد تا بخشِ نامربوط کمتر خوانده شود. جزئیات به موتور وابسته است.

مثال فنی: Partition و cluster در گزارش

وضعیت اولیه: فایل‌های فروش بر روز partition شده‌اند و داخل هر روز بر customer_id مرتب یا خوشه‌بندی شده‌اند.

اجرای مرحله‌به‌مرحله: فیلتر روز فقط partition مربوط را می‌خواند؛ فیلتر مشتری در همان روز می‌تواند بلوک‌های بیشتری را رد کند.

نتیجه و دلیل: partition حجم درشتِ خواندن و clustering حذف بلوک‌های داخل آن را هدف می‌گیرند؛ اثر دقیق وابسته به موتور است.

The partition key (like BigQuery's) decides which partition a record lands in, and cluster columns order inside the partition; Snowflake uses automatic micro-partitions with a cluster key, Delta Lake has both. Result: faster range scans, and sometimes better compression and filtering.

۶.۳6.3 اشتباهات رایج در جواب‌های مصاحبهCommon interview mistakes

  • یکی دانستنِ sharding و network partition — فقط اسمشان شبیه است.
  • گفتنِ «sharding یعنی داده را تقسیم کنیم» و فراموش کردنِ هدفِ اصلی: پخشِ یکنواختِ داده و بار و اجتناب از hotspot.
  • پیشنهادِ sharding برای هر سیستمی؛ اگر یک ماشین کافی است، sharding فقط پیچیدگیِ اضافه است.
  • توصیهٔ hash(key) % N به‌عنوانِ راه‌حل — بدونِ دیدنِ اینکه با تغییرِ N بیشترِ کلیدها جابه‌جا می‌شوند.
  • فراموش کردنِ اینکه در key-range sharding، کلیدِ timestamp بدونِ پیشوند، shardِ «این ماه» را داغ می‌کند.
  • گفتنِ «hash همه‌چیز را یکنواخت می‌کند» و ندیدنِ اینکه بارِ یک کلیدِ داغ (celebrity) همچنان روی یک shard می‌ماند.
  • گفتنِ «پیشوندِ تصادفی hotspot را حل می‌کند» بدونِ توضیحِ اینکه فقط نوشتن پخش می‌شود و خواندن باید ۱۰۰ کلید را ادغام کند.
  • یکی دانستنِ «consistent hashing» با consistency؛ اینجا consistent یعنی «کلید در جای خودش بماند».
  • فراموش کردنِ گرانیِ split: بازنویسیِ کلِ داده، دقیقاً روی shardِ زیرِ فشار.
  • در rebalancing، ندیدنِ ترکیبِ خطرناکِ «خودکار + تشخیصِ خرابیِ خودکار» و خطرِ خرابیِ آبشاری.
  • در مسیریابی، فقط گفتنِ «از load balancer استفاده می‌کنیم» و ندیدنِ سه پرسش: چه کسی تصمیم می‌گیرد، چه کسی از تغییر باخبر می‌شود، و درخواست‌های در پروازِ cutover.
  • فراموش کردنِ اینکه ایندکسِ ثانویه خودش هم باید shard شود و scatter-gather، tail latency را تقویت می‌کند.
  • گفتنِ «global index خواندن را رایگان می‌کند»؛ postings list یک shard است اما گرفتنِ رکوردها و اشتراکِ چند term هنوز گران است.
  • پیشنهادِ ایندکسِ دستی در کدِ برنامه بدونِ multi-object transaction (خطرِ ناسازگاریِ ایندکس و داده).
  • گفتنِ «shard یعنی یک ماشین»؛ shard واحدِ منطقیِ داده است و می‌تواند چند shard روی یک ماشین باشد (حتی چند پروسه روی یک CPU).
  • Conflating sharding with network partitions — only their names match.
  • Saying “sharding means splitting data” and forgetting the real goal: evenly spreading data and load and avoiding hotspots.
  • Recommending sharding for any system; if one machine suffices, sharding is just extra complexity.
  • Recommending hash(key) % N as the fix — blind to most keys moving when N changes.
  • Forgetting that in key-range sharding, a bare timestamp key heats “this month's” shard.
  • Saying “hashing evens everything” while missing that one hot (celebrity) key's load still stays on one shard.
  • Saying “a random prefix fixes hotspots” without explaining that only writes spread while reads must merge 100 keys.
  • Conflating “consistent hashing” with consistency; here consistent means “a key stays in its place”.
  • Forgetting split cost: rewriting all data, exactly on the shard under pressure.
  • In rebalancing, missing the dangerous “automatic + automatic failure detection” mix and cascading-failure risk.
  • In routing, only saying “we use a load balancer” and missing the three hard questions: who decides, who learns of changes, and cutover in-flight requests.
  • Forgetting that a secondary index must itself be sharded, and that scatter-gather amplifies tail latency.
  • Saying “a global index makes reads free”; the postings list is one shard but fetching records and multi-term intersection stay expensive.
  • Recommending hand-rolled indexes in application code with no multi-object transaction (index-data inconsistency risk).
  • Saying “a shard is one machine”; a shard is a logical data unit and several shards can sit on one machine (even several processes on one CPU).

۶.۴ چک‌لیست مرور ۵ دقیقه‌ای قبل از مصاحبه6.4 Five-minute checklist before the interview

  1. تعریفِ shard و تفاوتِ آن با replication و network partition — یک جمله‌ای.
  2. دو خانوادهٔ اصلی (key-range در برابر hash) + یک مزیت و یک بهای هرکدام.
  3. تلهٔ mod N را با یک مثالِ عددی (۳ → ۴ node) تعریف کن و سه جایگزینش را نام ببر.
  4. چهار روشِ rebalancing و خطرِ ترکیبِ rebalancing خودکار با تشخیصِ خرابیِ خودکار.
  5. تعریفِ hotspot و سه راهکارش (shard اختصاصی، پیشوندِ تصادفی، heat management) + بهای هرکدام.
  6. سه روشِ request routing و سه پرسشِ سخت (coordinator، اعلانِ تغییر، cutover).
  7. local در برابر global secondary index: مسیرِ نوشتن و خواندنِ هرکدام + بهای هرکدام.
  8. یک جمله دربارهٔ multitenancy و اینکه چرا «هر tenant یک shard» هم جذاب است و هم محدود.
  1. Define shard and its difference from replication and network partitions — in one sentence.
  2. Two main families (key-range vs hash) + one advantage and one price each.
  3. Define the mod-N trap with a numeric example (3 → 4 nodes) and name its three alternatives.
  4. Four rebalancing styles and the danger of mixing automatic rebalancing with automatic failure detection.
  5. Define hotspot and its three fixes (dedicated shard, random prefix, heat management) + each one's price.
  6. Three request-routing styles and the three hard questions (coordinator, change notification, cutover).
  7. Local vs global secondary index: each one's write and read paths + each one's price.
  8. One sentence on multitenancy and why “one shard per tenant” is both attractive and limited.

۷7 تمرین‌های مصاحبه و آزمایشگاهInterview exercises and hands-on labs

این تمرین‌ها برای مسیر Backend / Distributed Systems در ۱۴ اکتبر ۲۰۲۶ بازبینی شدند. سطح مربوط به نسخهٔ مصاحبه‌ای است؛ زمان آزمایشگاه جداست.Reviewed on 14 October 2026 for Backend / Distributed Systems. Level refers to the interview brief; full-lab time is separate. روش ارزیابی و مسیر مطالعهMethod and study path

تمرین ۱ — طرحِ sharding برای دادهٔ زمان‌سریExercise 1 — a sharding scheme for time-series data

L2 · SeniorL2 · Seniorاولویت اصلیCore priority40 دقیقه بحث40 min discussion

hotspot و query-aware sharding مهم‌اند؛ hash-range بدون تعیین کلید هش پاسخ یکتایی ندارد.Hash-range needs a specified hash key; monthly totals can hide instantaneous hotspots.

نسخهٔ بازبینی‌شده برای مصاحبهReviewed interview brief

برای دادهٔ ماهانهٔ سنسورها، timestamp-range، sensor:timestamp range، hash(sensor_id) و hash(full_key) را مقایسه کن. هم بار لحظه‌ای و هم مجموع ماه را بسنج. مسیر query یک سنسور در یک ماه، hotspot سنسور پرنرخ و repartitioning را توضیح بده.Compare timestamp ranges, sensor:timestamp ranges, hash(sensor_id) and hash(full_key). Measure instantaneous and monthly load, query fan-out, hot sensors and repartitioning.

معیار پاسخ خوبWhat a good answer demonstrates

  • توزیع ماهانه hotspot لحظه‌ای را پنهان نمی‌کندInstantaneous hotspot measurement
  • hash sensor را با hash full key یکی نمی‌گیردSpecified hash input
  • query fan-out و write skew کمی شده‌اندQuery fan-out and skew

پیگیری مصاحبه:Interview follow-up: یک سنسور ۴۰٪ نوشتن‌ها را تولید کند؛ چه تغییری در کلید می‌دهی؟One sensor generates 40% of all writes. How do you change the partition key?

آزمایشگاه تکمیلی · زمان تقریبی ۲–۴ ساعتExtended hands-on laboratory · estimated 2–4 hours

برای پایگاه دادهِ زمان‌سریِ سنسورها (کلید = timestamp) طرحِ sharding طراحی کن: سه جایگزین (key-range با کلیدِ timestamp، key-range با کلیدِ sensor_id:timestamp، و hash-range) را روی یک مجموعه‌دادهٔ مصنوعیِ یک ماهه پیاده کن. توزیعِ نوشتن بین shardها را اندازه بگیر، hotspot را با اعداد نشان بده، و trade-offِ «range scan برای یک ماهِ یک سنسور» را در هر سه طرح بسنج. کدام را برای چه نوع کوئری انتخاب می‌کنی و چرا؟

Design a sharding scheme for a sensor time-series database (key = timestamp): implement three alternatives (key-range on a timestamp key, key-range on a sensor_id:timestamp key, and hash-range) over one synthetic month of data. Measure write spread across shards, show the hotspot in numbers, and weigh the “one month of one sensor range scan” trade-off in all three schemes. Which do you pick for which query type, and why?

برای یادگیریِ عمیق است؛ انجام کاملش در زمان بحثِ مصاحبه انتظار نمی‌رود.The full laboratory is optional depth work and is not expected within the discussion time.

ارزیابی، سطح و شواهد این تمرینAssessment, level and evidence · حل آموزشی این تمرینWorked solution · Microsoft · technical interviewing · Amazon · SDE III interview preparation

تمرین ۲ — consistent hashing را خودت بسازExercise 2 — build consistent hashing yourself

L2 · SeniorL2 · Seniorوابسته به نقشRole-specific35 دقیقه بحث35 min discussion

consistent hashing مرتبط است، ولی درصد جابه‌جایی تنها معیار نیست؛ policy نگاشت fixed shard نیز باید تعریف شود.Movement percentage alone misses skew, capacity weights and migration policy.

نسخهٔ بازبینی‌شده برای مصاحبهReviewed interview brief

برای اضافه/حذف node، modulo، rendezvous و fixed logical shards با جدول مالکیت را مقایسه کن. درصد حرکت را برای mapping مشخص محاسبه کن و دربارهٔ skew، وزن node، failure، replication و مهاجرت تدریجی تصمیم بگیر.Compare modulo, rendezvous and fixed logical shards with an explicit ownership map. Estimate movement on joins/leaves and discuss skew, weighted nodes, replication and staged handoff.

معیار پاسخ خوبWhat a good answer demonstrates

  • ثبات hash در restart تضمین شدهStable hash function
  • حرکت داده را از تغییر routing جدا می‌کندData movement versus routing
  • fixed shard بدون ownership policy مقایسه نشدهExplicit shard ownership policy

پیگیری مصاحبه:Interview follow-up: node جدید دو برابر ظرفیت دارد؛ balancing را بدون جابه‌جایی همهٔ کلیدها چطور انجام می‌دهی؟A new node has twice the capacity. How do you rebalance without moving every key?

آزمایشگاه تکمیلی · زمان تقریبی ۱–۳ ساعتExtended hands-on laboratory · estimated 1–3 hours

یک rendezvous hashing ساده و یک «تعدادِ ثابتِ shard» پیاده کن. با شبیه‌سازیِ اضافه و حذفِ node (از ۱۰ به ۱۱ و از ۱۰ به ۹)، درصدِ کلیدهای جابه‌جاشده را در هر دو اندازه بگیر و با حالتِ hash % N مقایسه کن. منحنیِ «جابه‌جایی در برابر تغییرِ اندازهٔ خوشه» را رسم کن و جواب بده: برای چه الگویِ رشدی، کدام روش کمترین داده‌جابه‌جایی را می‌دهد؟

Implement a simple rendezvous hashing and a “fixed shard count”. Simulating node joins and leaves (10→11 and 10→9), measure the moved-key percentage in both and compare with hash % N. Plot the “moves vs cluster-size change” curve and answer: for which growth pattern does each method move the least data?

برای یادگیریِ عمیق است؛ انجام کاملش در زمان بحثِ مصاحبه انتظار نمی‌رود.The full laboratory is optional depth work and is not expected within the discussion time.

ارزیابی، سطح و شواهد این تمرینAssessment, level and evidence · حل آموزشی این تمرینWorked solution · Microsoft · technical interviewing · Atlassian · engineering interview handbook

تمرین ۳ — local در برابر global: اندازه‌گیری، نه تئوریExercise 3 — local vs global: measure, don't theorize

L3 · Staff / PrincipalL3 · Staff / Principalوابسته به نقشRole-specific45 دقیقه بحث45 min discussion

global index از deep diveهای خوب است؛ latency چهار SQLite محلی پیش‌بینی شبکه و cluster واقعی نیست.A useful distributed-index deep dive; local SQLite timings do not predict networked p99.

نسخهٔ بازبینی‌شده برای مصاحبهReviewed interview brief

برای دادهٔ چهار shard، local و global secondary index طراحی کن. write path، تک‌شرط/دوشرط، scatter-gather، failure جزئی و lag را مقایسه کن. یک تغییر چندtermی را با outbox/version مدیریت کن و تعداد RPC، p99 و هزینهٔ consistency را مشخص کن.Design local and global secondary indexes over four shards. Compare writes, one/two predicates, scatter-gather, partial failures and lag. Specify outbox/version handling, RPC counts and consistency cost.

معیار پاسخ خوبWhat a good answer demonstrates

  • source shard و index shard را جدا می‌شماردBase and index shard counts
  • index غیراتمیک را مرجع حقیقت فرض نمی‌کندDerived-index consistency limits
  • benchmark محلی را به cluster تعمیم نمی‌دهدNo network conclusions from local timing

پیگیری مصاحبه:Interview follow-up: index کالا را نشان می‌دهد ولی حذف آن در مرجع ثبت شده؛ خواننده چه می‌کند؟The index returns a product already deleted from the record. What does the reader do?

آزمایشگاه تکمیلی · زمان تقریبی ۴–۸ ساعتExtended hands-on laboratory · estimated 4–8 hours

روی چهار فایلِ SQLite (به‌عنوان چهار shard) دو مدلِ ایندکسِ ثانویه بساز: (الف) ایندکسِ محلیِ درونِ هر shard و خواندنِ scatter-gather، (ب) ایندکسِ globalِ جداگانه shardشده بر اساسِ مقدار. بارِ کاریِ نوشتن/خواندن (تک‌شرط و دوشرطه) را اجرا کن، latency (میانه و p99) و تعدادِ shardهای درگیر در هر عملیات را گزارش کن. سپس سناریوی «یک نوشتن، چند term» را بدونِ تراکنش اجرا کن و نشان بده چه اتفاقی برای سازگاریِ ایندکس می‌افتد.

On four SQLite files (as four shards) build two secondary-index models: (a) a per-shard local index with scatter-gather reads, (b) a separate global index sharded by value. Run a write/read workload (single- and double-condition), report latency (median and p99) and shards engaged per operation. Then run a “one write, several terms” scenario with no transaction and show what happens to index consistency.

برای یادگیریِ عمیق است؛ انجام کاملش در زمان بحثِ مصاحبه انتظار نمی‌رود.The full laboratory is optional depth work and is not expected within the discussion time.

ارزیابی، سطح و شواهد این تمرینAssessment, level and evidence · حل آموزشی این تمرینWorked solution · Debezium · outbox event router · Atlassian · engineering interview handbook