首页 > 数据库 > 数据库开发技术 > 一个有关UUID字段相关类型的有趣案例

提到GUID/UUID类型,确实有很多故事可讲。各大DBMS对这个类型都是各显神通。毕竟,从简单易用性角度来看,它的全局唯一性确实有很多方便之处,特别有点类似于傻瓜式使用风格,只管往库里头扔,反正它是“唯一”的。
方便归方便,在另一方面,它也存在着一些弊端,在我前边的一篇短文也提到了相关内容:PostgreSQL中的UUID的弊端及解决方案。这次我要提的是一个在SQL Server里头出现的案例。
有一个不大不小的Customer,他们的使用场景里头本来是想用SQLServer的,结果发现用了一种框架系统之后,UUID类型做主键的表的CUD性能实在是差,原话是那么说的,大概1800万条记录的表,更新一条记录需要花1秒钟。(同时发现,delete也是这个鬼样子,insert相对不那么明显)。
但是呢,换做PostgreSQL或者Sybase ASE,CUD尤其是Update/Delete,性能指标则要好的多。都是在同一台物理机上进行的相关测试。各个数据库使用的都是相对比较新的稳定版本。
至于JDBC Driver,都使用的是原厂提供的,也没加什么特殊参数。大致如下:
static String getDriverClassName(String type)
{
if (type.equals("ase")) return "com.sybase.jdbc4.jdbc.SybDriver";
if (type.equals("mssql")) return "com.microsoft.sqlserver.jdbc.SQLServerDriver";
if (type.equals("pgsql")) return "org.postgresql.Driver";
throw new RuntimeException("Unsupported db-type: " + type);
}
static String getDatabaseURL(String type, String host, String port, String database, String username, String password)
{
if (type.equals("ase")) return "jdbc:sybase:Tds:" + host + ":" + port + "/" + database + "?user=" + username + "&password=" + password;
if (type.equals("mssql")) return "jdbc:sqlserver://" + host + ":" + port + ";databaseName=" + database + ";user=" + username + ";password=" + password + ";responseBuffering=full;statementPoolingCacheSize=1000";
if (type.equals("pgsql")) return "jdbc:postgresql://" + host + ":" + port + "/" + database + "?user=" + username + "&password=" + password;
throw new RuntimeException("Unsupported db-type: " + type);
}
注意,这里头PG还没有加上&reWriteBatchedInserts=true,加上这个,使用batch,速度则更快。
说到这里,当时,也是使用了各种方式去试探:
1)、会不会是因为SQL Server默认主键使用的是clustered index,造成CUD比较分散乱序,性能下降呢,于是:
-- 简单的伪表如下:CustomerID为使用varchar(36)来存储UUID.
-- 也可以用:[uniqueidentifier] 类型,效果一样
CREATE TABLE [dbo].[Customers](
[CustomerID] [varchar](36) NOT NULL,
[City] [varchar](400) NULL,
[Country] [varchar](40) NULL,
[CountryISO] [varchar](40) NULL,
[Email] [varchar](400) NULL,
[Honorific] [varchar](80) NULL,
[Name] [varchar](400) NOT NULL,
[POBox] [varchar](80) NULL,
[Phone] [varchar](200) NULL,
[PostCode] [varchar](80) NULL,
[Region] [varchar](400) NULL,
[Street] [varchar](400) NULL,
[row_version] [bigint] NOT NULL,
[is_deleted] [tinyint] NOT NULL,
[last_modified] [datetimeoffset](7) NOT NULL,
PRIMARY KEY CLUSTERED
(
[CustomerID] ASC
)
把这里的PRIMARY KEY CLUSTERED 替换为:PRIMARY KEY NONCLUSTERED。结果发现,改进的程度不太大。甚至网上也有相关讨论:https://dba.stackexchange.com/questions/298324/performance-concerns-with-uniqueidentifiers-guids
2)、建议Customer直接弃用UUID的值的形式,采用identity。这下他们不乐意了,因为他们的系统一直采用的是UUID做主键。不想改。(所以,也想说一句,这也是UUID类型长久存在的一个主要原因之一)。
仔细想一下,MS不可能犯这么愚蠢的错误,或者在它的文档里头提的少?
就在他们想完全弃用SQL Server,转向PostgreSQL的那段时间,我们独领偶然发现SQLServer针对他们自身的UUID类型或存储形式,是做了一些特殊处理的。
需要在JDBC URL中添加一个特殊的属性:sendStringParametersAsUnicode=false
最后终于让CUD的性能跟PG、Sybase ASE达到差不多同等的水平。而customer也愿意在这几种数据库当中来回切换。
有的时候,我们可能需要有一些怀疑精神,即便在测出的结果相差很多的时候,也要审慎的进行分析,不要轻易下结论。虽然我不喜欢UUID类型,可是它已经在成千上万的应用系统中存在着,你就不能不正视它,还要尽量不要让它太拉垮。
https://dba.stackexchange.com/questions/231109/query-is-slow-with-jdbc-parameters-fast-with-concatenated-sql: https://dba.stackexchange.com/questions/231109/query-is-slow-with-jdbc-parameters-fast-with-concatenated-sql
sql server query running slow from java: https://stackoverflow.com/questions/961078/sql-server-query-running-slow-from-java/32867579#32867579

原文作者:数据库杂记;如有侵权,请联系管理员删除。