ITPub博客

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

一个有关UUID字段相关类型的有趣案例

原创 数据库开发技术 作者:数据库杂记 时间:2023-06-26 06:05:00 0 删除 编辑


背景

提到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类型,可是它已经在成千上万的应用系统中存在着,你就不能不正视它,还要尽量不要让它太拉垮。

参考:

  1. 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

  2. sql server query running slow from java: https://stackoverflow.com/questions/961078/sql-server-query-running-slow-from-java/32867579#32867579

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

请登录后发表评论 登录
全部评论

注册时间:2019-04-22

  • 博文量
    276
  • 访问量
    160360