多线程带来的困扰

an9ryfr09 2008-11-28 06:26:17
我的工作是做接口,有许多合作方,我们需要频繁通过http协议来传输数据。

接口里面还会有一些业务逻辑操作,首先要根据数据中某一个参数来判断数据库中是否存在,如果存在,exit()。否则插入数据库,我再去访问别人的接口。

但访问我接口的有些合作方,会采用多线程。我这边会遇到一个问题:有时同时间内会过来n个包,这n个包中有一些数据是重复的,同时去数据库中查询这条数据中的一个参数是否存在。但数据库的反应速度没那么快。也许第n个包去查询的时候,数据库中还没有存在的参数,于是返回数量为0,但n+1条数据竟然也跟着这条数据插入进了数据库。

最后造成的结果就是滤重失败,还是有重复数据插入了。

于是,我给数据库加了唯一约束,从数据库底层滤重,这样确实有效果了。重复数据插不进去了。但是,我的程序并没有停止,下面的业务逻辑操作还会继续,还会带着这条数据去请求其他合作方的接口。。。



对于这个问题,我能想到的解决方案有下面几个:
1 升级服务器
2 不将滤重的参数存在数据库,而是存在内存中,并且根据实际情况来说,重复参数只会发生在1个小时之内。
3 优化数据表结构,提高数据库查询和返回结果的速度。
4 要求我的每个合作方都在数据库增加唯一索引,因为我的转发速度也是相当快的,到他们那边程序滤重也会失效。
5 要求所有合作方采用单线程方式

关于第三个方案,实际上效果并不理想,如果并发请求真的很密集的话,还是有可能会有数据漏过去的。
...全文
108 点赞 收藏 15
写回复
15 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
海诗美妆 2008-11-29
[Quote=引用 12 楼 ShadowSniper 的回复:]
你:
通过http请求,带了3个参数,arg1,arg2,arg3
->
我:
1 接收$_REQUEST['arg1'],$_REQUEST['arg2'],$_REQUEST['arg3'];
2 插入数据库,如果arg2在数据库中存在,exit(),否则插入。
...
last 通过http请求将arg1,arg2,arg3其他合作方接口
->
他:
...


我现在最大的问题是在我那里第2步的时候,程序不能够停下来,还会继续下面的步骤。对方单线程发包的话,先用arg2在数据库中查询,返回数量>0,就exit()。如果多线程…
[/Quote]
可能是我到现在还不太清楚到底要做什么。
看lz的一些做法很反常规。

MyISAM桌面引擎只适合于没有复杂事务处理的web程序。
如果有复杂事务处理的系统,MyISAM桌面引擎,无法保证数据的一贯性。
锁表的话,效率更低。
数据库事务处理的控制在AP服务器实现是不现实的,几乎相当于开发1/3个RDB,而且用PHP似乎无法实现。

myisam在查询检索方面要强于innodb,但是具体强多少,做过性能测试吗?
速度不代表一切,连系统数据的质量都保证不了的话,再快有什么用?
如果像lz所说,对数据要求较高,为什么不考虑数据库或者数据的多重化?

其他的不说,web开发中以下这两条是想都不需要想的
1 升级服务器
如果google找lz开发个小程序,还要附带升级上万台服务器吗?
——Google半个月的收支都要赤字的话,够上头条的了。
5 要求所有合作方采用单线程方式
10客户*1线程5客户*2线程有本质区别吗?
web程序的前提就是多线程,如果真的只有一个线程,应该有比web更好的选择。
RDB提供了在多用户系统中,对单一的永持续的数据进行操作的大多数功能并将他们封装,不要让他们闲着。
回复
testoktest 2008-11-29
如果用myisam,可以使用程序锁

$overtime=5;//超时时间
$lockStr=$_REQUEST['arg2'];//需要锁定得关键值
$query="select GET_LOCK($lockStr,$overtime) ";//锁定
$res = @mysql_query($query);//为true,成功锁定

/*
干一些事,比如查询 $_REQUEST['arg2'] 是否存在
$query="select * from tableName WHERE fieldName='".$lockStr.'";
mysql_query($query);

在这段时间中,如果还有其他进程,使用一样的关键值的 sql操作,将被阻塞,直到本进程解锁
当然在这里,你在一开始的时候,就可以使用 IS_FREE_LOCK /IS_USED_LOCK 来判断关键值 的锁当前是否被占用,是的话就可以退出了。具体手册上有
*/

$query="select RELEASE_LOCK($lockStr) ";//解锁
$res = @mysql_query($query);
回复
an9ryfr09 2008-11-29
你:
通过http请求,带了3个参数,arg1,arg2,arg3
->
我:
1 接收$_REQUEST['arg1'],$_REQUEST['arg2'],$_REQUEST['arg3'];
2 插入数据库,如果arg2在数据库中存在,exit(),否则插入。
...
last 通过http请求将arg1,arg2,arg3其他合作方接口
->
他:
...


我现在最大的问题是在我那里第2步的时候,程序不能够停下来,还会继续下面的步骤。对方单线程发包的话,先用arg2在数据库中查询,返回数量>0,就exit()。如果多线程并且发包密集的话,程序滤重将会失效,这时可以利用唯一约束或事务处理。

使用UNIQUE和Transactions Processing能保证数据库插入的值唯一,但是不能让程序停止下来。而且,我的表已经有了N多数据,这些表类型都是myisam,如果将数据库引擎更改为innodb不得不说实在有点。。。
这些数据还要经常做查询,我们对实时数据也是要求很高的,myisam在查询检索方面要强于innodb。
回复
an9ryfr09 2008-11-29
[Quote=引用 8 楼 froole 的回复:]
引用 7 楼 ShadowSniper 的回复:
回楼上,你没看清我说的问题所在,问题不是出自数据库,而是程序无法中断。通过对数据库增加唯一约束可以防止数据重复插入。但我下面的程序还是会执行下去,会转发给别的合作方。这是我不愿意看到的。用事务处理可以只能解决数据库的问题,而根本无法中断程序

N个线程通过1个入口访问一个持续的数据。

转发给别的合作方是什么意思?
唯一约束是什么?

引用
同时去数据库中查询这…
[/Quote]

那个问题我已经通过将不希望重复的字段增加UNIQUE解决了。转发给别的合作方的意思就是,你通过http协议,带一些参数,访问我的接口,我收到后,还要再去请求其他合作方,把这些参数通过http协议传给他们。

现在数据库可以避免插入重复数据了,但我的php程序不会exit(),因为无法通过什么方法来告诉程序该exit了()。我希望当数据库中有重复数据的时候,程序也能exit(),事务处理也跟唯一约束在这里的作用一样,只能避免重复数据插入,并不能让程序也exit()。

目前这似乎没什么别的办法。我很多合作方也遇到这个问题没法解决,幸亏我们的数据并不要求非常精确。
回复
helloyou0 2008-11-29
1。用affected_rows判断实际插入是否成功,失败就退出不就行了吗?

2。使用事务处理是更好的方法,
innodb的速度问题没有那么严重,
你自己可以测试一下

回复
liubuweiright 2008-11-28
ding
回复
jumpheightway 2008-11-28
进行插入检查
加入缓存会影响机器性能
回复
海诗美妆 2008-11-28
[Quote=引用 7 楼 ShadowSniper 的回复:]
回楼上,你没看清我说的问题所在,问题不是出自数据库,而是程序无法中断。通过对数据库增加唯一约束可以防止数据重复插入。但我下面的程序还是会执行下去,会转发给别的合作方。这是我不愿意看到的。用事务处理可以只能解决数据库的问题,而根本无法中断程序
[/Quote]
N个线程通过1个入口访问一个持续的数据。

转发给别的合作方是什么意思?
唯一约束是什么?

[Quote]
同时去数据库中查询这条数据中的一个参数是否存在。但数据库的反应速度没那么快。也许第n个包去查询的时候,数据库中还没有存在的参数,于是返回数量为0,但n+1条数据竟然也跟着这条数据插入进了数据库。
[/Quote]
这不是典型的transaction processing?
回复
an9ryfr09 2008-11-28
回楼上,你没看清我说的问题所在,问题不是出自数据库,而是程序无法中断。通过对数据库增加唯一约束可以防止数据重复插入。但我下面的程序还是会执行下去,会转发给别的合作方。这是我不愿意看到的。用事务处理可以只能解决数据库的问题,而根本无法中断程序
回复
海诗美妆 2008-11-28
基础知识有待巩固
回复
海诗美妆 2008-11-28
lz的问题应该没有那么复杂,做好数据库的事务处理就好了。

如果用Mysql的话,桌面引擎使用InnoDB,
搜索的时候用select update语法,
这样的话,一直到下次rollback/commit,
数据一直都会被锁住。
多少个线程都无所谓。
回复
Zijian_Zhang 2008-11-28
[Quote=引用 3 楼 wj2002160416 的回复:]
我的想法是,当第一次调用某个数据时,从数据库中读出来,存在内存中(或者存为文件名),再去作判断,以后都先去判断内存中是否存去,再去判断数据库.

不过以楼主情形看来,最好是先把相关字段全部读出来,因为只有一个字段,应该不会太大,放在内存中应该可以,然后再操作
[/Quote]
这个不是楼主的解决方法的第2条吗:
2 不将滤重的参数存在数据库,而是存在内存中,并且根据实际情况来说,重复参数只会发生在1个小时之内。
回复
wj2002160416 2008-11-28
我的想法是,当第一次调用某个数据时,从数据库中读出来,存在内存中(或者存为文件名),再去作判断,以后都先去判断内存中是否存去,再去判断数据库.

不过以楼主情形看来,最好是先把相关字段全部读出来,因为只有一个字段,应该不会太大,放在内存中应该可以,然后再操作
回复
Zijian_Zhang 2008-11-28
再同情
回复
Zijian_Zhang 2008-11-28
唉,同情楼主,接分
回复
相关推荐
发帖
基础编程
创建于2007-09-28

2.1w+

社区成员

从PHP安装配置,PHP入门,PHP基础到PHP应用
申请成为版主
帖子事件
创建了帖子
2008-11-28 06:26
社区公告
暂无公告