萍聚社区-德国热线-德国实用信息网

 找回密码
 注册

微信登录

微信扫一扫,快速登录

萍聚头条

查看: 2736|回复: 38

[职业生涯] 求java数据库大神乱入!

[复制链接]
发表于 2016-11-26 13:04 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册 微信登录

x
本帖最后由 我想我会 于 2016-11-26 14:59 编辑

工作中碰到一个难题,求java数据库高手和大神解惑!

任务需求:

1. 数据库:

数据库里有一个表,表里只有一个列,这个列是主键,同时是自增的

每次存入一个entity的时候,主键自动增加1

2. 业务层:

每当用户打开一个业务的时候,就会有一个entity 被保存到数据库里,然后立即返回这个entity的主键,应该是当前主键最大值,发送给客户,类似下面的情形:


....

speichereAktenziechen(entity);  //存入数据库,主键自增1

int zahl = ermittleAktenzeichenZahl(); // 从数据库里取出该entity的主键值,也就是当前最大主键值

publischerEvent.schicke(zhal); // 发送回客户 ... 这个不重要

....

一句话概括就是: 每次打开业务用户都要获得一个当前最大的ankenzeichenzhal!

3. 这里不关心具体使用哪种数据库,Oracle or mysql or db2 or serversql , 因为上面的操作都是在 上层 ORM 层进行(ORM框架使用的是 apache cayenne,使用哪种orm框架也不重要), 所以这个问题不能通过具体的数据库操作和设置解决

4. 项目要求高并发,可能同1秒有几百到几千个用户在执行上面的操作

5. 问题:

1. 数据库只有一个列,该列是主键,而且是自增的,这个可行吗? 如何插入entity呢?

insert 直接 插入 空的 entity,  总报错说sql 的 insert 格式不正确,出现这种情况: insert into GWA_AKTENZEICHEN values();  values() 里面是空的,所以报错.

entity 应该带有一个属性 (attribute) id ?  这个属性id 是否是主键? 主键自增的话,怎么能随便插入值?


2.


int zahl = ermittleAktenzeichenZahl(); // 从数据库里取出该entity的主键值,也就是当前最大主键值

如何取出最后一次插入的主键值呢(自动新增的主键值,last insert)? 不在数据库层面如何解决这个问题?


3.

speichereAktenziechen(entity);  //存入数据库,主键自增1

int zahl = ermittleAktenzeichenZahl(); // 从数据库里取出该entity的主键值,也就是当前最大主键值

这两个操作在高并发的情况下,如何保证得到正确的 aktenzeichenZahl ?  

保证一个entity存入数据库之后,立即从数据库里得到这个entity的主键值? 这个Transaktion 的问题怎样解决呢?

大家都懂的,在高并发的情况下,int zahl = ermittleAktenzeichenZahl();不一定能取得正确的最后一次插入值 last insert, ,那么分配给用户的 zahl 可能是错误的!

两个操作直接写在一起应该不对吧?应该如何解决呢?


感觉这问题挺有难度的,不知道如何解决,工版藏龙卧虎,能让无数,所以求java数据库高手和大神指点一下!




Die von den Nutzern eingestellten Information und Meinungen sind nicht eigene Informationen und Meinungen der DOLC GmbH.
 楼主| 发表于 2016-11-26 13:16 | 显示全部楼层
本帖最后由 我想我会 于 2016-11-26 12:18 编辑

保存和取出在高并情况下可能出现的问题:

1.

java 方面已经执行了 3 次 speichen 操作

假设数据库方面 自增操作能快点,已经完成3 speichen, 但 ermittle 取出操较慢,才完成第一次取出操作,开始第二次取出,那么第2个用户得到的 aktenzeichenzhal 就不是2 而是错误的 3!

2. 另外一个更显而易见的错误

保存1个 entity,  数据库方面自增较慢,还没来的及存入,  java 方面已经开始执行 ermittleAktenzeichenZahl 方法, 数据库开始 取出操作,这时候取出的可能是0!

晕啊,这个问题到底应该如何解决呢????头疼  求高手和大神指点!
Die von den Nutzern eingestellten Information und Meinungen sind nicht eigene Informationen und Meinungen der DOLC GmbH.
发表于 2016-11-26 13:28 | 显示全部楼层
Die von den Nutzern eingestellten Information und Meinungen sind nicht eigene Informationen und Meinungen der DOLC GmbH.
发表于 2016-11-26 19:40 | 显示全部楼层
本帖最后由 leelight 于 2016-11-26 19:46 编辑

这种情况下,你用什么数据库区别非常的大:
1.  如果是mysql,那么LAST_INSERT_ID满足上述的高并发条件,因为mysql是根据每个连接来返回相应的值

For LAST_INSERT_ID(), the most recently generated ID is maintained in the server on a per-connection basis. It is not changed by another client. It is not even changed if you update another AUTO_INCREMENT column with a nonmagic value (that is, a value that is not NULL and not 0). Using LAST_INSERT_ID() and AUTO_INCREMENT columns simultaneously from multiple clients is perfectly valid. Each client will receive the last inserted ID for the last statement that client executed.

2.如果是mssql,放狗 scope_identity

3. oracle 的获取sequence的确有些隐患,可以预先自己给出id,放狗twitter的IdWorker(snowflake),生成同一时空下唯一的自增id.
当然snowflake算法适合所有非自增数据结构

论坛里有其它db大神,等其他人

Die von den Nutzern eingestellten Information und Meinungen sind nicht eigene Informationen und Meinungen der DOLC GmbH.
 楼主| 发表于 2016-11-26 22:16 | 显示全部楼层
Die von den Nutzern eingestellten Information und Meinungen sind nicht eigene Informationen und Meinungen der DOLC GmbH.
 楼主| 发表于 2016-11-26 22:28 | 显示全部楼层
leelight 发表于 2016-11-26 18:40
这种情况下,你用什么数据库区别非常的大:
1.  如果是mysql,那么LAST_INSERT_ID满足上述的高并发条件, ...

谢谢回复!

我狗狗了一下,看到过这个LAST_INSERT_ID,但需求上说,数据库类型是不确定的,可能是任何一种数据库,问题要在上一层解决,通过设置具体的数据库或者数据库操作,好像不行的

能不能在上一层通过简单的Transaktion设置解决呢? 把两个操作设置成一个transaktion内,保证每次两步操作作为一个单元执行,执行完了再处理下一个 entity? 这个transaktion应该在orm层上设置?



Die von den Nutzern eingestellten Information und Meinungen sind nicht eigene Informationen und Meinungen der DOLC GmbH.
发表于 2016-11-27 00:44 | 显示全部楼层
本帖最后由 leelight 于 2016-11-27 00:46 编辑
我想我会 发表于 2016-11-26 22:28
谢谢回复!

我狗狗了一下,看到过这个LAST_INSERT_ID,但需求上说,数据库类型是不确定的,可能是任何 ...

上层解决, 那就预给id,你们上层框架无非ssh或ssm
transaction atom不能保证多连接唯一性。
Die von den Nutzern eingestellten Information und Meinungen sind nicht eigene Informationen und Meinungen der DOLC GmbH.
发表于 2016-11-27 23:01 | 显示全部楼层
1:数据库一张表里面只有定义为key的行能设置为自增.一张表你可以定义多个key,但是一张表只能设置一个子增列.一般是设置到id(默认设置为primary key)上. 如果一张表只有一个列主键列id,先不说这么设计表有没有意义,你每次可以插入一个空项,id每次递增.你说数据库报错,那么需要更多信息,数据库版本,测试环境,具体报的什么错来进一步分析.
2:既然是用的orm框架,那么基本上在保存数据的时候会有返回值的吧,一般用法就是在程序里面创建一个对象,比如你想像1里面这样创建一个表,对应的对象实体比如档案编号只带一个值 id .那么你需要保存一个新的就可以建立一个新的空档案编号对象,然后保存以后返回的就是带当前id值的了.看了看apache cayenne文档,新增一条更简单,大概就是这么个写法 Artist artist = (Artist) context.newObject(Artist.class);不过既然你们是用java,那么java规范的jpa不是更好用么.还有看你提到主键插入值,你可以创建一个实体带你想要的id值,当然这个id是数据库里面没有分配过的,然后保存,但是这样的话下一个自动插入的对象,获得的id是从你刚刚保存的值开始自增的,不会自动填满系统原来最大id值和你刚刚保存的那个之间的部分.
3:同上,既然是用orm框架,那么一次persitent操作就是原子的啊,既然可以保存对象以后在返回的值里面拿到自动生成的id值,没必要再另外去尝试读取了.

另外你们这个设计谁做的啊,你们做项目有没有安排一个架构师跟进呢.看你问题里面描述的,又要高并发,高可用,那么把这么一个关键操作放到这样一张表里面来,这张表就存一个id,越简单那么其它表对它的依赖就越强,以后新增业务带来的复杂度增加有估算过没有呢.还有你们的系统部署是单实例的么,现在就在考虑每秒几k的访问操作,发展下去很快一台主机就抗不住了吧,有计划然后怎么办么,如果演变成多主机,那么数据库之间的同步还有协调准备怎么做,系统必须相应更新吧,到那时候再来做的话,业务量和发布新版本时间的压力就是山大了啊
Die von den Nutzern eingestellten Information und Meinungen sind nicht eigene Informationen und Meinungen der DOLC GmbH.
 楼主| 发表于 2016-11-27 23:32 | 显示全部楼层
leelight 发表于 2016-11-26 23:44
上层解决, 那就预给id,你们上层框架无非ssh或ssm
transaction atom不能保证多连接唯一性。

谢谢回复,我们不用SS,这两个框架或者相似功能部分用的是自己开发的,orm用的cayenne

头说不能用预给id,比如在上层 id + 1, 就用主键自增,然后还不让在数据库层设置,我真不知道怎么弄了
Die von den Nutzern eingestellten Information und Meinungen sind nicht eigene Informationen und Meinungen der DOLC GmbH.
 楼主| 发表于 2016-11-27 23:59 | 显示全部楼层
本帖最后由 我想我会 于 2016-11-27 23:04 编辑
gaokx 发表于 2016-11-27 22:01
1:数据库一张表里面只有定义为key的行能设置为自增.一张表你可以定义多个key,但是一张表只能设置一个子增列 ...

谢谢回复

1. 我测试用的数据库是Oracle, 我试过1. 插入空entity,entity没有任何属性, 出现上面的错误, 2. entity带一个id 属性,结果出现 数据库运行期错误,但是好像跟orm框架有关, 3. entity 带一个 localDate属性,文件号对于的年份,成功插入,主键自增,但需求要求entity应该是空的,不带任何属性!

2. 我们orm用的是 cayenne, cayenne 这个东西很烦,用的少所以文档和资料非常少,cayenne 使用起来远没那么简单, 它们文档提供的存取命令一点用没有,实际中必须用 map.xml 和 mapper (需要自己为每一个java Objekt写mapper, mapper里实现各种具体的映射)来映射存取数据, 非常繁琐和恶劣,而且出现问题跟本狗不到答案! 如通过cayenne设置主键自增需要在 map.xml里给主键分配属性 isGenerade=true, 1里的问题,我都不知道到底是数据库的原因,还是cayenne的问题?

我们不用jpa,直接用cayenne, 狗了一下,这个问题如果在数据库层面挺好解决的,oralce 用 sequence: Select CASE_SEQ.currval from dual, mysql 用 last_insert_id, 都能得到最新插入的自增主键, 而且能保证多用户高并发条件下反回正确ID,不同用户不同ID

而且像oracle 设置了自增主键以后,即便你插入一个id mit einem beliebigen wert,或者 id = null, 都不会破坏主键自增,因为主键自增来自于sequence,  而用了cayenne 之后,我给entity中设置一个属性id, 无论插入null或者一个值,都发生 1里 2 的错误! 我不知道到底是谁的原因?

所以明天我只能去问问头,这问题能不能放到数据库层解决

我们的头就是架构,不知道他怎么想的,把这么一个本来属于他的任务让我做,有故意为难我的感觉?!目前的任务就是这个,其它的我不清楚,也不关心,那些本来是他该做的!



Die von den Nutzern eingestellten Information und Meinungen sind nicht eigene Informationen und Meinungen der DOLC GmbH.
您需要登录后才可以回帖 登录 | 注册 微信登录

本版积分规则

手机版|Archiver|AGB|Impressum|Datenschutzerklärung|萍聚社区-德国热线-德国实用信息网

GMT+2, 2024-11-2 13:33 , Processed in 0.066969 second(s), 15 queries , MemCached On.

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表