公司里做的项目, 打算用多个web服务器, 共同连一个DB, 在某个基础表里的数据需要逻辑唯一, 既某对象(attr1="a", attr2="b")在数据库里只能存在一条记录, 由于这个基础表在用户使用中是用户自定义的, 因此有可能a,b2个用户通过web服务器a和web服务器b同时提交对象(attr1="a", attr2="b")...那么在这种情况下如何使DB里面最终只有一条记录?
公司的做法是:
public synchronized A save(A a){
//查找数据库看有没有a
if(没有记录){
//锁表//插入记录 }else{ //返回数据库记录 }
}
唯一索引。。。。
把save交给一台机器处理,然后暴露API给其他机器使用
可以在数据库里面建一个版本行。。。
如果提交a或者b就更新版本行
This is a read-write lock problem.
when someone is writing the DB, other people should wait.
In java the sychronized key word has the function of similar read-write lock.
I think the codes are proper under common situation.
[quote="by5739"][quote="rain2005"]可以在数据库里面建一个版本行。。。
如果提交a或者b就更新版本行[/quote]
建版本好像不能解决问题啊,不管有没版本,都要先查询数据库有没有记录才能决定是插入还是不插入(或者更新),但是这个2个动作之间要是被人抢先插入,就会出问题
[/quote]
所以说才加个版本行啊,提交是看版本有没有变化撒,如果版本行版本发生变化就会滚事务,不是很easy的事么?
[quote="by5739"][quote="rain2005"]可以在数据库里面建一个版本行。。。
如果提交a或者b就更新版本行[/quote]
建版本好像不能解决问题啊,不管有没版本,都要先查询数据库有没有记录才能决定是插入还是不插入(或者更新),但是这个2个动作之间要是被人抢先插入,就会出问题
[/quote]
所以说才加个版本行啊,提交是看版本有没有变化撒,如果版本行版本发生变化就会滚事务,不是很easy的事么?
如果是1个DB,那么唯一的事务数据库自己会处理的,需要你在程序中做判断么?纯粹属于不了解数据库
attr1和attr2做成主键,这样让数据库帮你检查。
乐观锁,版本号。参考hibernate实现
交给数据库。。。设计好数据库的数据规范就行
到是锁了 不过就是慢啊
[quote="by5739"][quote="rain2005"][quote="by5739"][quote="rain2005"]可以在数据库里面建一个版本行。。。
如果提交a或者b就更新版本行[/quote]
建版本好像不能解决问题啊,不管有没版本,都要先查询数据库有没有记录才能决定是插入还是不插入(或者更新),但是这个2个动作之间要是被人抢先插入,就会出问题
[/quote]
所以说才加个版本行啊,提交是看版本有没有变化撒,如果版本行版本发生变化就会滚事务,不是很easy的事么?
[/quote]
不太明白啊。。。比如顺序是这样的:
a服务器检查DB发现没有数据
b服务器检查DB发现没有数据
b服务器插入版本为1,id=1的数据
a服务器插入版本为1,id=2的数据
id是db自动生成,这样好像不能控制数据唯一吧?
[/quote]
直接往数据库里面插数据,加个时间字段,精确到毫秒级就差不多了,取最新的那个时间字段的行
做一个数据库操作锁,方法调用前都要取锁,取到锁的才用执行,否则等待,这个锁可以放在缓存服务器中,集群中的所有节点连上这台缓存服务器
并发操作概率不大的话用乐观锁就可以了。
感觉这就和以前C/S一样。
直接用数据库建立唯一性索引就可以解决,应用只需要处理好异常就可以了。期望Web应用来实现的话,太麻烦而且不一定可靠。
悲观锁 or 乐观锁, 悲观锁用在必发比较少的情况, 可以不用修改现有的数据库,
乐观锁 用于 并发相对较多的情况, 需要加一个version 字段
修改的时候where里面带上修改前的值,如:
update table set attr1='a2',attr2='b2' where attr1='a1' and attr2='b2'
用最高级别的序列化读?
脏读,不可重复读,幻读?到底是哪个问题呢?
[quote="jxsgy"]attr1和attr2做成主键,这样让数据库帮你检查。[/quote]
N多人提版本,也不看人家说的是并发插入问题,版本只解决更新问题
做成复合主键比较实在简单
PK是干嘛的?
利用数据库的锁机制解决,不同的数据库有所不同。
[quote="dmewy"]PK是干嘛的?[/quote]
唯一性约束比较好吧,不一定用pk,能不用联合主键尽量不用吧,特别是大表
如果不想锁表,且又必须保证两边的提交都不会出错,那么可以采用以下异步的方式来实现;
假设有对象
A{
att1,
att2
}
那么,针对这个对象建立两个表,一个是主数据表,保存状态,另外一个是脏数据表,用于提交状态修改的内容,两个表列基本一致,脏数据表多一个时间戳字段。所有的提交都insert到脏数据表,这样子不用锁表,但是又保证了每次提交都快速得到响应,然后在之后的任意一次读取属性的时候都尝试合并脏数据。
假设T1,T2,T3都在同时提交,
T1提交{'aa','bb'},T2提交{'11','22'},T3提交{'12','fr'}虽然说是同时提交,但是总有先后顺序,假设数据库中脏表此时如下
T1{'aa','bb'}2009-8-10 10:04:33 123
T3{'12','fr'}2009-8-10 10:04:33 125
T2{'11','22'}2009-8-10 10:04:33 127
每次入库都相差2ms,然后T2开始读取属性了,那么此时就开始尝试合并,其实合并很简单,就是取最后提交的那一条,这里是T2,那么根据T2在脏表中的数据update主表的值后将主表记录返回,同时删除脏表中的数据,之后T1,T3再来读取的话,当发现脏表无数据就直接返回主表的数据。
由此能够在不锁表的情况下实现高效率的并发访问。
[quote="by5739"][quote="rain2005"][quote="by5739"][quote="rain2005"]可以在数据库里面建一个版本行。。。
如果提交a或者b就更新版本行[/quote]
建版本好像不能解决问题啊,不管有没版本,都要先查询数据库有没有记录才能决定是插入还是不插入(或者更新),但是这个2个动作之间要是被人抢先插入,就会出问题
[/quote]
所以说才加个版本行啊,提交是看版本有没有变化撒,如果版本行版本发生变化就会滚事务,不是很easy的事么?
[/quote]
不太明白啊。。。比如顺序是这样的:
a服务器检查DB发现没有数据
b服务器检查DB发现没有数据
b服务器插入版本为1,id=1的数据
a服务器插入版本为1,id=2的数据
id是db自动生成,这样好像不能控制数据唯一吧?
[/quote]
如何发现没有数据呢?所以才说要加[b]版本行[/b]啊,不是[b]版本列[/b],在初始数据库数据是就插入这个版本行,插入数据时修改版本行数据,如果插入A或者B后版本行变化回滚就可以了。
乐观锁当然可以解决
这么简单的东西搞得这么复杂.
你要说清楚插入什么数据才会出现这种情况.
你把表设计好就行了.
你N太APP SERVER都是只一台DB.
根据你的业务把主键设计好就行了.
为何会出现两个字段肯定相同的情况?
那就说明这两个字段不适合做PK.
你再根据业务类型建立个PK就行了.
这明明就是一个表结构设计的业务问题.
这里非要搞成一个技术贴.
不懂不懂.
连锁都搞出来了..
这个可以在设计上加于考虑
如果这个记录只有主键约束,则可以考虑主键在插入的时候生成,或者是使用sequence。
如果有其他的约束,可以先锁住该记录,比如oracle的select **** for update
然后再去插入或者更新操作。
你那是跨进程的东西,加上sychronized有用么?服务器A上的实例跟服务器B上的实例互斥?
比较支持弄个专门服务器专门处理数据库事务,其它服务器直接调用它的增删改查的接口来进行操作。对于单机操作的话应该就不会出现楼主所担心的顾虑。
[quote="by5739"][quote="pipilu"]你那是跨进程的东西,加上sychronized有用么?服务器A上的实例跟服务器B上的实例互斥?[/quote]
不是, 那个是防止2个用户在同一台web服务器新增同样的数据用的[/quote]
那这就不只跟多个Web应用服务器有关系了,最好能从业务上避免这种提交。
[quote="zelsa"]把save交给一台机器处理,然后暴露API给其他机器使用[/quote]
如果这样子,是不是有些浪费了,而且SAVA请求过多,是不是会网络堵塞,有更好的解决方案吗。
[quote="treblesoftware"][quote="zelsa"]把save交给一台机器处理,然后暴露API给其他机器使用[/quote]
如果这样子,是不是有些浪费了,而且SAVA请求过多,是不是会网络堵塞,有更好的解决方案吗。[/quote]
交给一台机器也有并发的问题吧
是否考虑使用MQ的queue,所有的web发送到mq上,有一个消费者消费这些数据
这种方式有几个问题:
1、异步,不知业务允许
2、消费稍微有些慢
ps. 为什么是会员设置的数据,但是这个表有不和userId有关联。
1.针对这两个字段做唯一索引;
2.然后web服务器上直接插入,抓特定的Exception判断是不是违反了唯一性。
楼主没有把需求说的太清楚,从楼主的意思来看,只有唯一存放的APP逻辑才好解决。
否则你这种复杂判断唯一性的情况,靠数据库解决不了的。
[quote="by5739"][quote="lixjluck"][quote="treblesoftware"][quote="zelsa"]把save交给一台机器处理,然后暴露API给其他机器使用[/quote]
如果这样子,是不是有些浪费了,而且SAVA请求过多,是不是会网络堵塞,有更好的解决方案吗。[/quote]
交给一台机器也有并发的问题吧
是否考虑使用MQ的queue,所有的web发送到mq上,有一个消费者消费这些数据
这种方式有几个问题:
1、异步,不知业务允许
2、消费稍微有些慢
ps. 为什么是会员设置的数据,但是这个表有不和userId有关联。[/quote]
这个是基础业务数据, 是使用者自己录入然后在上面做业务的, 是大家共享的基础数据, 所以不和userid挂钩[/quote]
有什么浪费的,这样的系统本来就该用这种模式。如果save的压力过大,异步MQ是应该考虑的,数据库的能力终究是有瓶颈的,用一个集中的AppServer来处理具有更好的系统伸缩性。
乐观锁可以解决这个问题。还有另外一种思路,就是在app server之间共享一个锁,设置一个对所有app server起作用的写锁,这个需要用一些工具,如terracotta。这样的好处是你可以不用考虑你的app 最终部署到多少台机器上,需要部署多个实例时,只需要通过简单的配置就可以保证多server并发读写的正确性。
其实你们公司的做法就可以解决你提出的问题了....
在保存之前就已经查询了表里有没有相同的记录....
就算2台web服务器同时提交,在DB操作save方法的时候都会有先后顺序,
后一个save操作的时候查询的时候就有了相同的记录...就不会在save
不必要想的那么复杂.
不就是一个简单的分布式嘛!save等操作放到统一服务器上,其他的调用就是你,哪有那么复杂的啊!
很简单的告诉楼主,使用集群式(负载均衡)就完全不用考虑上述情况了。建议楼主多看点构架方面的书籍...
对,这是一个集群问题,数据软件架构要解决的问题,我认为最好方案是ejb
这个是属于典型的数据库封锁和并行性的问题。
你们这个在应用上解决的方案,从这可以看出你们这个团队没有人懂数据库。
如果你不想深入研究数据库锁知识,你可以看看的hibernate的锁机制。会对你有帮助。
给个方式,类似复合主键,但是要简单些,你看看
基于这个表对应的对象的某些属性,使用某种算法,生成唯一id,多个web服务器生成的id都是一样的,把这个字段建成unique。多服务器并发插入时,如果此id重复,则抛出异常。
精确到毫秒数啊。先根据插入的值在数据库查询一遍没有就在进行插入,修改啊
利用Hibernate的悲观锁和乐观锁
并发插入的问题,都会存在,个人的意见是楼主是否考虑一下这个情况发生的概率是多少,如果加入版本的问题实际上对解决这类问题作用不是很大,他解决了并发修改的问题,但是对于并发插入问题很难解决,所以建议如果非要做,就将这种事务交由数据库来完成,你可以对字段进行唯一性的约束
不明白LZ的问题。
1、有PK的话,那么不管你并发不并发,表里的每行数据肯定都是唯一的。
2、为什么两个用户提交了2条属性相同的数据,就只能保存一条,至少这两条数据的提交用户是不同的啊。(感觉需求没理解清楚)
3、LZ说的情况只有并发修改的话才要考虑,这种情况就采用乐观锁,在表里加个版本字段,每次更新时吧字段加1,update的时候根据版本字段和PK来更新就可以避免并发带来的问题