关于主键管理

提到主键管理,我能联想到几点:
1. 使用数据库自增主键很方便,但不推荐使用,移植性和灵活性大大降底,比如只能在插入记录后获取主键值,
相对而言,很多人更推荐自管理主键的方式,下面就忽略自增主键的讨论吧

2. 使用 UUID 作为主键非常方便,JAVA简单的 API 调用就可以生成 UUID(也可以考虑使用第 3 方类库生成 UUID),并且全球唯一,在集群环境中或其它复杂环境中使用都很方便,但经过查阅相关资料,发现一些劣势,一是生成 UUID 的效率不高,并发量大的情况下很明显,二是用 UUID 作为表的主键,等数据量大了之后,会严重影响 SELECT 和 INSERT 的效率。
虽然没尝试过用 UUID 作主键,但看了些文章后就被吓到了(我基本还是用关系型数据库的)

3. 一个常用的主键管理方案是,用一张表来维护所有的主键/值,在应用层生成下个 ID 值,经常见到这种代码:getNextSequence(key)。为了避免与数据库的频繁交互,一般加个缓存,一次取多个 sequence,偶然情况下会有少许的浪费。
针对集群环境,了解一点,有一种办法是每个结点生成 sequence 时使步进(step)错位,这样可以避免冲突。

4. 我自己寻思的一种方法,不使用数据库的自增主键特性,在应用启动的时刻,初始化/收集所有需要主键管理的表的最大主键值(maxId)并保存到内存中(针对 int/long 类型),然后使用 AtomicInteger 或 AtomicLong 实现递增,除了初始化阶段外,不需要为了主键管理而去和数据库打交道,而且 AtomicInteger 的并发效率更高吧?这种方式实战不多,有没有朋友提点建议?

5. 我发现有些应用,生成 ID 的时机非常早。比如新增一条记录,在进入到新增界面时,ID 就生成了,如果你未提交并且关闭浏览器窗口,会提示什么“数据会丢失”、“是否确认关闭窗口”等信息。这里我有几个疑问,希望了解的朋友给讲讲。
a). ID 这么早生成有什么特殊的应用场景吗?为要这么急?
b). 进入新建页面->不提交->关闭窗口,这样操作就要浪费一个 ID,这个影响不严重吗?恶意用户频率过高的浪费ID呢?或者干脆写个 HTTP 交互的程序暴ID呢?是我想得太多了,还是想歪了?请高手讲解如何正确使用这种方式。

说的不对的,谢谢指正,如果大家有好的主键管理方案,希望能让我学学。。


[该贴被edison87915于2014-09-17 08:54修改过]

哥们。。。。木有打理我的,是不是太小儿科了。。

下面这些都是我猜的,你看有没有一点启发。
不是小儿科是非常复杂。这个要具体业务场景来分析。
这几种情况要分不同的应用的。不是哪个好,哪个不好的。
第一种,自增长的,要在数据库完全自控的情况下,是可以使用的。像一些企业内部应用。
第二种,UUID,在一些操作频率不高,但又在不同分公司,最后还要集中到总公司的情况下,
还是有些用的。
第三种,也不很好,压力全集中到了这张表了。得需要至少两个服务器保证不会一起挂了。
第四种,一台主机,有点压力。可不可以把第三种和它结合起来呢,你可以看下推特的,好像
就是这两种结合在一起的。我猜的,不要相信我。
第五种,生成的id就类似uuid,不会浪费的。
至于为什么 那么急,可能为了便于页面管理吧。给每个页面生成个id,正好顺便就做该记录的id

总结得太全面了,,,好像没有什么补充的。

首先赞成你的观点。关于这个问题,我还思考了两天,也烦了两天,最后还是用了你的第3点。发现自己管理主键的好处就是对象进一步与数据库撇开了关系。

谢谢各位回答,
回复xianghx :看来光理论不行,多用用才行,哈哈,感谢。

回复banq : 我猜是板桥大哥一直搞 NOSQL。。,关系数据库懒的过问了。哈哈。。。感谢

回复 zjsong2012 : 我目前用的第4种,感觉方便些,不需要:
1.建表
2.生成序列还要和数据库打交道

简单的、大部分的情况下,只需要配置:序列key-Table,如:


<!--=====================================================================
主键管理
======================================================================-->

<bean class="xxxxxx.common.sequence.db.SequenceInitializer" init-method="init">
<property name=
"dataSource" ref="dataSource" />
<property name=
"mappings">
<list>
<map><entry key=
"key" value="attach.image" /><entry key="table" value="t_image" /></map>
<map><entry key=
"key" value="bizcard" /><entry key="table" value="t_bizcard" /></map>
</list>
</property>
</bean>

使用的话也很简单,完全 AtomicInteger 或 AtomicLong 实现递增,不需要去跟DB要,也不需要缓存:


int id = Sequences.next('xxxKey');

我感觉这种方式方便

关键看系统是否是集群应用或分布式应用,
其次再考虑性能问题

同意,集群和性能是我下一步要关注的,目前就图省事吧,能省一步是一步,每次看 JDON 上集群、性能相关的文章,都表示很迷糊,哈哈,需要实践,多谢回复,呵呵