mirror of https://github.com/abpframework/abp.git
Browse Source
Added a new community article explaining the drawbacks of random GUIDs for clustered indexes and the benefits of sequential GUIDs, with a focus on ABP Framework's default sequential GUID generation. Includes a cover image for the article.pull/23873/head
2 changed files with 108 additions and 0 deletions
@ -0,0 +1,108 @@ |
|||
# You May Have Trouble with GUIDs: Generating Sequential GUIDs in .NET |
|||
|
|||
|
|||
If you’ve ever shoved a bunch of `Guid.NewGuid()` values into a SQL Server table with a clustered index on the PK, you’ve probably felt the pain: **Index fragmentation so bad you could use it as modern art.** Inserts slow down, page splits go wild, and your DBA starts sending you passive-aggressive Slack messages. |
|||
|
|||
And yet… we keep doing it. Why? Because GUIDs are _easy_. They’re globally unique, they don’t need a round trip to the DB, and they make distributed systems happy. But here’s the catch: **random GUIDs are absolute chaos for ordered indexes**. |
|||
|
|||
## The Problem with Vanilla GUIDs |
|||
|
|||
* **Randomness kills order** — clustered indexes thrive on sequential inserts; random GUIDs force constant reordering. |
|||
|
|||
* **Performance hit** — every insert can trigger page splits and index reshuffling. |
|||
|
|||
* **Storage bloat** — fragmentation means wasted space and slower reads. |
|||
|
|||
Sure, you could switch to int or long identity columns, but then you lose the distributed generation magic and security benefits (predictable IDs are guessable). |
|||
|
|||
## Sequential GUIDs to the Rescue |
|||
|
|||
Sequential GUIDs keep the uniqueness but add a predictable ordering component — usually by embedding a timestamp in part of the GUID.This means: |
|||
|
|||
* Inserts happen at the “end” of the index, not all over the place. |
|||
|
|||
* Fragmentation drops dramatically. |
|||
|
|||
* You still get globally unique IDs without DB trips. |
|||
|
|||
Think of it as **GUIDs with manners**. |
|||
|
|||
## ABP Framework’s Secret Sauce |
|||
|
|||
|
|||
Here’s where ABP Framework flexes: it **uses sequential GUIDs by default** for entity IDs. No ceremony, no “remember to call this helper method” — it’s baked in. |
|||
|
|||
Under the hood: |
|||
|
|||
* ABP ships with IGuidGenerator (default: SequentialGuidGenerator). |
|||
|
|||
* It picks the right sequential strategy for your DB provider: |
|||
|
|||
* **SequentialAtEnd** → SQL Server |
|||
|
|||
* **SequentialAsString** → MySQL/PostgreSQL |
|||
|
|||
* **SequentialAsBinary** → Oracle |
|||
|
|||
* EF Core integration packages auto-configure this, so you rarely need to touch it. |
|||
|
|||
Example in ABP: |
|||
|
|||
```csharp |
|||
public class MyProductService : ITransientDependency |
|||
{ |
|||
private readonly IRepository<Product, Guid> _productRepository; |
|||
private readonly IGuidGenerator _guidGenerator; |
|||
|
|||
|
|||
public MyProductService( |
|||
IRepository<Product, Guid> productRepository, |
|||
IGuidGenerator guidGenerator) |
|||
{ |
|||
_productRepository = productRepository; |
|||
_guidGenerator = guidGenerator; |
|||
} |
|||
|
|||
|
|||
public async Task CreateAsync(string productName) |
|||
{ |
|||
var product = new Product(_guidGenerator.Create(), productName); |
|||
await _productRepository.InsertAsync(product); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
No `Guid.NewGuid()` here, \`_guidGenerator.Create()` gives you a sequential GUID every time. |
|||
|
|||
## Benefits of Squential GUIDs |
|||
|
|||
Let’s say you’re inserting 1M rows into a table with a clustered primary key: |
|||
|
|||
* **Random GUIDs** → fragmentation ~99%, insert throughput tanks. |
|||
|
|||
* **Sequential GUIDs** → fragmentation stays low, inserts fly. |
|||
|
|||
In high-volume systems, this difference is **not** academic — it’s the difference between smooth scaling and spending weekends rebuilding indexes. |
|||
|
|||
## When to Use Sequential GUIDs |
|||
|
|||
* **Distributed systems** that still want DB-friendly inserts. |
|||
|
|||
* **High-write workloads** with clustered indexes on GUID PKs. |
|||
|
|||
* **Multi-tenant apps** where IDs need to be unique across tenants. |
|||
|
|||
## When Random GUIDs Still Make Sense |
|||
|
|||
* Security through obscurity — if you don’t want IDs to hint at creation order. |
|||
|
|||
* Non-indexed identifiers — fragmentation isn’t a concern. |
|||
|
|||
## The Final Take |
|||
|
|||
ABP’s default sequential GUID generation is one of those “**small but huge**” features. It’s the kind of thing you don’t notice until you benchmark — and then you wonder why you ever lived without it. |
|||
|
|||
## Links |
|||
You may want to check the following references to learn more about sequential GUIDs: |
|||
|
|||
- [ABP Framework Documentation: Sequential GUIDs](https://docs.abp.io/en/abp/latest/Guid-Generation) |
|||
|
After Width: | Height: | Size: 822 KiB |
Loading…
Reference in new issue