- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我要解决的问题是:
1 - 在我们的数据库中,我们拥有所有表(也包括具有数百万条记录的表),其 PK id 列声明为 VARCHAR(36)。 上面还有一个聚集索引当然,正如我在网上看到的那样,这对性能来说是一件很糟糕的事情,还因为数据库有很多读取、插入、更新和删除操作。
2 - 我们将 Hibernate 用于我们的 java web 应用程序作为此数据库的 ORM
在网上大量阅读后,我开始将这些列的数据类型更改为 UNIQUEIDENTIFIER,并使用默认选项 newsequentialid(),因为此选项应该可以缓解我们索引的碎片问题.
我注意到碎片问题仍然存在,重建后表变得严重碎片化(我们每晚都重建完整索引)。
然后我看到我们所有的 id 列的 Hibernate 映射都包含以下内容:
<id name="id" column="id" type="string">
<generator class="guid"/>
</id>
当我们的系统中发生插入时,日志显示插入是在调用 select newid()
之后完成的,因此由于这会返回一个随机 guid,因此插入将被放置在索引,从而导致碎片化(这完全破坏了我所做的列数据类型更改)。
所以在另一次在线搜索之后,我尝试自己在 Hibernate 中实现一个 guid 生成器,实现接口(interface) IdentifierGenerator
并使用基于时间的生成器和 JUG ( http://wiki.fasterxml.com/JugHome )。
生成(我认为是顺序的)id 的代码是这样的:
String uuid = null;
EthernetAddress nic = EthernetAddress.fromInterface();
TimeBasedGenerator uuidGenerator = Generators.timeBasedGenerator(nic);
uuid = uuidGenerator.generate().toString();
我相应地改变了映射到这个:
<id name="id" column="id" type="string">
<generator class="my_package.hibernate.CustomSequentialGuidGenerator">
</generator>
</id>
然后我尝试生成一些测试 uuid 来测试它们的顺序(以 uniqueidentifier 方式顺序,所以二进制),这是一个短列表(每个元素都在连续之前生成):
314a9a1b-6295-11e5-8d2c-2c27d7e1614f
3d867801-6295-11e5-ae09-2c27d7e1614f
4434ac7d-6295-11e5-9ed1-2c27d7e1614f
491462c4-6295-11e5-af81-2c27d7e1614f
5389ff4c-6295-11e5-84cf-2c27d7e1614f
57098959-6295-11e5-b203-2c27d7e1614f
5b62d144-6295-11e5-9883-2c27d7e1614f
这在我看来是按字母顺序排列的,而不是二进制顺序的。
上面的测试执行了七次测试应用程序,它不是一个循环。
我试图将这些值插入声明为唯一标识符的列中,并在对该列发出选择后,这是 sql server 输出列表:
5389FF4C-6295-11E5-84CF-2C27D7E1614F
314A9A1B-6295-11E5-8D2C-2C27D7E1614F
5B62D144-6295-11E5-9883-2C27D7E1614F
4434AC7D-6295-11E5-9ED1-2C27D7E1614F
3D867801-6295-11E5-AE09-2C27D7E1614F
491462C4-6295-11E5-AF81-2C27D7E1614F
57098959-6295-11E5-B203-2C27D7E1614F
所以我真的不明白我应该做什么以及我是否可以使用 JUG 作为顺序 guid 生成器来避免我的碎片问题。
这是另一个 JUG 测试,我尝试运行 3 次,每次生成 10 个带循环的 guid:
运行 1
54bd156e-62a2-11e5-a1a7-2c27d7e1614f
54c3cc2f-62a2-11e5-a1a7-2c27d7e1614f
54caf820-62a2-11e5-a1a7-2c27d7e1614f
54d1aee1-62a2-11e5-a1a7-2c27d7e1614f
54d901e2-62a2-11e5-a1a7-2c27d7e1614f
54df9193-62a2-11e5-a1a7-2c27d7e1614f
54e64854-62a2-11e5-a1a7-2c27d7e1614f
54ecff15-62a2-11e5-a1a7-2c27d7e1614f
54f3b5d6-62a2-11e5-a1a7-2c27d7e1614f
54fa4587-62a2-11e5-a1a7-2c27d7e1614f
运行 2
87c66bcc-62a2-11e5-8e7c-2c27d7e1614f
87ccd46d-62a2-11e5-8e7c-2c27d7e1614f
87d3641e-62a2-11e5-8e7c-2c27d7e1614f
87d97e9f-62a2-11e5-8e7c-2c27d7e1614f
87e05c70-62a2-11e5-8e7c-2c27d7e1614f
87e6ec21-62a2-11e5-8e7c-2c27d7e1614f
87ed7bd2-62a2-11e5-8e7c-2c27d7e1614f
87f40b83-62a2-11e5-8e7c-2c27d7e1614f
87fac244-62a2-11e5-8e7c-2c27d7e1614f
880103d5-62a2-11e5-8e7c-2c27d7e1614f
运行 3
a4b690db-62a2-11e5-b667-2c27d7e1614f
a4bcd26c-62a2-11e5-b667-2c27d7e1614f
a4c2eced-62a2-11e5-b667-2c27d7e1614f
a4c92e7e-62a2-11e5-b667-2c27d7e1614f
a4cf48ff-62a2-11e5-b667-2c27d7e1614f
a4d5d8b0-62a2-11e5-b667-2c27d7e1614f
a4dc6861-62a2-11e5-b667-2c27d7e1614f
a4e34632-62a2-11e5-b667-2c27d7e1614f
a4e9d5e3-62a2-11e5-b667-2c27d7e1614f
a4f101d4-62a2-11e5-b667-2c27d7e1614f
运行 4
c2b872b2-62a2-11e5-b855-2c27d7e1614f
c2c17363-62a2-11e5-b855-2c27d7e1614f
c2c82a24-62a2-11e5-b855-2c27d7e1614f
c2ce92c5-62a2-11e5-b855-2c27d7e1614f
c2d57096-62a2-11e5-b855-2c27d7e1614f
c2dc2757-62a2-11e5-b855-2c27d7e1614f
c2e32c38-62a2-11e5-b855-2c27d7e1614f
c2e9bbe9-62a2-11e5-b855-2c27d7e1614f
c2f099ba-62a2-11e5-b855-2c27d7e1614f
c2f7507b-62a2-11e5-b855-2c27d7e1614f
运行 5
f0263d1b-62a2-11e5-8529-2c27d7e1614f
f02d1aec-62a2-11e5-8529-2c27d7e1614f
f033d1ad-62a2-11e5-8529-2c27d7e1614f
f03a615e-62a2-11e5-8529-2c27d7e1614f
f041181f-62a2-11e5-8529-2c27d7e1614f
f047a7d0-62a2-11e5-8529-2c27d7e1614f
f04dc251-62a2-11e5-8529-2c27d7e1614f
f05403e2-62a2-11e5-8529-2c27d7e1614f
f05a6c83-62a2-11e5-8529-2c27d7e1614f
f0608704-62a2-11e5-8529-2c27d7e1614f
运行 6(再次从 0 开始)
00fd4ec3-62a3-11e5-8ab8-2c27d7e1614f
01042c94-62a3-11e5-8ab8-2c27d7e1614f
010b3175-62a3-11e5-8ab8-2c27d7e1614f
0111e836-62a3-11e5-8ab8-2c27d7e1614f
0118ed17-62a3-11e5-8ab8-2c27d7e1614f
011fcae8-62a3-11e5-8ab8-2c27d7e1614f
0126a8b9-62a3-11e5-8ab8-2c27d7e1614f
012d115a-62a3-11e5-8ab8-2c27d7e1614f
0133c81b-62a3-11e5-8ab8-2c27d7e1614f
013a30bc-62a3-11e5-8ab8-2c27d7e1614f
单个组是按字母顺序(但不是二进制)排序的,并且将不同的运行作为一个整体,它们不是按事件字母顺序排序的(叹息)。
我错过了什么?
*************************** 编辑 - 我的实现说明 ********* *********
在收到各种评论和回答后,我实现了以下策略:
我生成了自己的顺序(基于当前时间戳)guid,这是生成器类:
package it.hibernate;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.lang.RandomStringUtils;
import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.id.IdentifierGenerator;
public class CustomSequentialGuidGenerator implements IdentifierGenerator{
@Override
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException
{
String uuid = null;
try {
Date data = new Date();
SimpleDateFormat sdf = new SimpleDateFormat();
String rand = RandomStringUtils.randomAlphanumeric(12);
sdf.applyPattern("yyyy");
String year = sdf.format(data);
sdf.applyPattern("MM");
String month = sdf.format(data);
sdf.applyPattern("dd");
String day = sdf.format(data);
sdf.applyPattern("HH");
String hour = sdf.format(data);
sdf.applyPattern("mm");
String mins = sdf.format(data);
sdf.applyPattern("ss");
String secs = sdf.format(data);
sdf.applyPattern("SSS");
String millis = sdf.format(data);
//G carachter is used to insert the rows after
uuid = "GG" + year + month + "-" + day + hour + "-" + mins + secs + "-" + "0" + millis + "-" + rand;
}
catch (Exception exception)
{
exception.printStackTrace();
}
return uuid;
}
}
您可以注意到所有行都以字符串 'GG'
开头,因为我必须确保在通过 select newid() 生成的所有旧行之后插入所有新行
。之后是当前时间戳和 12 个随机字符,以避免在同一毫秒内插入多行时发生冲突。
经过 2000 次测试后,主键索引碎片从 17,92% 下降到 0,15%。
注意我重新引入的数据类型显然又是 varchar(36) 而不是 uniqueidentifier,因此行按字母顺序排序。
最佳答案
newsequentialid()
的默认选项当然不起作用,因为 hibernate 不使用默认值,它总是设置一个由其生成器发出的值。
通过快速查看 JUG 库,它似乎没有提供任何顺序生成 GUID 的方法。我不知道您为什么认为通过 Generators.timeBasedGenerator()
获得的生成器的 generate()
方法会给您连续的 GUID。基于时间的生成器只是一个在生成 GUID 时考虑当前时间的生成器,但是在将当前时间坐标嵌入到 GUID 中时,它可以以任何它认为合适的方式自由地破坏当前时间坐标,所以它不保证生成的 GUID 会有任何顺序。
通常,术语“GUID”和“顺序”彼此不兼容。您可以拥有 GUID 键或顺序键,但在正常情况下,您不能同时拥有这两种键。
那么,您确定 key 必须是 GUID 吗?就个人而言,我发现 GUID 很难使用。
但是如果您必须进行任何必要的修改以获得连续的 GUID,那么我的建议是编写您自己的函数来生成 36 个字符的字符串,如下所示GUID,但是是连续的。
顺序部分应该来自 SEQUENCE
,它只是发出顺序整数。 (我相信 MS-SQL-Server 支持它们。)
您可以阅读 IETF's UUID specification关于如何正确构建 GUID,但您不必一字不差地遵循它。在大多数情况下,如果它只是看起来像一个 GUID,就足够了。
如果您可以为此使用一个全局序列,那就太好了。如果您不能拥有一个单一的全局序列,那么您需要以某种方式识别您的序列,然后在生成您的 GUID 时考虑每个序列的标识符。 (那将是 IETF 文档中提到的“节点 ID”。)
我曾经有一个不合理的要求,即我要传输到某个 Web 服务的行必须由 GUID 标识,并且有太多的繁文缛节阻止我联系他们问他们“你他妈的吗? ' 严肃的?”所以我只是传输如下 GUID:
|--- random part -----| |-- key ---|
314a9a1b-6295-11e5-8d2c-000000000001
314a9a1b-6295-11e5-8d2c-000000000002
314a9a1b-6295-11e5-8d2c-000000000003
314a9a1b-6295-11e5-8d2c-000000000004
314a9a1b-6295-11e5-8d2c-000000000005
...
他们什么也没说。
关于java - 使用 SQL Server uniqueidentifier 在 Java 中生成顺序 GUID,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32758726/
假设您有一个提供一些在线类(class)的付费网站。并且您要确保一个人不只是购买访问权限,然后将用户名和密码提供给他的所有 friend ,这样他们就可以免费参加类(class)。 你会怎么做? 到目
我有多个数据库,每个数据库都有一个审计表。其中一个数据库是一个“通用”数据库,其中包含国家、州等表格。 这个公共(public)数据库的表在其他数据库中作为 View 被引用。 目前我的审计表有一个标
我正在编写一个 Entity Framework LINQ 查询,其中我想将字符串解析为 UNIQUEIDENTIFIER(又名 GUID),作为 WHERE 子句的一部分: public IEnum
我刚开始使用 SQL Server,但在向具有 uniqueidentifier 类型列的表添加值时遇到了一些麻烦。 表格包括: ID (uniqueidentifier), CODE (nchar(
我有多个数据库,每个数据库都有一个审计表。其中一个数据库是一个“通用”数据库,其中包含国家、州等表格。 这个公共(public)数据库的表在其他数据库中作为 View 被引用。 目前我的审计表有一个标
我有两个表Backup 和Requests。 下面是两个表的脚本 备份 CREATE TABLE UserBackup( FileName varchar(70) NOT NUL
当我运行我的最终查询时,我收到了上述错误。我已经正确设置了我的表格格式,如果我运行我的 @ProdTotal单独查询我得到正确的数据,如果我运行最终 @Sales_Prod查询不带 pt.Produc
为了调查 Hibernate 的行为是否不同于 NHibernate 的 for a particular usage scenario , 我开始为基于 NHibernate 的应用程序的相关部分编
我想根据他们在我网站上的电子邮件 ID 来注册用户,假设用户和他们的 emailid 字符串之间存在一对一的关系。但是gmail doesn't recongnize . s in email ids
在我的数据库 SQL Express 2014 中, 我有名为 Membership 的表,其中列名为 UserId。列类型为 uniqueidentifier。 我想将 UserId 列的内容转换为
我有一个动态查询,我想在其中连接 uniqueidentifier,但 + 和 & 运算符不支持此操作,有没有办法可以将 uniqueidentifier 连接到动态字符串。在这方面的任何示例或任何帮
我正在尝试比较 WHERE 子句中的列 col1 和变量 @myvar。两者通常都包含 GUID,但也可能包含 NULL 值。我认为我可以通过使用 WHERE ISNULL(col1, '')=ISN
我创建了一个数据库,其中使用 uniqueidentifier 作为主键。我正在尝试运行一个最终看起来像这样的参数化查询。 (insert into someTable (uniqueID) Valu
我在 Web 和移动平台(iOS 和 Android)上有一个应用程序,我试图从用户登录的设备中识别出来,并出于安全目的保留它们的列表。是否有任何设备的通用 ID,例如用于计算机的 IMEI(手机),
在我的 C# 项目中,我需要向 UniqueIdentifier 列插入一些空值。 对于字段 city Guid,state Guid 字段我应该能够插入空值如果没有可用的值(唯一标识符)。我收到以下
这个问题在这里已经有了答案: UIDevice uniqueIdentifier deprecated - What to do now? (32 个回答) 已关闭10 年前。
我经常被告知要忽略这个编译器警告,因为在发布下一个 iOS 主要版本之前它不会成为问题。好吧……如果我的 iPhone 应用程序没有有一天突然停止工作就好了! 奇怪的是我在 OpenGL 2.0 上使
关闭。这个问题需要details or clarity .它目前不接受答案。 想改进这个问题吗? 通过 editing this post 添加细节并澄清问题. 关闭 9 年前。 Improve
我正在尝试使用以下代码将 uniqueidentifier 参数传递给存储过程: myCommand.Parameters.Add("@BlogID", SqlDbType.UniqueIdentif
这个问题在这里已经有了答案: How to check if a string is a uniqueidentifier? (15 个答案) 关闭 6 年前。 我有一个 SQL Server 表,
我是一名优秀的程序员,十分优秀!