`

【原创】分布式自增id的一次实践测试

 
阅读更多

自增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表直接建立在同一个库中,这样以后扩展也是非常方便的。

 

 

 

 

 

 

分享到:
评论

相关推荐

    springboot分布式自增id_javaredis_源码

    在分布式系统中,为了生成全局唯一的自增ID,可以使用Snowflake算法、Twitter的分布式ID生成服务,或者基于Redis的序列号生成方案。Spring Boot可以轻松集成这些方案,通过配置和注解简化开发流程。 2. **Redis作为...

    Twitter的分布式自增ID雪花算法snowflake

    雪花算法是Twitter开源的一种分布式ID生成算法,它能够生成全局唯一、递增且无碰撞的64位整数ID。这个算法在分布式系统中非常适用,因为传统的序列号生成方式在分布式环境中往往难以解决冲突问题。下面我们将详细...

    snowflake-C语言实现分布式自增有序的唯一ID生成算法

    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的分布式自增ID算法Snowflake的PHP实现,Snowflake PHP版本,高并发唯一id,全局唯一id,不重复id

    Twitter Snowflake算法,php版代码; 请见博客: http://blog.csdn.net/envon123/article/details/52953872

    2.分布式数据库架构及企业实践-基于Mycat中间件

    分布式数据库架构及企业实践-基于Mycat中间件分布式数据库架构及企业实践-基于Mycat中间件分布式数据库架构及企业实践-基于Mycat中间件分布式数据库架构及企业实践-基于Mycat中间件分布式数据库架构及企业实践-基于...

    [网盘]大型分布式网站架构设计与实践.pdf

    大型分布式网站架构设计与实践.pdf &lt;br/&gt;《大型分布式网站架构设计与实践》主要介绍了大型分布式网站架构所涉及的一些技术细节,包括SOA架构的实现、互联网安全架构、构建分布式网站所依赖的基础设施、系统稳定...

    分布式Java应用基础与实践pdf

    分布式Java应用基础与实践是Java开发领域中的一个重要主题,它涉及到如何通过网络将多个独立的计算机节点连接起来,协同处理任务,以实现系统的高可用性、高性能和可伸缩性。在Java中,分布式系统主要依赖于一些核心...

    分布式数据库架构及企业实践-基于Mycat中间件

    无论是对于软件工程师、测试工程师、运维工程师、软件架构师、技术经理,还是对于资深 IT 人士来说,《分布式数据库架构及企业实践——基于Mycat中间件》都极具参考价值。 目录 第 1 章数据库中间件与分布式数据库...

    分布式数据库架构及企业实践-基于Mycat中间件.pdf

    分布式数据库架构及企业实践——基于Mycat...无论是对于软件工程师、测试工程师、运维工程师、软件架构师、技术经理,还是对于资深 IT 人士来说,《分布式数据库架构及企业实践——基于Mycat中间件》都极具参考价值。

    从Paxos到Zookeeper分布式一致性原理与实践.pdf

    从Paxos到Zookeeper分布式一致性原理与实践.pdf从Paxos到Zookeeper分布式一致性原理与实践.pdf从Paxos到Zookeeper分布式一致性原理与实践.pdf从Paxos到Zookeeper分布式一致性原理与实践.pdf从Paxos到Zookeeper分布式...

    分布式服务框架原理与实践-李林锋著.pdf

     3、《分布式服务框架原理与实践》首先分析了作为一个分布式服务框架所需具备的能力,包括服务注册中心、服务调用、服务路由、服务发布/灰度发布等;接着分析了服务底层如何有效地进行通信,包括通信框架、序列化...

    分布式操作系统 原理与实践 pdf

    分布式操作系统 原理与实践 pdf

    从PAXOS到ZOOKEEPER分布式一致性原理与实践

    《从PAXOS到ZOOKEEPER:分布式一致性原理与实践》是一本深入探讨分布式系统中一致性问题的著作。在当今大数据和云计算的时代背景下,分布式系统的应用越来越广泛,而其中的核心挑战之一就是如何保证数据的一致性。...

    分布式服务框架原理与实践_李林锋著.pdf

    分布式服务框架原理与实践_李林锋著.pdf 分布式服务框架原理与实践_李林锋著.pdf

    大型分布式网站架构设计与实践.带目录书签.完整版 陈康贤

    《大型分布式网站架构设计与实践》主要介绍了大型分布式网站架构所涉及的一些技术细节,包括SOA架构的实现、互联网安全架构、构建分布式网站所依赖的基础设施、系统稳定性保障和海量数据分析等内容;深入地讲述了...

    《分布式服务框架原理与实践》高清完整版

    《分布式服务框架原理与实践》这本书深入浅出地讲解了分布式服务的基础理论和实际操作,对于想要深入了解和掌握这一领域的读者来说是一份宝贵的学习资料。 在分布式服务框架中,主要涉及以下几个核心知识点: 1. *...

    《分布式Java应用基础与实践》pdf电子版

    《分布式Java应用基础与实践》pdf电子版,

    分布式服务框架原理与实践.zip

    简介:《分布式服务框架:原理与实践》创作者具有丰富的分布式服务框架、平台中间件的架构设计和实践经验,主导设计的华为分布式服务框架已经在全球数十个国家成功商用。《分布式服务框架:原理与实践》依托工作实践...

    java快速ID自增器

    3. **分布式ID生成器**:如Snowflake算法,它是由Twitter开源的一种分布式ID生成方案。通过时间戳、工作机器ID和序列号三部分组合,可以生成全局唯一的ID,而且排序性好。在Java中,可以使用诸如Snowflake或者其变种...

Global site tag (gtag.js) - Google Analytics