Clustered Sequential GUID Primary Key vs Non-Clustered GUID and Clustered Sequential ID Primary Keys
I'm trying to figure out what the optimal solution is in regards to size and performance when using GUIDs as a primary key. The main reason for wanting GUIDs is that it's more appropriate in our micro-service architecture. There seems to be a lot of debate on what's actually best.
The two main solutions appear to be:
Have the primary key as a sequential GUID (generated by the
application, rather than SQL Servers built in NEWSEQUENTIALID() as it isn't truly sequential)
with a clustered index.Have two primary keys, a randomly generated GUID with a
non-clustered index and a basic int IDENTITY column with a
clustered index -- the approach is described best in this stackoverflow response here
I'm not sure which solution is best. The first method seems to be the most popular and widely used from what I've read. The second seems to have this benefit from that linked stackoverflow response:
Then there's another issue to consider: the clustering key on a table will be added to each and every entry on each and every non-clustered index on your table as well - thus you really want to make sure it's as small as possible.
Any advice would be appreciated.
sql-server performance index clustered-index uuid
add a comment |
I'm trying to figure out what the optimal solution is in regards to size and performance when using GUIDs as a primary key. The main reason for wanting GUIDs is that it's more appropriate in our micro-service architecture. There seems to be a lot of debate on what's actually best.
The two main solutions appear to be:
Have the primary key as a sequential GUID (generated by the
application, rather than SQL Servers built in NEWSEQUENTIALID() as it isn't truly sequential)
with a clustered index.Have two primary keys, a randomly generated GUID with a
non-clustered index and a basic int IDENTITY column with a
clustered index -- the approach is described best in this stackoverflow response here
I'm not sure which solution is best. The first method seems to be the most popular and widely used from what I've read. The second seems to have this benefit from that linked stackoverflow response:
Then there's another issue to consider: the clustering key on a table will be added to each and every entry on each and every non-clustered index on your table as well - thus you really want to make sure it's as small as possible.
Any advice would be appreciated.
sql-server performance index clustered-index uuid
Having the GUID in the NC indexes can also be a benefit, if the queries that are driven by such an index also refers to the GUID. If you want to cover such a query, then the GUID is already in there.
– Tibor Karaszi
Nov 29 '18 at 11:37
add a comment |
I'm trying to figure out what the optimal solution is in regards to size and performance when using GUIDs as a primary key. The main reason for wanting GUIDs is that it's more appropriate in our micro-service architecture. There seems to be a lot of debate on what's actually best.
The two main solutions appear to be:
Have the primary key as a sequential GUID (generated by the
application, rather than SQL Servers built in NEWSEQUENTIALID() as it isn't truly sequential)
with a clustered index.Have two primary keys, a randomly generated GUID with a
non-clustered index and a basic int IDENTITY column with a
clustered index -- the approach is described best in this stackoverflow response here
I'm not sure which solution is best. The first method seems to be the most popular and widely used from what I've read. The second seems to have this benefit from that linked stackoverflow response:
Then there's another issue to consider: the clustering key on a table will be added to each and every entry on each and every non-clustered index on your table as well - thus you really want to make sure it's as small as possible.
Any advice would be appreciated.
sql-server performance index clustered-index uuid
I'm trying to figure out what the optimal solution is in regards to size and performance when using GUIDs as a primary key. The main reason for wanting GUIDs is that it's more appropriate in our micro-service architecture. There seems to be a lot of debate on what's actually best.
The two main solutions appear to be:
Have the primary key as a sequential GUID (generated by the
application, rather than SQL Servers built in NEWSEQUENTIALID() as it isn't truly sequential)
with a clustered index.Have two primary keys, a randomly generated GUID with a
non-clustered index and a basic int IDENTITY column with a
clustered index -- the approach is described best in this stackoverflow response here
I'm not sure which solution is best. The first method seems to be the most popular and widely used from what I've read. The second seems to have this benefit from that linked stackoverflow response:
Then there's another issue to consider: the clustering key on a table will be added to each and every entry on each and every non-clustered index on your table as well - thus you really want to make sure it's as small as possible.
Any advice would be appreciated.
sql-server performance index clustered-index uuid
sql-server performance index clustered-index uuid
asked Nov 29 '18 at 11:14
Callum BreenCallum Breen
82
82
Having the GUID in the NC indexes can also be a benefit, if the queries that are driven by such an index also refers to the GUID. If you want to cover such a query, then the GUID is already in there.
– Tibor Karaszi
Nov 29 '18 at 11:37
add a comment |
Having the GUID in the NC indexes can also be a benefit, if the queries that are driven by such an index also refers to the GUID. If you want to cover such a query, then the GUID is already in there.
– Tibor Karaszi
Nov 29 '18 at 11:37
Having the GUID in the NC indexes can also be a benefit, if the queries that are driven by such an index also refers to the GUID. If you want to cover such a query, then the GUID is already in there.
– Tibor Karaszi
Nov 29 '18 at 11:37
Having the GUID in the NC indexes can also be a benefit, if the queries that are driven by such an index also refers to the GUID. If you want to cover such a query, then the GUID is already in there.
– Tibor Karaszi
Nov 29 '18 at 11:37
add a comment |
2 Answers
2
active
oldest
votes
If you're going to use a GUID key, simply use a clustered index with NEWSEQENTIALID() and move on with your application. The alternative designs are simply not predictably and significantly better when you consider the extra data structures and complexity they introduce. So while careful testing of your actual application might reveal a superior alternative design, just start with a simple clustered index on a sequentially-generated GUID.
For the gory details on why it doesn't really matter that the GUIDs are generated in the "middle" of the sort order, and the insertion point changes see: Good Page Splits and Sequential GUID Key Generation.
add a comment |
Using an INT
(clustered) and a UNQUEIDENTIFIER
(not) could save significant space if you have a lot of non-clustered indexes on the table, though be careful to make sure this internal value never leaks out into other layers of your application, and make sure it is well documented so others working on the DB know, as your distributed system could accidentally gain a global dependency on what should be a local only value.
There is a third option to consider: do you have anything else in the table that might benefit from being the clustering key? Perhaps a DATE
column that sees a lot of range queries? Range queries on that column would then see a performance increase, you wouldn't be subject to the fragmentation see with arbitrary UUIDs and the date+uniquifier while still larger than an integer at 8 bytes is still smaller than the 16 needed for a UUID. Even if it is a DATETIME
, or DATETIME2
depending on precision, the result is 12 bytes, so if you are using system managed history tables and this is a base table perhaps consider using sysStartTime
.
Even if all you have is an INT
(or smaller) that doesn't vary a lot, that plus the uniquifier will be a smaller key than a UUID and the uniquifier is effectively an INT IDENTITY(1,1)
so it'll behave like your extra INT
colummn without needing to pollute the data model with an extra column existing only for size/performance reasons.
(generated by the application, rather than SQL Servers built in NEWSEQUENTIALID() as it isn't truly sequential)
It is "sequential enough" to vastly reduce any fragmentation concern though, so don't go out of your way re-inventing the wheel if that is your only issue.
1
The key takeaway from the first sentence is that your primary key doesn't have to be clustered and you can have multiple "keys" that are enforced through unique constraints/indexes instead of the primary key syntax. The end result is largely the same.
– Aaron Bertrand♦
Nov 29 '18 at 13:25
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "182"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f223737%2fclustered-sequential-guid-primary-key-vs-non-clustered-guid-and-clustered-sequen%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
If you're going to use a GUID key, simply use a clustered index with NEWSEQENTIALID() and move on with your application. The alternative designs are simply not predictably and significantly better when you consider the extra data structures and complexity they introduce. So while careful testing of your actual application might reveal a superior alternative design, just start with a simple clustered index on a sequentially-generated GUID.
For the gory details on why it doesn't really matter that the GUIDs are generated in the "middle" of the sort order, and the insertion point changes see: Good Page Splits and Sequential GUID Key Generation.
add a comment |
If you're going to use a GUID key, simply use a clustered index with NEWSEQENTIALID() and move on with your application. The alternative designs are simply not predictably and significantly better when you consider the extra data structures and complexity they introduce. So while careful testing of your actual application might reveal a superior alternative design, just start with a simple clustered index on a sequentially-generated GUID.
For the gory details on why it doesn't really matter that the GUIDs are generated in the "middle" of the sort order, and the insertion point changes see: Good Page Splits and Sequential GUID Key Generation.
add a comment |
If you're going to use a GUID key, simply use a clustered index with NEWSEQENTIALID() and move on with your application. The alternative designs are simply not predictably and significantly better when you consider the extra data structures and complexity they introduce. So while careful testing of your actual application might reveal a superior alternative design, just start with a simple clustered index on a sequentially-generated GUID.
For the gory details on why it doesn't really matter that the GUIDs are generated in the "middle" of the sort order, and the insertion point changes see: Good Page Splits and Sequential GUID Key Generation.
If you're going to use a GUID key, simply use a clustered index with NEWSEQENTIALID() and move on with your application. The alternative designs are simply not predictably and significantly better when you consider the extra data structures and complexity they introduce. So while careful testing of your actual application might reveal a superior alternative design, just start with a simple clustered index on a sequentially-generated GUID.
For the gory details on why it doesn't really matter that the GUIDs are generated in the "middle" of the sort order, and the insertion point changes see: Good Page Splits and Sequential GUID Key Generation.
edited Nov 29 '18 at 15:36
answered Nov 29 '18 at 14:04
David Browne - MicrosoftDavid Browne - Microsoft
10.8k727
10.8k727
add a comment |
add a comment |
Using an INT
(clustered) and a UNQUEIDENTIFIER
(not) could save significant space if you have a lot of non-clustered indexes on the table, though be careful to make sure this internal value never leaks out into other layers of your application, and make sure it is well documented so others working on the DB know, as your distributed system could accidentally gain a global dependency on what should be a local only value.
There is a third option to consider: do you have anything else in the table that might benefit from being the clustering key? Perhaps a DATE
column that sees a lot of range queries? Range queries on that column would then see a performance increase, you wouldn't be subject to the fragmentation see with arbitrary UUIDs and the date+uniquifier while still larger than an integer at 8 bytes is still smaller than the 16 needed for a UUID. Even if it is a DATETIME
, or DATETIME2
depending on precision, the result is 12 bytes, so if you are using system managed history tables and this is a base table perhaps consider using sysStartTime
.
Even if all you have is an INT
(or smaller) that doesn't vary a lot, that plus the uniquifier will be a smaller key than a UUID and the uniquifier is effectively an INT IDENTITY(1,1)
so it'll behave like your extra INT
colummn without needing to pollute the data model with an extra column existing only for size/performance reasons.
(generated by the application, rather than SQL Servers built in NEWSEQUENTIALID() as it isn't truly sequential)
It is "sequential enough" to vastly reduce any fragmentation concern though, so don't go out of your way re-inventing the wheel if that is your only issue.
1
The key takeaway from the first sentence is that your primary key doesn't have to be clustered and you can have multiple "keys" that are enforced through unique constraints/indexes instead of the primary key syntax. The end result is largely the same.
– Aaron Bertrand♦
Nov 29 '18 at 13:25
add a comment |
Using an INT
(clustered) and a UNQUEIDENTIFIER
(not) could save significant space if you have a lot of non-clustered indexes on the table, though be careful to make sure this internal value never leaks out into other layers of your application, and make sure it is well documented so others working on the DB know, as your distributed system could accidentally gain a global dependency on what should be a local only value.
There is a third option to consider: do you have anything else in the table that might benefit from being the clustering key? Perhaps a DATE
column that sees a lot of range queries? Range queries on that column would then see a performance increase, you wouldn't be subject to the fragmentation see with arbitrary UUIDs and the date+uniquifier while still larger than an integer at 8 bytes is still smaller than the 16 needed for a UUID. Even if it is a DATETIME
, or DATETIME2
depending on precision, the result is 12 bytes, so if you are using system managed history tables and this is a base table perhaps consider using sysStartTime
.
Even if all you have is an INT
(or smaller) that doesn't vary a lot, that plus the uniquifier will be a smaller key than a UUID and the uniquifier is effectively an INT IDENTITY(1,1)
so it'll behave like your extra INT
colummn without needing to pollute the data model with an extra column existing only for size/performance reasons.
(generated by the application, rather than SQL Servers built in NEWSEQUENTIALID() as it isn't truly sequential)
It is "sequential enough" to vastly reduce any fragmentation concern though, so don't go out of your way re-inventing the wheel if that is your only issue.
1
The key takeaway from the first sentence is that your primary key doesn't have to be clustered and you can have multiple "keys" that are enforced through unique constraints/indexes instead of the primary key syntax. The end result is largely the same.
– Aaron Bertrand♦
Nov 29 '18 at 13:25
add a comment |
Using an INT
(clustered) and a UNQUEIDENTIFIER
(not) could save significant space if you have a lot of non-clustered indexes on the table, though be careful to make sure this internal value never leaks out into other layers of your application, and make sure it is well documented so others working on the DB know, as your distributed system could accidentally gain a global dependency on what should be a local only value.
There is a third option to consider: do you have anything else in the table that might benefit from being the clustering key? Perhaps a DATE
column that sees a lot of range queries? Range queries on that column would then see a performance increase, you wouldn't be subject to the fragmentation see with arbitrary UUIDs and the date+uniquifier while still larger than an integer at 8 bytes is still smaller than the 16 needed for a UUID. Even if it is a DATETIME
, or DATETIME2
depending on precision, the result is 12 bytes, so if you are using system managed history tables and this is a base table perhaps consider using sysStartTime
.
Even if all you have is an INT
(or smaller) that doesn't vary a lot, that plus the uniquifier will be a smaller key than a UUID and the uniquifier is effectively an INT IDENTITY(1,1)
so it'll behave like your extra INT
colummn without needing to pollute the data model with an extra column existing only for size/performance reasons.
(generated by the application, rather than SQL Servers built in NEWSEQUENTIALID() as it isn't truly sequential)
It is "sequential enough" to vastly reduce any fragmentation concern though, so don't go out of your way re-inventing the wheel if that is your only issue.
Using an INT
(clustered) and a UNQUEIDENTIFIER
(not) could save significant space if you have a lot of non-clustered indexes on the table, though be careful to make sure this internal value never leaks out into other layers of your application, and make sure it is well documented so others working on the DB know, as your distributed system could accidentally gain a global dependency on what should be a local only value.
There is a third option to consider: do you have anything else in the table that might benefit from being the clustering key? Perhaps a DATE
column that sees a lot of range queries? Range queries on that column would then see a performance increase, you wouldn't be subject to the fragmentation see with arbitrary UUIDs and the date+uniquifier while still larger than an integer at 8 bytes is still smaller than the 16 needed for a UUID. Even if it is a DATETIME
, or DATETIME2
depending on precision, the result is 12 bytes, so if you are using system managed history tables and this is a base table perhaps consider using sysStartTime
.
Even if all you have is an INT
(or smaller) that doesn't vary a lot, that plus the uniquifier will be a smaller key than a UUID and the uniquifier is effectively an INT IDENTITY(1,1)
so it'll behave like your extra INT
colummn without needing to pollute the data model with an extra column existing only for size/performance reasons.
(generated by the application, rather than SQL Servers built in NEWSEQUENTIALID() as it isn't truly sequential)
It is "sequential enough" to vastly reduce any fragmentation concern though, so don't go out of your way re-inventing the wheel if that is your only issue.
answered Nov 29 '18 at 11:42
David SpillettDavid Spillett
22.1k23267
22.1k23267
1
The key takeaway from the first sentence is that your primary key doesn't have to be clustered and you can have multiple "keys" that are enforced through unique constraints/indexes instead of the primary key syntax. The end result is largely the same.
– Aaron Bertrand♦
Nov 29 '18 at 13:25
add a comment |
1
The key takeaway from the first sentence is that your primary key doesn't have to be clustered and you can have multiple "keys" that are enforced through unique constraints/indexes instead of the primary key syntax. The end result is largely the same.
– Aaron Bertrand♦
Nov 29 '18 at 13:25
1
1
The key takeaway from the first sentence is that your primary key doesn't have to be clustered and you can have multiple "keys" that are enforced through unique constraints/indexes instead of the primary key syntax. The end result is largely the same.
– Aaron Bertrand♦
Nov 29 '18 at 13:25
The key takeaway from the first sentence is that your primary key doesn't have to be clustered and you can have multiple "keys" that are enforced through unique constraints/indexes instead of the primary key syntax. The end result is largely the same.
– Aaron Bertrand♦
Nov 29 '18 at 13:25
add a comment |
Thanks for contributing an answer to Database Administrators Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f223737%2fclustered-sequential-guid-primary-key-vs-non-clustered-guid-and-clustered-sequen%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Having the GUID in the NC indexes can also be a benefit, if the queries that are driven by such an index also refers to the GUID. If you want to cover such a query, then the GUID is already in there.
– Tibor Karaszi
Nov 29 '18 at 11:37