自增id一般情况下有两种方案,一个是使用数据库自增功能,另外一种就是oracle这样的sequance机制。
个人觉得,无论你的系统是否考虑日后分布式扩展,建议统一采用sequance方式获取。这样对系统日后可能产生的数据库移植也是很好的支持。
比如说系统需要获得一个表的 新id,可以这样统一封装一个方法:seuHelper.getTableSeq(tableName);
public long getTableSeq(String tableName) { synchronized(tableName)//重要,确保每个表一个锁 { //自己实现sequance机制 } }
对于oracle有自己的sequance机制,实现起来就很容易了。但是对于mysql来说,它只有自增方式。如果是做大型集群、分库分表又如何实现一个统一的sequance机制呢?
经过网上查找,发现flicker采用了一种非常巧妙的既简单又可行的实现方案:
1、建一个存放对应表的sequance表
CREATE TABLE `order_seq` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`stub` char(1) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `stub` (`stub`)
) ENGINE=InnoDB AUTO_INCREMENT=102951 DEFAULT CHARSET=utf8;
这里有几个关键点:
每个表,一个sequance表,确保性能
UNIQUE KEY,确保每个表,只有一条记录,避免数据无限增大,提高性能
2、自增和获取最新id
REPLACE INTO pro_seq (stub) VALUES ('a')
SELECT LAST_INSERT_ID()
这里需要注意的是,这两个操作,需要放在一个连接里面执行,否则SELECT LAST_INSERT_ID()可能查询不到正确结果。另外还有一个重要原因,SELECT LAST_INSERT_ID()只会查询到当前连接最新的自增id,
也就是说,其他连接更新其他seq表,互不干扰。正是这种基础,保证了我们能建立多个seq表实现seq服务。
测试:
为了测试多个表,多并发下,是否正常互不干扰产生自增id,并且不重复,我们再建立几个表
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
`oid` bigint(20) NOT NULL,
PRIMARY KEY (`oid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入新id,检测是否有主键重复
CREATE TABLE `pro_seq` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`stub` char(1) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `stub` (`stub`)
) ENGINE=InnoDB AUTO_INCREMENT=22207 DEFAULT CHARSET=utf8;
商品表seq
CREATE TABLE `pros` (
`pid` bigint(20) NOT NULL,
PRIMARY KEY (`pid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入商品id,检测是否重复主键
数据库操作代码
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void addOrder(long id) throws DataAccessException { String sql = "insert into orders(oid) values(?)"; jdbcTemplate.update( sql, new Object[] { id }); } @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public synchronized long getOrdersSeq() throws DataAccessException { jdbcTemplate.update("REPLACE INTO order_seq (stub) VALUES ('a')"); Long id = jdbcTemplate.queryForObject("SELECT LAST_INSERT_ID()", Long.class); return id.longValue(); } @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public synchronized long getProSeq() throws DataAccessException { jdbcTemplate.update("REPLACE INTO pro_seq (stub) VALUES ('a')"); Long id = jdbcTemplate.queryForObject("SELECT LAST_INSERT_ID()", Long.class); return id.longValue(); } @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void addPro(long id) throws DataAccessException { String sql = "insert into pros(pid) values(?)"; jdbcTemplate.update( sql, new Object[] { id }); }
并发测试代码
final SeqHelper seqHelper = new FileSystemXmlApplicationContext(Utils.getRootPath()+"/conf/app-context.xml").getBean(SeqHelper.class); new Thread(new Runnable() { @Override public void run() { for (int i=0; i<10; i++) { for (int j=0; j<2000; j++) { new Thread(new Runnable() { @Override public void run() { long id = seqHelper.getOrdersSeq(); System.out.println("order------>"+id); seqHelper.addOrder(id); } }).start(); } } } }).start(); for (int i=0; i<10; i++) { for (int j=0; j<2000; j++) { new Thread(new Runnable() { @Override public void run() { long id = seqHelper.getProSeq(); System.out.println("pro ------>"+id); seqHelper.addPro(id); } }).start(); } }
观察测试过程,并没有出现主键冲突异常。
order------>122943
order------>122944
order------>122945
order------>122946
order------>122947
order------>122948
pro ------>42202
pro ------>42203
order------>122949
pro ------>42204
order------>122950
pro ------>42205
pro ------>42206
从测试结果看到,order表和pro表,产生的id是互相对立的,并不是顺序一起的。
结论是这个方案确实可行,在大型分片集群中,我们可以使用一台独立的数据库,作为专门的seq服务器。对于单点问题,flicker也给出一个很巧妙的方法,就是两台服务器做一个轮训负载,分别设置两台数据库产生的id方式为奇、偶,这样即使一台出现当机,也不会出现id混乱问题。对于小型无分布式应用,我们可以把seq表直接建立在同一个库中,这样以后扩展也是非常方便的。
相关推荐
Twitter的分布式自增ID算法snowflake (Java版)
Twitter的分布式自增ID雪花算法snowflake (Java版)
主要介绍了Java实现Twitter的分布式自增ID算法snowflake,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
Evict using approximated LRU among the keys with an expire set
We have retired the initial release of Snowflake and working on open sourcing the next version based on Twitter-server, in a form that can run anywhere without requiring Twitter's own infrastructure ...
Twitter Snowflake算法,php版代码; 请见博客: http://blog.csdn.net/envon123/article/details/52953872
分布式数据库架构及企业实践-基于Mycat中间件分布式数据库架构及企业实践-基于Mycat中间件分布式数据库架构及企业实践-基于Mycat中间件分布式数据库架构及企业实践-基于Mycat中间件分布式数据库架构及企业实践-基于...
用 gRPC Protobuf Go 在分布式系统中实现连续递增 ID
无论是对于软件工程师、测试工程师、运维工程师、软件架构师、技术经理,还是对于资深 IT 人士来说,《分布式数据库架构及企业实践——基于Mycat中间件》都极具参考价值。 目录 第 1 章数据库中间件与分布式数据库...
分布式数据库架构及企业实践——基于Mycat...无论是对于软件工程师、测试工程师、运维工程师、软件架构师、技术经理,还是对于资深 IT 人士来说,《分布式数据库架构及企业实践——基于Mycat中间件》都极具参考价值。
从Paxos到Zookeeper分布式一致性原理与实践.pdf从Paxos到Zookeeper分布式一致性原理与实践.pdf从Paxos到Zookeeper分布式一致性原理与实践.pdf从Paxos到Zookeeper分布式一致性原理与实践.pdf从Paxos到Zookeeper分布式...
3、《分布式服务框架原理与实践》首先分析了作为一个分布式服务框架所需具备的能力,包括服务注册中心、服务调用、服务路由、服务发布/灰度发布等;接着分析了服务底层如何有效地进行通信,包括通信框架、序列化...
分布式Java应用基础与实践pdf高清扫描版,解压即可使用,欢迎下载...........................................
分布式操作系统 原理与实践 pdf
分布式服务框架原理与实践_李林锋著.pdf 分布式服务框架原理与实践_李林锋著.pdf
《大型分布式网站架构设计与实践》主要介绍了大型分布式网站架构所涉及的一些技术细节,包括SOA架构的实现、互联网安全架构、构建分布式网站所依赖的基础设施、系统稳定性保障和海量数据分析等内容;深入地讲述了...
《分布式服务框架原理与实践》,学习分布式必备。该资源仅供大家了解书的内容,如果真有兴趣深入学习,建议购买正版书籍。
分布式数据库架构及企业实践:基于Mycat中间件 分布式数据库架构及企业实践:基于Mycat中间件
从PAXOS到ZOOKEEPER分布式一致性原理与实践从PAXOS到ZOOKEEPER分布式一致性原理与实践从PAXOS到ZOOKEEPER分布式一致性原理与实践
《分布式Java应用基础与实践》pdf电子版,