分布式环境Javaweb+MySQL项目如何生成指定规则的不重复订单编号呢?

离开了鼠标的键盘 2019-01-18 11:50:51
项目没有用高级框架,是最基本的SpringJavaWeb项目,订单号规则是:地区代码+商品类别代码+年份+流水号
地区代码和商品类别代码都需要支持后期增加,比如用户增加南京市,就会有一个新的订单前缀 NJ+商品类别+年份+001
项目是分布式高并发的
...全文
521 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
999,你确定?超过了999不就完蛋了
maradona1984 2019-01-22
  • 打赏
  • 举报
回复
引用 11 楼 离开了鼠标的键盘 的回复:
[quote=引用 10 楼 maradona1984 的回复:]
流水号最大999,就不要扯啥高并发好不好...
直接基于数据库表生成就行了,拿一次生成一次

如何基于数据库生成呢?每个商品不同的地区不同的型号都需要从001开始,每年的同一天多个用户操作生成[/quote]
基于数据库逻辑上是很简单的,你建一个表,字段就key,序列值,key生成由调用者决定,你这个服务对外的逻辑就是,调用者拿key过来,你返回序列值,服务自身的逻辑就是,key在表中不存在,则插入一条记录,如果已经存在,在原来基础上增加

当然这个要做乐观锁,而且要保证可以得到序列,那得对乐观锁这段逻辑自旋,也就是如果没有获取锁,再重新执行下获取序列的代码,直到获取到为止,这个是最难的逻辑
你不能引入任何第三方服务,数据库这种实现是最简单的

你楼上的存储过程,个人觉得没必要思考高并发的问题,因为你单个key的序列最多999,几乎没有并发,如果要考虑较高并发,不太建议用数据库的锁来实现(你这种就是悲观锁,但存储过程一般很快的)
码匠笔记 2019-01-21
  • 打赏
  • 举报
回复
可以考虑使用 Redis 简单搭建一个服务,保存一下当前的增长序列号
  • 打赏
  • 举报
回复
引用 7 楼 离开了鼠标的键盘 的回复:
[quote=引用 6 楼 咸哼酒家 的回复:]

可以设置初始值,每次增长值,最大值,
实时获取当前值、下一个值

Mysql是不是没有序列机制,需要自己创建一个表个写函数来实现自增序列?如果是这样,分布式并非如何控制程序不同时调用呢?[/quote]
打错字了,是分布式并发
  • 打赏
  • 举报
回复
引用 6 楼 咸哼酒家 的回复:
可以设置初始值,每次增长值,最大值,
实时获取当前值、下一个值

Mysql是不是没有序列机制,需要自己创建一个表个写函数来实现自增序列?如果是这样,分布式并非如何控制程序不同时调用呢?
  • 打赏
  • 举报
回复
有没有人帮我看看我这个存储过程?扛得住分布式并发么?
  • 打赏
  • 举报
回复

CREATE TABLE `sequence_pricing` (
name VARCHAR(40) NOT NULL, -- 编号名称
current_value INT NOT NULL DEFAULT 1, -- 当前值
increment INT NOT NULL DEFAULT 1, -- 步长(每次取值的增量)
PRIMARY KEY (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE sequence_pricing ADD UNIQUE (name); -- 添加唯一索引


DELIMITER $$

DROP PROCEDURE IF EXISTS `nextval_pricing`$$

CREATE PROCEDURE `nextval_pricing`(IN seq_name VARCHAR(40),OUT result VARCHAR(40) )
BEGIN

DECLARE tsValue VARCHAR(40);
DECLARE t_error INTEGER DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET t_error=1;

START TRANSACTION;
-- 在同一个事物中,执行了update语句之后就会启动锁
UPDATE sequence_pricing SET current_value=current_value WHERE name=seq_name;
SELECT current_value INTO tsValue FROM sequence_pricing WHERE name=seq_name;
-- 因子表中没有记录,插入初始值
IF tsValue IS NULL THEN
insert into sequence_pricing(name,current_value,increment) values(seq_name, 1,1);
ELSE
UPDATE sequence_pricing SET current_value = current_value + increment WHERE name = seq_name;
END IF;
SELECT current_value INTO result FROM sequence_pricing WHERE name=seq_name;
IF t_error =1 THEN
ROLLBACK;
SET result = 'Error';
ELSE
COMMIT;
END IF;
SELECT result ;
END$$

DELIMITER ;


我这个存储过程能解决并发么?不知道insert那一步会不会被同时执行
  • 打赏
  • 举报
回复

CREATE TABLE `sequence_pricing` (
name VARCHAR(40) NOT NULL,
current_value INT NOT NULL DEFAULT 1,
increment INT NOT NULL DEFAULT 1,
PRIMARY KEY (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE sequence_pricing ADD UNIQUE (name);


DELIMITER $$

DROP PROCEDURE IF EXISTS `nextval_pricing`$$

CREATE PROCEDURE `nextval_pricing`(IN seq_name VARCHAR(40),OUT result VARCHAR(40) )
BEGIN

DECLARE tsValue VARCHAR(40);
DECLARE t_error INTEGER DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET t_error=1;

START TRANSACTION;
-- 在同一个事物中,执行了update语句之后就会启动锁
UPDATE sequence_pricing SET current_value=current_value WHERE name=seq_name;
SELECT current_value INTO tsValue FROM sequence_pricing WHERE name=seq_name;
-- 因子表中没有记录,插入初始值
IF tsValue IS NULL THEN
insert into sequence_pricing(name,current_value,increment) values(seq_name, 1,1);
ELSE
UPDATE sequence_pricing SET current_value = current_value + increment WHERE name = seq_name;
END IF;
SELECT current_value INTO result FROM sequence_pricing WHERE name=seq_name;
IF t_error =1 THEN
ROLLBACK;
SET result = 'Error';
ELSE
COMMIT;
END IF;
SELECT result ;
END$$

DELIMITER ;

我这个存储过程能解决并发么?不知道insert那一步会不会被同时执行
码匠笔记 2019-01-21
  • 打赏
  • 举报
回复
引用 12 楼 离开了鼠标的键盘 的回复:
[quote=引用 9 楼 码匠笔记 的回复:] 可以考虑使用 Redis 简单搭建一个服务,保存一下当前的增长序列号
受项目环境限制,客户不允许增加服务[/quote] 那是用 mysql 也可以自己写一个自增ID啊
  • 打赏
  • 举报
回复
引用 9 楼 码匠笔记 的回复:
可以考虑使用 Redis 简单搭建一个服务,保存一下当前的增长序列号

受项目环境限制,客户不允许增加服务
  • 打赏
  • 举报
回复
引用 10 楼 maradona1984 的回复:
流水号最大999,就不要扯啥高并发好不好...
直接基于数据库表生成就行了,拿一次生成一次

如何基于数据库生成呢?每个商品不同的地区不同的型号都需要从001开始,每年的同一天多个用户操作生成
maradona1984 2019-01-21
  • 打赏
  • 举报
回复
流水号最大999,就不要扯啥高并发好不好...
直接基于数据库表生成就行了,拿一次生成一次
咸哼酒家 2019-01-18
  • 打赏
  • 举报
回复

可以设置初始值,每次增长值,最大值,
实时获取当前值、下一个值
  • 打赏
  • 举报
回复
引用 4 楼 咸哼酒家 的回复:
拼接字符串生成订单编号:
数据字典表维护 地区代码、商品类别;
年份是代码实时获取生成的;
最高999用sequence 设置区间1-1000,不了解可以百度下数据库序列号;
然后插入数据库

刚百度了下MySQL的序列,好像是只能指定起始值和步长
咸哼酒家 2019-01-18
  • 打赏
  • 举报
回复
拼接字符串生成订单编号:
数据字典表维护 地区代码、商品类别;
年份是代码实时获取生成的;
最高999用sequence 设置区间1-1000,不了解可以百度下数据库序列号;
然后插入数据库
亲爱的Joe 2019-01-18
  • 打赏
  • 举报
回复
引用 2 楼 离开了鼠标的键盘 的回复:
[quote=引用 1 楼 亲爱的Joe 的回复:]
Snowflake可以了解一下,专门用于分布式id生成。地区代码等其他信息,你自己添加就好了。
Snowflake的效率很高

忘记说一点了,流水号必须3位数,不能大于999,这个办法能指定ID的最大值么?[/quote]
流水号最大999,意味着一个商品在一个地区每年最多999个订单。

另外,snowflake是可以自己修改源码的,假如你之前根本不知道有这个个东西的话,那就不建议你用了,你可以考虑下
UidGenerator
  • 打赏
  • 举报
回复
引用 1 楼 亲爱的Joe 的回复:
Snowflake可以了解一下,专门用于分布式id生成。地区代码等其他信息,你自己添加就好了。
Snowflake的效率很高

忘记说一点了,流水号必须3位数,不能大于999,这个办法能指定ID的最大值么?
亲爱的Joe 2019-01-18
  • 打赏
  • 举报
回复
Snowflake可以了解一下,专门用于分布式id生成。地区代码等其他信息,你自己添加就好了。
Snowflake的效率很高

81,092

社区成员

发帖
与我相关
我的任务
社区描述
Java Web 开发
社区管理员
  • Web 开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧