Sql 从群集复合密钥移动到顺序整数群集密钥时性能下降

Sql 从群集复合密钥移动到顺序整数群集密钥时性能下降,sql,sql-server,database,database-design,Sql,Sql Server,Database,Database Design,我有一个主键(primkey)的表,由(用户名VARCHAR(50)、年龄INT、日期时间)组成。然后在这一天为用户索引一组数据。一般来说,我会为用户名请求所有数据 如果我错了,请纠正我-这里的集群工作得很好,因为它将首先基于USER\u NAME进行集群,因此将USER\u NAME=JOHN\u SMITH的所有数据放在一起。然后,它将根据年龄等进行聚类。因为我请求特定用户的所有数据,这意味着IO得到了优化,即我读取的页面数量最少,而查询请求的数据量较大,因此IO绑定速度最快 我目前计划用u

我有一个主键(primkey)的表,由(用户名VARCHAR(50)、年龄INT、日期时间)组成。然后在这一天为用户索引一组数据。一般来说,我会为用户名请求所有数据

如果我错了,请纠正我-这里的集群工作得很好,因为它将首先基于USER\u NAME进行集群,因此将USER\u NAME=JOHN\u SMITH的所有数据放在一起。然后,它将根据年龄等进行聚类。因为我请求特定用户的所有数据,这意味着IO得到了优化,即我读取的页面数量最少,而查询请求的数据量较大,因此IO绑定速度最快

我目前计划用uid替换(用户名,年龄),而顺序递增的数字是(用户名,年龄)和uid之间的随机映射。这当然也会将primkey更改为(UID INT,DATE DATETIME),因为UID只是一个数字,例如(JOHN_-SMITH,24)可能是123124,(JOHN_-SMITH,25)可能是352431,就我所见,集群变得毫无意义。我的意思是,虽然旧primkey中的(JOHN_SMITH,24)和(JOHN_SMITH,25)显然是连续两年内同一用户的数据,DB会将数据紧密地聚集在磁盘上,但数字123124和352431不包含任何有关引用数据的信息。也就是说,旧的primkey具有结构,新的primkey没有结构,也没有关于引用数据的隐式信息

一种解决方案是在UID中实现某种寻址方案(例如IPv4样式,但要简单得多)——即每个用户名都有150个UID的保留空间,也就是说,如果JOHN_SMITH的UID为0,JOHN_SMYTH将获得至少150个UID,并且0-149是为(用户名=JOHN_SMITH,年龄=?)组合保留的

我实际上不想采用寻址方案。如果您对此有任何想法(包括我的理论是否正确),我们将不胜感激

  • 我对SELECT有性能限制,不特别关心INSERT和DELETE
  • 用户表非常大(数十GB)
  • 编辑:选择查询的示例(值可能会比列表长很多,而不仅仅是两个元素)

         DECLARE @testtable TABLE 
         (
         uid INT,
         startdate DATETIME,
         enddate DATETIME
         );
         INSERT INTO @testtable
         (
         uid,
         startdate,
         enddate
         )
         VALUES
         (1233890,'01-Jul-2017 00:00:00','15-Jul-2017 23:59:59'),
         (1523420,'01-Jul-2018 00:00:00','15-Jul-2018 23:59:59')
    
         SELECT UID, [DATE], [WAKEUP_TIME] 
         FROM dbo.USERS user 
         INNER JOIN @testtable cont 
         ON user.uid = cont.uid 
         AND user.DATE >= cont.startdate 
         AND user.DATE <= cont.enddate
         WHERE user.USER_NAME = 'John'
         ORDER BY 2 ; 
    
    DECLARE@testtable
    (
    uid INT,
    开始日期时间,
    结束日期日期时间
    );
    插入到@testtable中
    (
    uid,
    开始日期,
    结束日期
    )
    价值观
    (1233890,'2017年7月1日00:00:00','2017年7月15日23:59:59'),
    (1523420,'2018年7月1日00:00:00','2018年7月15日23:59:59')
    选择UID、[日期]、[唤醒时间]
    来自dbo.USERS
    内部联接@testtable cont
    ON user.uid=cont.uid
    和user.DATE>=cont.startdate
    
    和user.DATE首先,您猜测的
    select
    查询的损失是
    insert
    s和
    delete
    s的收益。新记录只需添加到表的“末尾”,不需要拆分页面

    第二,如果可以,您可能想尝试新的结构。例如,如果整个表都能放入内存,那么从多个页面读取数据与从单个页面读取数据不会有太大的区别


    最后,SQL Server不要求将主键用于群集。您只有一个群集键。但您可以引入一个新的唯一id,使其成为主键,并仍然按其他列进行群集。

    您似乎在按
    用户名、年龄
    日期
    的范围进行平等筛选。如果替换
    >用户名、年龄
    通过一个新的人工值
    uid,则基于相等过滤的索引搜索仍然有效

    从您发布的查询中可以看出,SQL Server可能会通过反复探测
    用户来执行它。对于
    @testtable
    中的每个项目,都会执行一次。这是作为嵌套循环联接完成的

    这是相同的索引使用模式和查询计划形状。但是您是正确的,
    AGE
    的不同值现在将基本上随机地分布在索引中,而之前相同用户的所有
    AGE
    值都是共位的

    这肯定会导致更多的磁盘寻道,从而导致性能损失。您指出,大多数表不会缓存在RAM中。因此,索引中必须访问的点数对性能非常重要(正如您正确识别的)

    当然,最简单的解决方案是不采用新的
    uid
    列。但我认为您有理由这样做

    您可以通过将
    AGE
    值冗余地打包到最后一个字节(例如,
    db\u uid=sequential\u id\u for_user\u name*256+AGE
    )来实现一个简单的“寻址方案”。您需要注意不要溢出

    这将物理地将相关的
    年龄
    值组合在一起,很可能导致加速


    也考虑使用<代码> BigInt/Cude>有更多的空间来对数据进行编码。

    谢谢-(1)我选择了性能约束,不特别关心插入和删除。(2)表很大(几十个GBS)。(3)我知道,我也使用了一个聚集索引。不管查询关键字是不是PRIMKY(例如索引)。,您对上述性能僵局有何看法?哪些重要的select查询应该保持快速?@usr请参阅原始问题中的修正案