پارتیشن‌بندی داده Data Partitioning

میلیاردها سطر در یک جدول — چطور فقط همان یک سطر را پیدا می‌کنید، بدون اینکه بقیه را حتی نگاه کنید؟ A billion rows in one table — how do you find just the one row you need, without even glancing at the rest?

در یک دیتابیس بزرگ، بعد از اینکه ایندکس‌ها را درست مدیریت کردید، نوبت به تقسیم‌بندی خودِ داده می‌رسد. یعنی داده را طبقه‌بندی می‌کنیم تا موتور فقط همان تکه‌ای را بخواند که به کارمان می‌آید — و بقیه را کلاً نادیده بگیرد. In a large database, after you've managed your indexes properly, it's time to partition the data itself. We categorize the data so the engine reads only the slice we need — and ignores the rest entirely.

Alireza Haeri ع
علیرضا حایریAlireza Haeri
توسعه‌دهندهٔ بک‌اند ‎.NET.NET Backend Developer · Author of this path
شروع مسیر Start the path مشاهده موضوعات دیگر View other topics
01

چرا پارتیشن‌بندی — و کِی نباید عجله کردWhy partition — and when not to rush

نکته‌ای که شاید باورتان نشود: در SQL Server، پارتیشن‌بندی در درجه‌ی اول یک ابزار مدیریت و نگهداری است، نه ابزاری برای تندتر کردنِ کوئری‌ها. این جمله‌ی کلیدیِ فصل ۱۶ کتابِ Pro SQL Server Internals است — و اگر آن را جدی نگیرید، تمام تصمیم‌گیری‌هایتان درباره‌ی پارتیشن‌بندی، از همان ابتدا اشتباه خواهد بود. Here's something you might not believe: in SQL Server, partitioning is primarily a management and maintenance tool — not a query-speed hack. This is the key takeaway from Chapter 16 of Pro SQL Server Internals — and if you ignore it, every partitioning decision you make will be wrong from the start.

دلایلِ واقعی برای پارتیشن‌بندیThe real reasons to partition

۱. نگهداریِ تکه‌تکه: بکاپ، بازسازی ایندکس، به‌روزرسانی آمار — همه روی یک پارتیشن، نه کلِ جدولِ غول‌پیکر.
۲. حذف یا آرشیوِ سریع: به‌جای DELETE کردنِ میلیون‌ها سطر، کلِ یک پارتیشن را در چند میلی‌ثانیه SWITCH می‌کنید.
۳. ذخیره‌سازیِ طبقه‌ای: دادهٔ داغ روی SSD، دادهٔ کهنه روی HDD ارزان.
۴. رد کردنِ پارتیشن‌های نامربوط: کوئری‌های بازه‌ای می‌توانند پارتیشن‌هایی را که به کارشان نمی‌آیند، از پلن اجرا کنار بگذارند — اما این معمولاً یک سودِ جانبی است، نه هدفِ اصلی.
1. Chunk-level maintenance: backups, index rebuilds, stats updates — all on one partition, not the giant whole table.
2. Fast purge or archive: instead of DELETEing millions of rows, you SWITCH a whole partition away in milliseconds.
3. Tiered storage: hot data on SSD, cold data on cheap HDD.
4. Pruning irrelevant partitions: range queries can exclude partitions that aren't needed from the execution plan — but this is usually a side benefit, not the main goal.

کِی پارتیشن‌بندی نکنیمWhen not to partition

اگر جدول شما فقط «چند میلیون سطر» دارد، پارتیشن‌بندی سربار اضافه می‌کند بدون اینکه سودی داشته باشد. پارتیشن‌بندی برای جدول‌های واقعاً بزرگ است — ده‌ها میلیون تا میلیاردها سطر، جایی که نگهداریِ کلِ جدول به یک دردسر تبدیل شده. قبل از پارتیشن‌بندی، اول ایندکس‌ها را بررسی کنید (فصل قبلِ همین مسیر). If your table only has "a few million rows", partitioning adds overhead with no real benefit. Partitioning is for truly large tables — tens of millions to billions of rows, where maintaining the whole table has become a pain. Before partitioning, check your indexes first (the previous chapter of this path).

قاعدهٔ سرانگشتی: قبل از پارتیشن‌بندی یک سوال بپرسید: «مشکل من نگهداریِ جدول است یا کندیِ یک کوئری؟» اگر جواب «نگهداری» است، پارتیشن‌بندی احتمالاً راه‌گشاست. اگر «کوئری» است، اول ایندکس، بعد پلن اجرا، و در آخر پارتیشن‌بندی. Rule of thumb: before partitioning, ask one question: "is my problem table maintenance or a slow query?" If it's maintenance, partitioning probably helps. If it's the query, check indexes first, then the execution plan, and partitioning last.

.NET

وقتی یک کوئری EF Core روی جدولی با ده‌ها میلیون سطر کند می‌شود، ترتیب کار این است: اول ایندکس‌ها را بررسی کنید، بعد پلن اجرا را ببینید (با SET STATISTICS IO ON تعداد logical reads را چک کنید)، و فقط اگر جدول واقعاً بزرگ است و مشکل نگهداری دارید، سراغ پارتیشن‌بندی بروید. کد LINQ شما دست‌نخورده می‌ماند — پارتیشن‌بندی برای آن شفاف است. When an EF Core query gets slow on a table with tens of millions of rows, the order is: first check your indexes, then review the execution plan (use SET STATISTICS IO ON to check logical reads), and only if the table is truly large and you have a maintenance problem, consider partitioning. Your LINQ code stays untouched — partitioning is transparent to it.

02

افقی در برابر عمودی — دو معنای «تقسیم»Horizontal vs vertical — two meanings of "split"

کلمهٔ «پارتیشن» دو معنا دارد که اغلب قاطی می‌شوند. قبل از هر چیزی، این دو را یک‌بار برای همیشه از هم جدا کنید: The word "partition" has two meanings that often get mixed up. Before anything else, let's separate them once and for all:

افقی — تقسیمِ سطرهاHorizontal — splitting rows

سطرها را بین پارتیشن‌ها پخش می‌کنید؛ هر پارتیشن همهٔ ستون‌ها را دارد. مثلاً جدول Orders با ۱۰۰ میلیون سطر را در نظر بگیرید. اگر آن را به ۱۲ پارتیشن تقسیم کنید، هر پارتیشن حدود ۸ تا ۹ میلیون سطر خواهد داشت — و همهٔ آنها ساختارِ یکسانی دارند. دقیقاً همین است که در این صفحه درباره‌اش صحبت می‌کنیم. You spread rows across partitions; every partition has all the columns. Take the Orders table with 100 million rows. If you split it into 12 partitions, each partition will hold roughly 8 to 9 million rows — all with the exact same structure. This is exactly what this page is about.

عمودی — تقسیمِ ستون‌هاVertical — splitting columns

ستون‌ها را از هم جدا می‌کنید. مثلاً ستونِ سنگینِ Notes NVARCHAR(MAX) را به جدول OrderNotes می‌برید تا جدول اصلی باریک بماند و صفحه‌های بیشتری در حافظه جا شوند. این کار را معمولاً در طرحِ خودِ جدول انجام می‌دهید، نه با قابلیتِ پارتیشنِ موتور. You split columns apart. For example, moving a heavy Notes NVARCHAR(MAX) column into an OrderNotes table, so the main table stays slim and more pages fit in memory. You usually do this in your schema design, not with the engine's partitioning feature.

افقی: سطرها بریده می‌شوندHorizontal: rows are cut

هر خانه یک سطر است. سبز = پارتیشن ۱ (سال ۲۰۲۴)، نارنجی = پارتیشن ۲ (سال ۲۰۲۵). هر دو پارتیشن همان ستون‌ها را دارند.Each cell is one row. Green = partition 1 (2024), orange = partition 2 (2025). Both partitions have the same columns.

عمودی: ستون‌ها بریده می‌شوندVertical: columns are cut

سبز = ستون‌های داغ و کوچک (OrdersCore)، بنفش = ستون‌های سنگین (OrderNotes). هر دو تکه همان سطرها را دارند.Green = hot, slim columns (OrdersCore), violet = heavy columns (OrderNotes). Both pieces hold the same rows.

.NET

پارتیشن‌بندی افقی یک قابلیتِ موتور دیتابیس است (SQL Server / MySQL) — در کد C# کاری برای آن انجام نمی‌دهید. اما تقسیم عمودی یک تصمیمِ طراحی مدل است که در EF Core می‌گیرید: مثلاً Order و OrderDetails را جدا کنید چون معمولاً با هم خوانده نمی‌شوند. هر کدام از این دو، مشکلِ متفاوتی را حل می‌کنند. Horizontal partitioning is a database engine feature (SQL Server / MySQL) — you don't write any C# code for it. But vertical splitting is a model design decision you make in EF Core: for example, separating Order from OrderDetails because they're rarely read together. Each of these solves a different problem.

03

راهبردهای پارتیشن‌بندی — RANGE، LIST، HASH و KEYPartitioning strategies — RANGE, LIST, HASH, and KEY

چهار روش پارتیشن‌بندی در دنیای دیتابیس‌ها وجود دارد: RANGE، LIST، HASH و KEY. اما هر موتوری همهٔ آنها را پشتیبانی نمی‌کند. در SQL Server فقط RANGE در دسترس است؛ MySQL هر چهار روش را دارد که KEY مختص خودش است. There are four partitioning methods in the database world: RANGE, LIST, HASH, and KEY. But not every engine supports all of them. In SQL Server, only RANGE is available; MySQL has all four, with KEY being its own.

RANGE

هر پارتیشن، یک بازهٔ پیوسته را پوشش می‌دهد. برای داده‌های سری‌زمانی (لاگ، سفارش، سنسور) انتخابِ طبیعی است. کوئری‌های بازه‌ای (BETWEEN, >=) مستقیم به یک یا چند پارتیشن می‌رسند و بقیه حذف می‌شوند. Each partition covers a continuous range. The natural choice for time-series data (logs, orders, sensors). Range queries (BETWEEN, >=) map directly onto one or a few partitions, and the rest are pruned.

LIST

مقدار ستون پارتیشن با لیست مقادیرِ تعیین‌شده برای هر پارتیشن مقایسه می‌شود و در صورت تطابق، سطر به همان پارتیشن می‌رود — مثلاً پارتیشن اروپا فقط سطرهایی را می‌گیرد که ستونِ کشورشان DE, FR, IT باشد. این روش وقتی دسته‌های طبیعی و کم‌تعداد دارید (منطقه، وضعیت، نوع اشتراک) عالی است. The partition column value is compared against each partition's defined value list and if it matches, the row goes to that partition — for example, the Europe partition only gets rows where the country column is DE, FR, IT. This method is great when you have natural, low‑cardinality groups (region, status, subscription tier).

HASH

شما یک عبارتِ عددی روی ستون تعریف می‌کنید (مثلاً YEAR(OrderDate)) و باقی‌ماندهٔ تقسیم آن بر تعداد پارتیشن‌ها، شمارهٔ پارتیشن را مشخص می‌کند. توزیع سطرها بین پارتیشن‌ها یکنواخت است، اما ترتیب داده‌ها از بین می‌رود و کوئری‌های بازه‌ای نمی‌توانند پارتیشن‌های نامربوط را از پلن اجرا کنار بگذارند. You define an integer expression on the column (e.g. YEAR(OrderDate)) and the remainder of its division by the partition count determines the partition number. Rows are evenly distributed across partitions, but data order is lost and range queries cannot exclude irrelevant partitions from the execution plan.

KEY (MySQL only)

شبیه HASH، اما MySQL تابعِ هشِ داخلی خودش را استفاده می‌کند و شما نیازی به نوشتن عبارت ندارید. ستون‌های غیرعددی (رشته، تاریخ) را هم می‌پذیرد. Like HASH, but MySQL uses its own internal hashing function and you don't write an expression. It accepts non-integer columns (strings, dates).

امتحان کنید — یک سطر کجا می‌نشیند؟Try it — where does a row land?

بین چهار راهبرد جابه‌جا شوید و ببینید هر سطر نمونه در کدام پارتیشن فرود می‌آید.Switch between the four strategies and watch each sample row land in its partition.

Q1
Jan – Mar
2025-02-10
Q2
Apr – Jun
2025-05-03
Q3
Jul – Sep
2025-08-21
Q4
Oct – Dec
2025-11-30
مقدار ستون پارتیشن با مرزهای تعیین‌شده مقایسه می‌شود و بر اساس قرار گرفتن در یکی از بازه‌ها، به همان پارتیشن می‌رود، این روش برای کوئری‌های بازه‌ای بسیار کارآمد است The partition column value is compared against the defined boundaries and based on which range it falls into, it goes to that partition, this method is very efficient for range queries
AMER
USBR
EMEA
DEFR
APAC
JPIN
مقدار ستون پارتیشن با لیست مقادیر هر پارتیشن مقایسه می‌شود و در صورت تطابق، به آن پارتیشن می‌رود The partition column value is compared against each partition's value list and if it matches, it goes to that partition
bucket 0
year(col) % 4 = 0
bucket 1
year(col) % 4 = 1
bucket 2
year(col) % 4 = 2
bucket 3
year(col) % 4 = 3
عبارت عددیِ دلخواه شما روی ستون اعمال می‌شود و باقی‌ماندهٔ تقسیم آن بر تعداد پارتیشن‌ها، شمارهٔ سطل را مشخص می‌کند، توزیع یکنواخت است اما حذف بازه‌ای ممکن نیست Your custom integer expression is applied to the column and the remainder of its division by the partition count determines the bucket number, distribution is even but range pruning is not possible
bucket 0
sku:B-205
bucket 1
order:8841
bucket 2
user:42
bucket 3
user:113
MySQL از تابع هش داخلی خود روی ستون استفاده می‌کند و شمارهٔ سطل را مشخص می‌کند، نیازی به نوشتن عبارت نیست و روی هر نوع ستونی کار می‌کند MySQL applies its own internal hash function to the column and determines the bucket number, no expression needed and it works on any column type

علاوه بر چهار روش اصلی، MySQL دو قابلیتِ تکمیلی نیز دارد که در ادامه توضیح داده شده‌اند In addition to the four main methods, MySQL also has two complementary features explained below

RANGE COLUMNS / LIST COLUMNSRANGE COLUMNS / LIST COLUMNS

این‌ها در واقع نسخه‌های توسعه‌یافته‌ی همان RANGE و LIST هستند، نه روش‌های جداگانه. تفاوتشان این است که به‌جای یک عبارتِ عددی، خودِ ستون را می‌پذیرند — از جمله ستون‌های رشته‌ای و تاریخی. برای پارتیشن‌بندی مستقیم بر اساس ستونِ تاریخ، نیازی به TO_DAYS ندارید و ساده‌تر است. These are actually extended versions of the same RANGE and LIST, not separate methods. The difference is that they accept the column itself instead of an integer expression — including string and date columns. For partitioning directly on a date column, you don't need TO_DAYS and it's simpler.

Subpartitioning (پارتیشنِ تو در تو)Subpartitioning

این هم یک روشِ جداگانه نیست؛ بلکه یک لایه‌ی اضافی روی پارتیشن‌بندیِ موجود است. در MySQL می‌توانید هر پارتیشنِ RANGE یا LIST را یک بار دیگر با HASH یا KEY زیرپارتیشن کنید. مثلاً پارتیشن‌بندیِ اصلی بر اساس ماه، و زیرپارتیشن بر اساس CustomerID. کاربردی است، اما پیچیدگیِ مدیریت را چند برابر می‌کند. This is not a separate method either; it's an additional layer on top of existing partitioning. In MySQL, you can subpartition each RANGE or LIST partition once more with HASH or KEY. For example, main partitioning by month, and subpartitioning by CustomerID. Useful, but it multiplies management complexity.

یک نکتهٔ مهم: در MySQL چیزی به نام «پارتیشن‌بندی تاریخ» به عنوان یک روشِ مستقل وجود ندارد. هر وقت در مستندات یا مقالات می‌بینید که از «پارتیشن‌بندی بر اساس تاریخ» صحبت می‌شود، منظور همان روش RANGE است که روی یک ستونِ تاریخ یا یک عبارت مثل TO_DAYS(date_col) اعمال شده. در SQL Server نیز دقیقاً به همین شکل کار می‌کند — تنها راهِ پارتیشن‌بندیِ زمانی، استفاده از RANGE است. Important note: MySQL does not have a separate method called "date partitioning". Whenever you see "date-based partitioning" mentioned in documentation or articles, it means the RANGE method applied to a date column or an expression like TO_DAYS(date_col). SQL Server works exactly the same way — the only way to partition by time is using RANGE.

04

پارتیشن‌بندی در SQL Server — جدول‌ها، ویوها و فراترPartitioning in SQL Server — tables, views, and beyond

SQL Server دو مکانیزم مجزا برای پارتیشن‌بندی دارد و درک تفاوت‌شان برای هر توسعه‌دهنده‌ای ضروری است: Partitioned Tables (روش جدیدتر و قدرتمندتر) و Partitioned Views (روش قدیمی‌تر، اما هنوز هم کاربردی). بیایید اول این دو را کنار هم ببینیم، بعد سراغ جزئیات هرکدام برویم. SQL Server has two separate mechanisms for partitioning, and understanding the difference is essential for every developer: Partitioned Tables (the newer, more powerful approach) and Partitioned Views (the older method, but still very much alive). Let's first look at them side by side, then dive into the details of each.

Partitioned TablesPartitioned Tables

یک جدول منطقی، چند تکهٔ فیزیکی. از دید کاربر، یک جدول واحد می‌بینید. اما در پشت‌صحنه، داده بین چند پارتیشن توزیع شده که هرکدام روی یک Filegroup جداگانه قرار دارند. موتور با کمک Partition Function و Partition Scheme، مدیریت کامل این ساختار را به‌عهده دارد. One logical table, several physical pieces. From the user's perspective, it's a single table. Behind the scenes, data is distributed across multiple partitions, each sitting on its own filegroup. The engine handles the entire management using a partition function and a partition scheme.

Partitioned ViewsPartitioned Views

چند جدول جداگانه، یک ویو. شما چند جدول مستقل می‌سازید (مثلاً Orders_2023، Orders_2024، Orders_2025)، روی هرکدام یک CHECK CONSTRAINT برای ستون پارتیشن تعریف می‌کنید، و در نهایت یک ویو با UNION ALL روی همهٔ آنها می‌سازید. وقتی کوئری با WHERE اجرا می‌شود، موتور جدول‌های نامربوط را از پلن اجرا کنار می‌گذارد. Several separate tables, one view. You create independent tables (e.g. Orders_2023, Orders_2024, Orders_2025), define a CHECK CONSTRAINT on the partition column for each, and finally build a view that UNION ALLs them. When a query with a WHERE clause runs, the engine excludes irrelevant tables from the execution plan.

۱. Partitioned Tables — آجر به آجر 1. Partitioned Tables — brick by brick

برای ساختن یک جدول پارتیشن‌شده در SQL Server، سه مؤلفهٔ کلیدی داریم: Building a partitioned table in SQL Server requires three key components:

تابع پارتیشن Partition Function

Partition Function نوع ستون و مرزهای بین پارتیشن‌ها را مشخص می‌کند. در این مرحله، تنها یک قانون ریاضی تعریف شده‌است — هنوز به هیچ جدولی متصل نشده. The partition function defines the column type and the boundaries between partitions. At this stage, it's just a mathematical rule — not yet attached to any table.

طرح پارتیشن Partition Scheme

Partition Scheme تابع را به Filegroup ها نگاشت می‌کند — یعنی مشخص می‌کند هر پارتیشن روی کدام فایل دیسک قرار بگیرد. اینجاست که Tiered Storage ممکن می‌شود. The partition scheme maps the function onto filegroups — determining which disk file each partition lives on. This is where Tiered Storage becomes possible.

ستون پارتیشن Partitioning Column

ستونی که جدول بر اساس آن پارتیشن‌بندی می‌شود — مثلاً OrderDate. با ON scheme(column) جدول را روی طرح می‌سازید. این ستون قلب ماجراست و باید در هر کلید یکتا (از جمله Primary Key) حضور داشته باشد. The column by which the table is partitioned — for example, OrderDate. You create the table using ON scheme(column). This column is the heart of the matter and must be part of every unique key (including the Primary Key).

sql — partitioned table, three steps
-- 1) Partition function: the rule + boundaries
      CREATE PARTITION FUNCTION pf_OrdersByYear (date)
      AS RANGE RIGHT FOR VALUES
      ('2023-01-01', '2024-01-01', '2025-01-01');
      -- → 4 partitions: <2023, 2023–2024, 2024–2025, 2025+
      
      -- 2) Partition scheme: map partitions to filegroups
      CREATE PARTITION SCHEME ps_Orders
      AS PARTITION pf_OrdersByYear TO
      ([FG_Archive], [FG_2023], [FG_2024], [FG_Current]);
      
      -- 3) Create the table ON the scheme — OrderDate is the key
      CREATE TABLE dbo.Orders (
          Id         bigint IDENTITY NOT NULL,
          OrderDate  date NOT NULL,
          CustomerId int NOT NULL,
          Total      decimal(12,2) NOT NULL,
          CONSTRAINT PK_Orders PRIMARY KEY (Id, OrderDate)  -- key must include OrderDate!
      ) ON ps_Orders(OrderDate);

۲. Partitioned Views — روشِ قدیمی‌تر 2. Partitioned Views — the older technique

پیش از ورود Partitioned Tables به SQL Server، این روش تنها راهِ ممکن بود. اما هنوز هم کاربرد خودش را دارد؛ به‌ویژه وقتی که نیاز دارید جدول‌های عضو، ساختارِ کمی متفاوت داشته باشند یا روی سرورهای جداگانه (از طریق Linked Servers) قرار گیرند. Before Partitioned Tables arrived in SQL Server, this was the only option. It's still useful, especially when member tables need slightly different structures or need to reside on different servers (via Linked Servers).

sql — partitioned view with CHECK constraints
-- each member table has its own CHECK constraint to enforce the partition range
  CREATE TABLE dbo.Orders_2023 (
      Id int NOT NULL PRIMARY KEY,
      OrderDate date NOT NULL,
      CONSTRAINT CK_Orders_2023 CHECK
          (OrderDate >= '2023-01-01' AND OrderDate < '2024-01-01')
  );
  
  CREATE TABLE dbo.Orders_2024 (
      Id int NOT NULL PRIMARY KEY,
      OrderDate date NOT NULL,
      CONSTRAINT CK_Orders_2024 CHECK
          (OrderDate >= '2024-01-01' AND OrderDate < '2025-01-01')
  );
  
  CREATE TABLE dbo.Orders_2025 (
      Id int NOT NULL PRIMARY KEY,
      OrderDate date NOT NULL,
      CONSTRAINT CK_Orders_2025 CHECK
          (OrderDate >= '2025-01-01' AND OrderDate < '2026-01-01')
  );
  GO
  
  -- the view unions all member tables
  CREATE VIEW dbo.AllOrders AS
  SELECT * FROM dbo.Orders_2023
  UNION ALL
  SELECT * FROM dbo.Orders_2024
  UNION ALL
  SELECT * FROM dbo.Orders_2025;
  
  -- query through the view: optimizer uses CHECK constraints to eliminate irrelevant tables
  SELECT * FROM dbo.AllOrders
  WHERE OrderDate >= '2024-06-01' AND OrderDate < '2024-07-01';
  -- execution plan: only Orders_2024 is touched — the other tables are pruned ✓

۳. مقایسه و استفادهٔ ترکیبی 3. Comparison and using them together

حالا که با هر دو مکانیزم آشنا شدید، این جدول کمک می‌کند در یک نگاه تفاوت‌های کلیدی را ببینید و تصمیم بگیرید کدام یک برای سناریوی شما مناسب‌تر است. Now that you're familiar with both mechanisms, this table helps you see the key differences at a glance and decide which one fits your scenario better.

Partitioned Tables Partitioned Views
ساختارStructure یک جدول منطقی، چند تکهٔ فیزیکیOne logical table, multiple physical pieces چند جدول جداگانه، یک ویوSeveral separate tables, one view
انعطافِ ساختاریSchema flexibility همهٔ پارتیشن‌ها ساختار یکسانی دارندAll partitions share the same schema هر جدول عضو می‌تواند ساختارِ کمی متفاوت داشته باشدEach member table can have a slightly different schema
مدیریتManagement موتور همه‌چیز را خودکار مدیریت می‌کندThe engine handles everything automatically شما باید جدول‌های عضو و ویو را دستی مدیریت کنیدYou must manage member tables and the view manually
عملیاتِ SWITCH SWITCH operation [1] پشتیبانی می‌شود — انتقالِ آنیِ یک پارتیشن به جدول دیگرSupported — instant transfer of a partition to another table پشتیبانی نمی‌شود — باید جدول را حذف و ویو را تغییر دهیدNot supported — you must drop the table and alter the view
سرورهای مختلفDifferent servers فقط در یک نمونهٔ SQL ServerOnly within one SQL Server instance با Linked Servers، جدول‌های عضو می‌توانند روی سرورهای جدا باشندWith Linked Servers, member tables can reside on separate servers
کِی استفاده کنیمWhen to use گزینهٔ پیش‌فرض برای اکثر سناریوهاThe default choice for most scenarios وقتی به ساختارهای متفاوت یا سرورهای جداگانه نیاز داریدWhen you need different schemas or separate servers
[1] SWITCH عملیاتی است که یک پارتیشن کامل را در حد میلی‌ثانیه از یک جدول به جدول دیگر منتقل می‌کند — بدون اینکه حتی یک سطر جابه‌جا شود. فقط متادیتا (اشاره‌گرهای کاتالوگ) تغییر می‌کند. این ویژگی برای آرشیو کردنِ سریعِ داده‌های قدیمی (الگوی Sliding Window) بسیار ارزشمند است. SWITCH is an operation that transfers an entire partition from one table to another in milliseconds — without moving a single row. Only metadata (catalog pointers) changes. This feature is extremely valuable for quickly archiving old data (the Sliding Window pattern).

استفادهٔ ترکیبی: در عمل، می‌توانید از هر دو روش با هم بهره ببرید. مثلاً داده‌های «داغ» را در یک جدول پارتیشن‌شده نگه دارید و یک ویوی پارتیشن‌شده بسازید که این جدول را با یک جدول قدیمیِ جداگانه (روی یک Filegroup آرشیو) ترکیب کند. این رویکرد، انعطاف‌پذیری هر دو روش را در اختیار شما قرار می‌دهد. Using them together: in practice, you can combine both approaches. For example, keep "hot" data in a partitioned table and create a partitioned view that combines it with a separate older table (on an archive filegroup). This approach gives you the flexibility of both methods.

۴. Tiered Storage — داغ روی SSD، کهنه روی HDD 4. Tiered Storage — hot on SSD, cold on HDD

یادتان هست که Partition Scheme هر پارتیشن را به یک Filegroup متصل می‌کند؟ این یعنی می‌توانید Filegroup‌های مختلف را روی دیسک‌های متفاوت قرار دهید — یکی روی SSDِ گران‌قیمت و سریع، دیگری روی HDDِ ارزان‌تر و کندتر. داده‌ی امسال روی SSD می‌نشیند و کوئری‌های داغ را سریع پاسخ می‌دهد؛ داده‌ی ۵ سال پیش روی HDD می‌رود و هزینه‌ی ذخیره‌سازی را پایین می‌آورد. همین. Remember that the partition scheme maps each partition to a filegroup? This means you can place different filegroups on different disks — one on expensive, fast SSD, another on cheaper, slower HDD. This year's data lives on SSD and serves hot queries quickly; 5-year-old data goes to HDD and keeps storage costs down. That's it.

🔥 SSD · HOT · fast

پارتیشن‌های ۲۰۲۵ و ۲۰۲۶: داده‌ای که همین الان کوئری می‌شود. سریع، گران‌تر، اما حجمش کم است. 2025 and 2026 partitions: data being queried right now. Fast, more expensive, but smaller in size.

❄ HDD · COLD · cheap

پارتیشن‌های قدیمی‌تر: داده‌ای که به‌ندرت لمس می‌شود. کندتر، ارزان‌تر، اما حجمش زیاد است. Older partitions: data that's rarely touched. Slower, cheaper, but larger in size.

۵. Sliding Window — آرشیوِ آنی، بدون DELETE 5. Sliding Window — instant archive, no DELETE

این یکی از کاربردی‌ترین الگوهای پارتیشن‌بندی است — و همان دلیلی که بسیاری از تیم‌ها اصلاً سراغ پارتیشن‌بندی می‌روند. فرض کنید تصمیم دارید همیشه ۱۲ ماهِ اخیر را در جدول اصلی نگه دارید. هر ماه، یک پارتیشنِ جدید برای داده‌های تازه اضافه می‌کنید و قدیمی‌ترین پارتیشن را با ALTER TABLE ... SWITCH آنی به یک جدول آرشیو منتقل می‌کنید. این عملیات فقط متادیتا را تغییر می‌دهد و حتی اگر پارتیشن صدها میلیون سطر داشته باشد، فقط چند میلی‌ثانیه طول می‌کشد. خبری از DELETE های سنگین نیست، لاگ تراکنش باد نمی‌کند، و قفلِ طولانی‌مدتی روی جدول نمی‌آید. This is one of the most practical partitioning patterns — and the very reason many teams adopt partitioning in the first place. Suppose you decide to keep only the last 12 months in the main table. Each month, you add a fresh partition for new data and ALTER TABLE ... SWITCH the oldest partition instantly to an archive table. This operation changes only metadata and takes milliseconds, even if the partition holds hundreds of millions of rows. No heavy DELETEs, no transaction log bloat, and no long‑lasting table locks.

جدول اصلی — OrdersMain table — Orders
P1 (Jan) P2 (Feb) ← moved out P3 (Mar) P4 (Apr)
SWITCH
جدول آرشیو — OrdersArchiveArchive table — OrdersArchive
P2 (Feb) — ۸ میلیون سطر، در ۲ میلی‌ثانیه!P2 (Feb) — 8 million rows, in 2ms!
sql — the sliding window in action
-- archive table must have EXACTLY the same structure as the main table
CREATE TABLE dbo.OrdersArchive (/* identical schema */);

-- instant move: metadata-only, milliseconds even for huge partitions
ALTER TABLE dbo.Orders
SWITCH PARTITION 1 TO dbo.OrdersArchive;

-- remove the now‑empty partition boundary (merge it with the previous one)
ALTER PARTITION FUNCTION pf_OrdersByYear()
MERGE RANGE ('2023-01-01');

-- add a fresh partition for the upcoming month/year
ALTER PARTITION FUNCTION pf_OrdersByYear()
SPLIT RANGE ('2026-01-01');

۶. Potential Issues — تله‌هایی که باید بدانید 6. Potential Issues — traps to know about

کلید یکتا باید شامل ستون پارتیشن باشد Unique keys must include the partition column

این مهم‌ترین محدودیت است: هر PRIMARY KEY یا UNIQUE روی جدول پارتیشن‌شده باید ستون پارتیشن را هم داشته باشد. پس نمی‌توانید PRIMARY KEY (Id) داشته باشید — باید PRIMARY KEY (Id, OrderDate) باشد. این را از روز اول در مدل EF Core لحاظ کنید. This is the most important constraint: every PRIMARY KEY or UNIQUE constraint on a partitioned table must include the partition column. So you can't have PRIMARY KEY (Id) — it must be PRIMARY KEY (Id, OrderDate). Bake this into your EF Core model from day one.

اگر ستون پارتیشن در WHERE نباشد، رد شدن اتفاق نمی‌افتد If the partition column is missing from WHERE, pruning does not occur

رایج‌ترین اشتباه این است که کوئری درست می‌نویسید و جواب هم درست می‌گیرید، اما متوجه نمی‌شوید موتور همهٔ پارتیشن‌ها را خوانده است. چون ستون پارتیشن در فیلتر نیست، موتور راهی برای رد کردنِ پارتیشن‌های نامربوط ندارد. WHERE YEAR(OrderDate) = 2025 هم دقیقاً همین مشکل را دارد — تابع، مقایسه با مرزهای پارتیشن را غیرممکن می‌کند. راه‌حل: همیشه مستقیماً روی خودِ ستون مقایسه کنید، مثلاً WHERE OrderDate >= '2025-01-01' AND OrderDate < '2026-01-01'. The most common mistake is writing a valid query that returns correct results, without realizing the engine has read every partition. Because the partition column isn't in the filter, the engine has no way to prune irrelevant partitions. WHERE YEAR(OrderDate) = 2025 has the exact same issue — the function makes boundary comparison impossible. The fix: always compare the column directly, e.g. WHERE OrderDate >= '2025-01-01' AND OrderDate < '2026-01-01'.

ایندکس‌های aligned و non‑aligned Aligned vs non‑aligned indexes

ایندکسی که از همان Partition Scheme استفاده می‌کند، aligned نامیده می‌شود. SWITCH فقط با ایندکس‌های aligned کار می‌کند. اگر ایندکسی روی Scheme دیگری بسازید (non‑aligned)، نمی‌توانید پارتیشن را سوییچ کنید مگر اینکه اول آن ایندکس را حذف کنید. An index using the same partition scheme is called aligned. SWITCH only works with aligned indexes. If you build an index on a different scheme (non‑aligned), you cannot switch the partition unless you drop that index first.

سوییچ، ساختارِ دقیقاً یکسان می‌خواهد SWITCH needs exactly matching structure

جدول مقصدِ SWITCH باید دقیقاً همان ساختار، همان ایندکس‌های aligned، همان CHECK CONSTRAINTها و همان Filegroup را داشته باشد. کوچک‌ترین تفاوتی، عملیات را خراب می‌کند. The SWITCH target table must have exactly the same structure, the same aligned indexes, the same CHECK CONSTRAINTs, and the same filegroup. The slightest difference breaks the operation.

.NET

EF Core به‌طور خودکار تابع و طرح پارتیشن نمی‌سازد — این کارِ Migration دستی یا DBA است. اما EF Core از PRIMARY KEYهای ترکیبی پشتیبانی می‌کند: builder.HasKey(o => new { o.Id, o.OrderDate }); و این دقیقاً همان چیزی است که پارتیشن‌بندی SQL Server نیاز دارد. کد LINQ شما دست‌نخورده می‌ماند — همه‌چیز شفاف است. EF Core won't auto‑create partition functions or schemes — that's manual migration or DBA work. But EF Core supports composite primary keys: builder.HasKey(o => new { o.Id, o.OrderDate }); — and that's exactly what SQL Server partitioning requires. Your LINQ code stays untouched — everything is transparent.

05

پارتیشن‌بندی در MySQL — همان ایده، نحوهٔ تعریف متفاوت Partitioning in MySQL — same idea, different definition syntax

در MySQL، چهار روش پارتیشن‌بندی یعنی RANGE، LIST، HASH و KEY را داریم، اما نحوهٔ تعریف آنها با SQL Server تفاوت دارد. MySQL همه‌چیز را داخل خودِ دستور CREATE TABLE تعریف می‌کند — خبری از اشیای جداگانه‌ای مثل Partition Function و Scheme نیست. یک تفاوت مهم دیگر: از MySQL 8.0 به بعد، فقط InnoDB و NDB از پارتیشن‌بندی پشتیبانی می‌کنند ولی MyISAM نه. In MySQL, we have the four partitioning methods RANGE, LIST, HASH, and KEY, but the way they're defined differs from SQL Server. MySQL defines everything inside the CREATE TABLE statement itself — no separate objects like partition functions and schemes. Another important difference: from MySQL 8.0 onward, only InnoDB and NDB support partitioning — not MyISAM.

mysql — RANGE partitioning
CREATE TABLE orders (
  id          BIGINT NOT NULL AUTO_INCREMENT,
  order_date  DATE NOT NULL,
  customer_id INT NOT NULL,
  total       DECIMAL(12,2),
  PRIMARY KEY (id, order_date)
)
PARTITION BY RANGE (YEAR(order_date)) (
  PARTITION p2023 VALUES LESS THAN (2024),
  PARTITION p2024 VALUES LESS THAN (2025),
  PARTITION p2025 VALUES LESS THAN (2026),
  PARTITION pmax  VALUES LESS THAN (MAXVALUE)
);
mysql — RANGE COLUMNS (no function needed)
CREATE TABLE events (
  id         BIGINT NOT NULL AUTO_INCREMENT,
  event_date DATE NOT NULL,
  payload    JSON,
  PRIMARY KEY (id, event_date)
)
PARTITION BY RANGE COLUMNS(event_date) (
  PARTITION p2024 VALUES LESS THAN ('2025-01-01'),
  PARTITION p2025 VALUES LESS THAN ('2026-01-01'),
  PARTITION pmax  VALUES LESS THAN (MAXVALUE)
);
-- COLUMNS variant: use the date directly, no TO_DAYS()
mysql — HASH (your expression)
CREATE TABLE user_sessions (
  id         INT NOT NULL,
  user_id    INT NOT NULL,
  payload    TEXT,
  PRIMARY KEY (id, user_id)
)
PARTITION BY HASH(user_id)
PARTITIONS 8;
-- user_id % 8 picks the bucket
mysql — KEY (MySQL's own hash)
CREATE TABLE api_logs (
  trace_id   VARCHAR(36) NOT NULL,
  ts         DATETIME NOT NULL,
  body       TEXT,
  PRIMARY KEY (trace_id, ts)
)
PARTITION BY KEY(trace_id)
PARTITIONS 16;
-- works on non-integer columns; MySQL hashes internally
mysql — subpartitioning (partition within partition)
CREATE TABLE sales (
  id          BIGINT NOT NULL AUTO_INCREMENT,
  region      VARCHAR(10) NOT NULL,
  sale_date   DATE NOT NULL,
  amount      DECIMAL(12,2),
  PRIMARY KEY (id, region, sale_date)
)
PARTITION BY LIST COLUMNS(region)
SUBPARTITION BY HASH (YEAR(sale_date))
SUBPARTITIONS 4 (
  PARTITION pAMER VALUES IN ('US', 'CA', 'BR'),
  PARTITION pEMEA VALUES IN ('DE', 'FR', 'UK'),
  PARTITION pAPAC VALUES IN ('JP', 'IN', 'AU')
);
-- first layer: LIST by region
-- second layer: HASH on year within each region
تفاوت‌های کلیدی با SQL ServerKey differences vs SQL Server SQL Server MySQL
محل تعریفDefinition location دو شیء جدا (تابع + طرح) و سپس جدولTwo separate objects (function + scheme), then the table مستقیم داخل CREATE TABLEInline inside CREATE TABLE
انواع پارتیشنPartition types RANGE RANGE · LIST · HASH · KEY
SubpartitioningSubpartitioning پشتیبانی نمی‌شودNot supported پشتیبانی می‌شودSupported
کلید یکتا / PKUnique key / PK باید شامل ستون پارتیشن باشدMust include the partition column همین‌طور — سخت‌گیرانهSame — strictly enforced
Foreign KeyForeign keys پشتیبانی می‌شودSupported روی جدول پارتیشن‌شده پشتیبانی نمی‌شودNot supported on partitioned tables
موتورهای پشتیبانی‌شدهSupported engines همه (SQL Server فقط یک موتور دارد)All (SQL Server only has one engine) فقط InnoDB و NDB (از 8.0 به بعد)InnoDB and NDB only (since 8.0)
دیدن پارتیشن‌های استفاده‌شدهSee which partitions were used Actual Partition Count در پلن اجراin the plan ستون partitions در خروجی EXPLAINthe partitions column in EXPLAIN
.NET

اگر از MySQL با EF Core استفاده می‌کنید (مثلاً با Pomelo.EntityFrameworkCore.MySql)، پارتیشن‌بندی برای کد شما کاملاً شفاف است. فقط دو نکته را جدی بگیرید: ۱) محدودیت Foreign Key — اگر مدل شما به FKهای زیادی متکی است، قبل از پارتیشن‌بندی در MySQL دو بار فکر کنید. ۲) فقط از InnoDB استفاده کنید؛ MyISAM از نسخهٔ ۸٫۰ به بعد دیگر پشتیبانی نمی‌شود. If you're using MySQL with EF Core (say, with Pomelo.EntityFrameworkCore.MySql), partitioning is completely transparent to your code. Just keep two things in mind: 1) the foreign-key limitation — if your model relies heavily on FKs, think twice before partitioning in MySQL. 2) use InnoDB only; MyISAM is no longer supported from version 8.0 onward.

06

پارتیشن‌بندی در برابر Sharding — این دو را قاطی نکنید Partitioning vs Sharding — don't mix these up

هر دو یعنی «تقسیم داده» و به همین دلیل اغلب به جای هم استفاده می‌شوند. اما تفاوتشان یک سوال ساده است: تقسیم روی یک سرور، یا روی چند سرور؟ پاسخ به این سوال، معماری شما را تعیین می‌کند. Both mean "splitting data" and are often used interchangeably. But their difference comes down to one simple question: splitting on one server, or across many? The answer defines your architecture.

Partitioning — یک خانه، چند اتاق Partitioning — one house, several rooms

همهٔ پارتیشن‌ها داخل یک نمونهٔ دیتابیس قرار دارند. موتور خودش می‌داند هر داده کجاست؛ کوئری شما عادی است و نیازی به مسیریابی دستی ندارید. JOIN بین پارتیشن‌ها؟ موتور خودش انجام می‌دهد. All partitions live inside one database instance. The engine knows where everything is; your queries are normal and need no manual routing. JOINs across partitions? The engine handles them itself.

Sharding — چند خانهٔ جدا Sharding — several separate houses

داده بین چند سرور مستقل پخش می‌شود. هیچ سروری همهٔ داده را ندارد. حالا شما (یا یک لایهٔ مسیریاب) باید بدانید هر بخش از داده کجاست، کوئری را به سرور درست بفرستید، و JOIN های بین‌بخشی را خودتان مدیریت کنید. Data is spread across multiple independent servers. No single server holds everything. Now you (or a routing layer) must know where each shard lives, route queries to the right server, and handle cross‑shard JOINs yourself.

Partitioning Sharding
کجا؟Where? یک سرور / یک نمونهOne server / one instance چند سرور مستقلMultiple independent servers
چه کسی مسیریابی می‌کند؟Who routes? خودِ موتور — شفافThe engine itself — transparent اپلیکیشن یا لایهٔ proxyThe app or a proxy layer
کوئری بین تکه‌هاCross‑piece queries عادی — موتور انجام می‌دهدNormal — the engine handles it سخت — باید خودتان جمع کنیدHard — you must stitch results together
هدف اصلیMain goal کارایی و نگهداری روی دادهٔ بزرگPerformance and maintenance on large data مقیاس افقی فراتر از توان یک سرورHorizontal scale beyond one server's limits
تشبیهAnalogy یک ساختمان با چندین طبقهOne building with multiple floors چند ساختمان جداگانه در نقاط مختلف شهرSeparate buildings in different parts of the city
.NET

ترتیب عملی: اول ایندکس. بعد پارتیشن‌بندی — چون کد شما را تغییر نمی‌دهد. پارتیشن‌بندی توزیع‌شده را فقط وقتی به میز بیاورید که یک سرور واقعاً دیگر پاسخگو نیست — چون یک پروژهٔ معماری کامل است: مسیریابی، توزیع مجدد، تراکنش‌های بین‌بخشی. Practical order: indexes first. Then partitioning — because it doesn't change your code. Bring sharding to the table only when one server truly can't keep up — because it's a full architecture project: routing, rebalancing, cross‑shard transactions.

07

تماشا کنید — حذف پارتیشن در عملWatch it — partition elimination in action

جدول Orders — پارتیشن‌شده بر اساس دوماهOrders table — partitioned by two-month slices

جدول ما ۶ پارتیشن دارد (هرکدام ~۲٬۰۰۰ صفحه). یک کوئری انتخاب کنید و ببینید موتور کدام پارتیشن‌ها را می‌خواند و کدام‌ها را کلاً رد می‌کند. Our table has 6 partitions (~2,000 pages each). Pick a query and watch which partitions the engine reads and which it skips entirely.

SKIPPED ✓
P1
Jan–Feb
SKIPPED ✓
P2
Mar–Apr
SKIPPED ✓
P3
May–Jun
SKIPPED ✓
P4
Jul–Aug
SKIPPED ✓
P5
Sep–Oct
SKIPPED ✓
P6
Nov–Dec
پارتیشن خوانده‌شدهPartitions read
0 / 6
Logical Reads (تقریبی)Logical reads (approx.)
0
دادهٔ ردشدهData skipped

به کوئری سوم دقت کنید: WHERE CustomerId = 1042. این کوئری کاملاً درست است و جواب درست می‌دهد — اما چون ستون پارتیشن (OrderDate) در WHERE نیست، موتور هیچ راهی برای حذف پارتیشن ندارد و هر شش پارتیشن را می‌خواند. این پرهزینه‌ترین نتیجهٔ یک انتخابِ بدِ کلید است. Look closely at query three: WHERE CustomerId = 1042. This query is perfectly valid and returns correct results — but because the partition key (OrderDate) isn't in the WHERE clause, the engine has no way to eliminate anything and reads all six partitions. That's the most expensive outcome of a poor key choice.

08

سود و زیان — جمع‌بندی Trade-offs — the recap

پارتیشن‌بندی کمک می‌کند وقتی… Partitioning helps when…

۱. جدول واقعاً بزرگ باشد — از ده‌ها میلیون سطر شروع شود.
۲. داده سری‌زمانی باشد و کوئری‌ها فیلترِ بازهٔ زمانی داشته باشند.
۳. نیاز به آرشیو یا پاک‌سازیِ منظمِ دادهٔ قدیمی داشته باشید (Sliding Window).
۴. نگهداری (بازسازی ایندکس، بکاپ) روی کلِ جدول به یک دردسر تبدیل شده باشد.
۵. بخواهید دادهٔ داغ و کهنه را روی دیسک‌های متفاوت نگه دارید (Tiered Storage).
1. The table is truly large — starting from tens of millions of rows.
2. The data is time‑series and queries carry time‑range filters.
3. You need regular archiving or purging of old data (sliding window).
4. Maintenance (index rebuilds, backups) on the whole table has become a pain.
5. You want hot and cold data on different disks (tiered storage).

ضرر می‌زند وقتی… It hurts when…

۱. جدول کوچک باشد — پیچیدگی اضافه می‌شود بدون اینکه سودی داشته باشد.
۲. کوئری‌ها هیچ‌وقت روی ستون پارتیشن فیلتر نکنند.
۳. بیشتر دسترسی‌ها تک‌سطری با Primary Key باشد — همان ایندکس خوشه‌ای کافی است.
۴. تیم، ظرفیتِ نگهداریِ یک ساختار پیچیده‌تر را نداشته باشد.
۵. در MySQL به Foreign Key نیاز داشته باشید — پشتیبانی نمی‌شود.
1. The table is small — added complexity without any benefit.
2. Queries never filter on the partition column.
3. Most access is single‑row by Primary Key — the clustered index already covers it.
4. Your team can't handle the maintenance of a more complex structure.
5. You need foreign keys in MySQL — they're not supported.

قاعدهٔ سرانگشتی: قبل از پارتیشن‌بندی دو سوال بپرسید: «کوئری‌های داغِ من روی کدام ستون فیلتر دارند؟» و «نگهداریِ جدول چقدر برایم دردسر دارد؟» اگر پاسخِ اولی یک ستونِ تاریخ با توزیعِ منظم باشد و پاسخِ دومی «زیاد»، پارتیشن‌بندی احتمالاً سود دارد. اگر جواب «نمی‌دانم» است، اول Query Store (در SQL Server) یا performance_schema (در MySQL) را روشن کنید. Rule of thumb: before partitioning, ask two questions: "which column do my hot queries filter on?" and "how painful is table maintenance?" If the first answer is a date column with steady distribution and the second is "very", partitioning probably pays off. If the answer is "I don't know", turn on Query Store (in SQL Server) or performance_schema (in MySQL) first.

.NET

چک‌لیست سریع شما: در SSMS، پلن اجرای واقعی (Actual Plan) را باز کنید و خاصیت Actual Partition Count را روی عملگر Scan/Seek ببینید. اگر برابر تعداد کل پارتیشن‌ها بود، یعنی رد شدن اتفاق نیفتاده است. در MySQL با EXPLAIN ستون partitions را بررسی کنید. در EF Core با LogTo(Console.WriteLine) خروجی SQL را ببینید و مطمئن شوید که فیلتر تاریخ واقعاً ارسال می‌شود. Your quick checklist: in SSMS, open the Actual Execution Plan and check the Actual Partition Count property on the Scan/Seek operator. If it equals the total number of partitions, pruning did not occur. In MySQL, run EXPLAIN and check the partitions column. In EF Core, use LogTo(Console.WriteLine) to see the SQL output and confirm the date filter is actually being sent.

خودآزمایی — می‌توانید این‌ها را بلند جواب بدهید؟ Self-check — can you answer these out loud?

صفحه را ببندید و با کلمات خودتان جواب بدهید، سپس کلیک کنید و پاسخ را با توضیحات مقایسه کنید. اگر سوالی شما را گیر انداخت، به همان ایستگاه برگردید و دوباره بخوانید. Close the page, answer in your own words, then click to compare your response with the explanation. If a question stumps you, jump back to that stop and review it.

Partitioned Tables یک جدول منطقی است که موتور با کمک Partition Function و Partition Scheme آن را به چند تکهٔ فیزیکی تقسیم می‌کند. همه‌چیز خودکار است و ALTER TABLE SWITCH پشتیبانی می‌شود. Partitioned Views چند جدول جداگانه هستند که هرکدام CHECK CONSTRAINT مخصوص خود را دارند و یک ویو با UNION ALL آنها را ترکیب می‌کند. این روش قدیمی‌تر، اما انعطاف‌پذیرتر است. زمانی سراغ ویوها می‌رویم که هر جدول عضو نیاز به ساختارِ کمی متفاوت داشته باشد، یا وقتی جدول‌ها باید روی سرورهای مختلف (از طریق Linked Servers) قرار بگیرند. Partitioned tables are a single logical table that the engine splits into physical pieces using a partition function and a partition scheme. Everything is automatic and ALTER TABLE SWITCH is supported. Partitioned views are separate tables, each with its own CHECK CONSTRAINT, combined by a view with UNION ALL. This older method is more flexible. Use views when each member table needs a slightly different schema, or when tables must reside on different servers (via Linked Servers).
Sliding Window الگویی برای نگه‌داشتن یک پنجرهٔ زمانیِ ثابت از داده است (مثلاً ۱۲ ماه اخیر). هر دوره، یک پارتیشن جدید برای دادهٔ تازه اضافه می‌کنید و قدیمی‌ترین پارتیشن را با SWITCH به یک جدول آرشیو منتقل می‌کنید. دلیل سرعت: این عملیات فقط متادیتا را تغییر می‌دهد — هیچ سطری جابه‌جا یا کپی نمی‌شود. موتور فقط اشاره‌گرهای کاتالوگ را به‌روز می‌کند تا بداند پارتیشن حالا متعلق به جدول دیگری است. به همین دلیل، حتی برای پارتیشن‌های صدها میلیونی، در حد میلی‌ثانیه تمام می‌شود و لاگ تراکنش را باد نمی‌کند. Sliding Window is a pattern for keeping a fixed time window of data (e.g., the last 12 months). Each period, you add a new partition for fresh data and SWITCH the oldest partition to an archive table. Why it's fast: the operation only changes metadata — no rows are moved or copied. The engine updates catalog pointers so the partition now belongs to a different table. That's why it finishes in milliseconds even for huge partitions and doesn't bloat the transaction log.
هر دو توزیع یکنواخت بین چند سطل (bucket) ایجاد می‌کنند. تفاوت اصلی: HASH به یک عبارت عددی که شما می‌نویسید نیاز دارد (مثلاً YEAR(col) یا col % 4)، اما KEY از تابع هش داخلی خود MySQL استفاده می‌کند و روی ستون‌های غیرعددی (رشته، تاریخ) بدون هیچ تبدیلی کار می‌کند. هر دو روش، ترتیب داده را از بین می‌برند و کوئری‌های بازه‌ای نمی‌توانند پارتیشن‌های نامربوط را از پلن اجرا کنار بگذارند. Both spread data evenly across buckets. The main difference: HASH needs an integer expression you write (e.g., YEAR(col) or col % 4), while KEY uses MySQL's own internal hash function and works on non‑integer columns (strings, dates) without any conversion. Both methods lose data order, and range queries cannot exclude irrelevant partitions from the execution plan.
وقتی ستون را درون تابع می‌پیچید، موتور دیگر مقدار خام را در اختیار ندارد که با مرزهای پارتیشن مقایسه کند — پس مجبور است تابع را برای هر سطر محاسبه کند و همهٔ پارتیشن‌ها را بخواند. راه درست، مقایسهٔ مستقیم و بازه‌ای است: WHERE OrderDate >= '2025-01-01' AND OrderDate < '2026-01-01'. When you wrap the column in a function, the engine no longer has the raw value to compare against partition boundaries — so it must evaluate the function per row and read every partition. The correct approach is a direct range comparison: WHERE OrderDate >= '2025-01-01' AND OrderDate < '2026-01-01'.
Partitioning داده را داخل یک نمونهٔ دیتابیس تقسیم می‌کند — موتور همه‌چیز را مدیریت می‌کند و کوئری‌ها شفاف می‌مانند. Sharding داده را بین چند سرور مستقل پخش می‌کند؛ مسیریابی کوئری‌ها به عهدهٔ اپلیکیشن یا یک لایهٔ proxy است و JOINهای بین‌بخشی سخت می‌شوند. ترتیب منطقی: اول ایندکس، بعد پارتیشن‌بندی، و Sharding را فقط وقتی به کار بگیرید که یک سرور واقعاً کافی نباشد — چون یک پروژهٔ معماری کامل است. Partitioning splits data within one database instance — the engine handles everything and queries stay transparent. Sharding spreads data across multiple independent servers; the application or a proxy layer must route queries, and cross‑shard JOINs become hard. The logical order: indexes first, then partitioning, and only consider sharding when one server truly isn't enough — because it's a full architecture project.