当前位置: 代码迷 >> Java Web开发 >> 分布式系统,数据库主键有关问题
  详细解决方案

分布式系统,数据库主键有关问题

热度:42   发布时间:2016-04-16 22:16:48.0
分布式系统,数据库主键问题
怎样保持数据库主键不重复并且性能够好
------解决方案--------------------
本帖最后由 defonds 于 2014-06-17 16:43:06 编辑
调整使用聚集索引,不一定非得把主键给 id,因为有些数据库默认聚集索引就是主键。释放出来给其他字段,有时候会有意想不到的性能大提升!
举个例子。
以 MySql 为例,InnoDB选取聚集索引参照列的顺序是
                1. 如果声声明了主键(primary key),则这个列会被做为聚集索引;
                2. 如果没有声明主键,则会用一个唯一且不为空的索引列做为主键,成为此表的聚集索引;
                3. 上面二个条件都不满足,InnoDB会自己产生一个虚拟的聚集索引。
CREATE TABLE `timeline_raw` (
  `rawId` bigint(20) NOT NULL AUTO_INCREMENT,
  `uid` bigint(20) DEFAULT NULL,
  `did` bigint(20) DEFAULT NULL,
  `channelId` char(1) NOT NULL DEFAULT '1' COMMENT '1:qvga; 2:720p',
  `fileId` bigint(20) DEFAULT NULL,
  `sectionId` bigint(20) DEFAULT NULL,
  `headerFilePath` varchar(120) DEFAULT NULL,
  `startTime` bigint(20) DEFAULT NULL,
  `endTime` bigint(20) DEFAULT NULL,
  `updateTime` datetime DEFAULT NULL,
  `createTime` datetime DEFAULT NULL,
  PRIMARY KEY (`rawId`),
  KEY `index_uid_did_startTime` (`uid`,`did`,`startTime`) USING BTREE,
  KEY `index_uid_did_endTime` (`uid`,`did`,`endTime`) USING BTREE,
  KEY `index_time` (`startTime`) USING BTREE,
  KEY `index_uid_did_fileId` (`uid`,`did`,`sectionId`) USING BTREE,
  KEY `index_sectionId` (`sectionId`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8


这个表有四个索引:主键 rawId、sectionId、`uid`,`did`、startTime。
        项目的 iBatis2 中有这样一条查询语句:
	<select id="getRawFileList" parameterClass="java.util.HashMap" resultClass="com.defonds.mysql.raw.entity.TimelineRaw">
SELECT * FROM timeline_raw_
WHERE uid=#uid# 
AND did=#did# 
AND channelId=#channelId#
<isNotNull  property="sectionId"> AND sectionId = #sectionId#</isNotNull>
AND 
(
(startTime BETWEEN #startTime# and #endTime#) 
OR 
(endTime BETWEEN #startTime# and #endTime#)
OR
 (
 <![CDATA[ 
 startTime<=#startTime# 
  ]]> 
 AND 
 <![CDATA[ 
 endTime>=#endTime#
 ]]> 
 )
)
ORDER BY startTime;
</select>


        根据实际业务向 timeline_raw 表注入一千万条数据,进行模拟测试(参考《sql 性能测试例子》),发现 getRawFileList 的执行平均时间为 160 ms 以上。这是不能接受的。
        考虑到实际业务中对于主键 rawId 查询条件甚少,我们把rawId主键索引取消掉,改为唯一约束,却把sectionId+startTime+endTime作为主键(业务上能够保证其唯一性,根据InnoDB索引规则,这个索引将成为我们新表的聚集索引)。然后把sectionId、startTime两个索引也取消掉,仅保留`uid`,`did`索引。
        这样子,我们新表的索引实际上只有两个了:一个聚集索引(sectionId+startTime+endTime)一个非聚集索引(`uid`,`did`)。
再次进行模拟测试,同样的数据、数据量,同样的查询结果集,getRawFileList 执行平均时间已经降到了 11 ms。结果是令人振奋的,不是么?
------解决方案--------------------
引用:
Quote: 引用:

你是什么系统分布式,是数据库分布式还是应用层分布式?
如果是数据库分布式怎么处理,如果是应用层分布式怎么处理?

应用层分布式除非主键是你自己生成的,否则不需要考虑主键重复问题。如果主键是你自己生成的,在数据库里加一个逻辑主键把主键生成的问题丢给数据库。如果一定要生成不重复的键,可以在新增节点的时候在节点上加入一个类似id的部署数据,生成主键的时候把这个id带上就可以保证不同的节点之间不会产生重复的数据。

如果是数据库分布式,就要看数据库的部署策略。不过不管是什么策略,数据都很好的处理了主键问题,不需要你来关心。
Master-Master策略,这种情况下写操作是要两个节点确认的,所以不存在重复问题
Master-Slave策略,这种情况下是单一节点写,也没有重复问题
按照业务切分的分库策略,这种情况下库是分开的,所以也没有重复问题
  相关解决方案