杂谈---令人抓狂的数据库行级锁问题
发布时间:2013-11-01 22:17:00作者:左潇龙阅读(1922 )评论(68)
引言
本篇文章只是工作当中的一个简单记录,尽管这次遇到的问题从技术上来讲并不算是特别高深的问题,但是在面临着多方压力的情况下,问题的解决还是有着不小的难度。因此这里LZ就简单的描述一下整个问题从出现到解决所采取的一系列措施,如果有对此经验丰富的猿友,不妨指点一二。倘若是新手猿友,尚未遇到过此类问题,也算是一个小小的引导吧。
东窗事发
9月27号的一天中午,LZ正在悠闲的听着音乐,exception happy的写着增删改查的代码。忽然之间,狂风大作,通讯工具疯狂弹窗,打破了LZ的闲情逸致。消息当中显示,某业务部门在顷刻之间,整个华北地区的营业部的业务无法进行。在问题出现后的半个小时之内,问题已然延伸到整个部门全国的营业部业务都无法进行。这简直是十分可怕的问题,LZ可担待不起。
当时问题的根源就发生在LZ所负责的系统当中,而很巧的是,我们的项目经理刚好请假了,于是这一重任就落在了LZ的肩上。不过LZ所负责的系统类似于一个窗口,其实在这之后,还有很多项目组在幕后默默的提供着服务,当然,也可能在默默的制造着错误。当时LZ在受到反馈之后,就立马找到运维组同事查看后台日志,结果发现出现了大量的“unable to create new native thread”。
好在LZ对JVM的运行机制以及参数配置还比较熟悉,一看到这个错误,LZ就断定,问题基本上只有两个可能。第一个是,Xss参数太大,导致每个线程的栈太大,从而减少了整体可创建的线程数量。第二个可能是,有大批量的线程一直在进行,但却一直不终止,最终导致线程数量过大。
对于这两个可能,LZ很干脆的就排除掉了第一个,因为我们的系统运行了三年之多,怎么可能将JVM的参数配置错误。所以问题的根源,一定是有一些线程,一直在运行,类似于while(true)的情况。可是很显然,系统当中不可能出现这么多while(true)的线程,那么这些线程究竟为何一直在运行呢?
正在LZ一筹莫展的时候,DBA组的同事给了一个十分有用的线索,DBA组的同事发现,我们系统的数据库当中,出现了大量的行级锁,而且这些锁的数量还在不断增加,导致数据库连接数巨大,压力剧增几近崩溃。由于数据库的服务器上不只运行着一个数据库实例,所以DBA组的同事强烈要求LZ停止系统运行。
这一下信息量有点大,LZ一时也很难快速定位问题的所在,因此只能妥协,暂停了整个系统的运行。在问题出现的前一天晚上,LZ的系统刚刚上线了一大批功能。此时在业务部门的极力催促之下,LZ在短时间内也无法找到问题根源,尽管不甘,但迫于各方面的压力,LZ最终只能采取了回退策略。而且在案发现场,公司的CTO已经到场,因为LZ系统给数据库造成的压力已经影响到了很多系统。
在回退之后,系统终于恢复了正常,从出问题到恢复,共历时一个半小时。不过这也等于失去了排错的机会,只能留着一些比较模糊的信息,私底下自己排查。这些模糊的信息主要包括以下几点。
1、代码回退之后,问题解决,因此可能是代码的问题。
2、数据库产生行级锁,因此与事务有关。
3、线程数量剧增,说明有线程被长时间挂起。
之后LZ私底下也进行了排查,经查之后,代码没有发现任何问题,但第一条的情况却表明,很可能是代码所造成的问题。出现问题的一段代码,只是一个普通的service层的方法,这类方法在系统中大批量存在,LZ实在看不出来哪里有问题。之后在LZ的系统再次上线时,也并未再出现上一次所出现的问题,因此这个问题有点不了了之的意思了。【后话:其实现在这样一分析,从上面三点已经可以找出一些蛛丝马迹了。只是LZ在当时当局者迷,加上俗务缠身,所以才忽略了这个重要的线索】
缠人梦魇
10月31日,今天本来是特别悠闲的一天,尽管LZ手中有着不少开发任务,但时间还算是比较宽裕,因此也算是不慌不忙。然而事情往往会发生在你预料之外,就在LZ刚吃完午饭,享受着人生最美好的时刻的时候(饭后一支烟,你懂的),却被一个小小的噩耗猛然间打断。而噩耗的根源,正是上一次不了了之的那个问题,如今已经时隔一个多月,这充分说明了,“出来混,迟早是要还的”。
当LZ从冒烟处回到工位上时,已然收到无数同事的批判,大体上的意思都是说,LZ的系统中一个依赖于MQ的接口再次出现了问题(与上次出现的问题的地方是一样的),导致业务流程无法进行,业务部门已经炸锅。LZ的通讯软件一根烟的功夫就弹出来N(N>=10)个窗口,邮箱也几近爆满。批判的同事更是来自四面八方,有运维组的,有业务部门的,有研发部门的,有DBA组的,全部都在问同样一个问题,“你们系统怎么回事?现在所有XX部门(某业务部门,由于业务办理周期很短,所以有什么问题都比较火急火燎)都无法正常开展业务了!”。
冷静对待
经过了上次的阵仗,LZ这一次更加蛋定了,轻咳一声,开始挨个仔细的询问各个组(不包括业务部门)的同事一个问题,“有什么异常情况吗?”。对于业务部门,LZ只能采取安抚策略,于是只好扔过去一句“放心,问题应该不大,请稍等一下,我看看是怎么回事!”。接下来LZ收到的,便是技术部各个组同事的一系列反馈,以及来自业务部门的错误截图和描述。
业务人员的反馈:在点某个按钮的时候(此按钮背后是由MQ包装的接口,业务人员自然是不知道的,他们只知道是这个按钮造成的问题),网页似乎一直在提交(就是无限读进度条),也不提示错误,但是也不提示成功,导致业务流程无法进行,后果严重。
运维组的反馈:后台会有异常信息出现,显示为“无返回结果”。(收到此反馈后,尝试了一下,LZ发现并不是对请求没有响应,而是时间很长,最终会产生无返回结果的异常)
开发人员的反馈:这里的开发人员是我们出问题的这个接口的服务端开发人员,他们表示系统无异常信息。
DBA组的反馈:数据库有行级锁,而且数量一直在堆积,导致数据库随时面临被压垮的危险。(这个表现与上次一模一样)
这次之所以可以蛋定的收集信息,是因为项目经理在压阵,否则就LZ一个人,身份低微,扛不住业务部门以及技术部各个组的压力,只能像之前那次一样,放弃治疗,先让系统恢复再说。而这一次有项目经理抗着就不一样了,LZ可以随时再现问题,这样就可以更好的定位问题了。
随机应变
由于数据库的行级锁问题,导致数据库压力太大,随时面临崩溃的可能,而且数据库的服务器上并不只有一个数据库,这还可能导致其它业务系统的崩溃。因此数据库的问题是当下之急,为此DBA组特意派专员及时帮我们杀掉产生的行级锁,释放数据库的session连接,避免了数据库服务器压力骤增而宕机的问题(上次导致好几个系统的数据库全部崩溃)。
抗着业务部门以及技术部各个组同事的压力,LZ与另外一个项目组A组(也就是出问题的接口的服务端,以下都简称A组)的核心开发以及项目经理,开始排查问题出现的可能性。从数据库的表现上来看,问题基本已经断定在接口这个service层的方法当中,而出现问题的应该是方法当中的事务没有及时的提交或回滚,接下来便是一系列排查问题的过程。
日志查询
日志的作用不需要多说,LZ先从服务器上down下来了发生问题的时间段的日志,开始一一检查当中出现的异常。结果却令人十分不满意,因为日志当中并没有什么可用的信息,唯一的一类异常,还是我们自己抛出来的(如下),而且业务人员的反馈是页面长时间没有响应,并不是系统报错(事实证明,业务人员的话一般是不可信的,不是因为他们说谎,而是因为他们的世界与我们是不一样的)。
if(result == null){
throw new RuntimeException();
}
上面这个异常是因为MQ监听消息通道时,接收的结果为空所抛出的,当时写这一段代码的开发人员的目的,应该是希望通过unchecked异常使得事务管理器将事务回滚。但是可惜的是,事务很显然没有正常回滚,也没有正常提交。
代码审查
日志查询并没有发现什么有用的信息,因为程序的显示是正常的,虽然抛出了运行时异常,但这属于事务失败而回滚的正常情况。接下来,我们就只能排查出问题的service层的方法,不过从spring事务管理器的使用惯例上来看,这一段代码并没有任何问题,只是简单的update、insert以及消息发送。
在看代码的时候我们不能忘了一点,那就是这个问题是忽然出现的,如果代码的逻辑或者事务管理是有问题的,那么在测试的时候,或者上一次上线的时候,就应该出现问题。事实上,这一段代码已经很久没有更改,并成功运行了很久(代码的极度缩减版如下所示)。
public class XXXService{
private XXXDao XXXdao;
public void methodWithAProblem(){
XXXDao.update();
sendMessage();
}
}
在此次出问题的方法当中,代码的量其实并不大,因此代码审查的时间并没有使用太多。不过可惜的是,代码的审查与日志的查询一样,都没有取得实质性的效果,因此LZ就不得不拿出了必杀技。
疯狂的日志
既然代码看上去没有明显的事务管理问题,而且事实上这段代码也成功运行了很久,因此在代码审查上花费再长时间,也很难定位问题。LZ当机立断,只能采取最卑劣的做法,将这个方法当中,任何一个有关数据库操作的后面都加入日志,并打印详细的参数信息,可以说是一句代码一句日志。
之后LZ便联系运维组的同事,将修改后并编译好的class文件放到服务器上去,并采取了重启的措施。重启其实是挺有压力的,因为很多业务人员在用,毕竟此次出问题的,只是系统中某一个规模相对中等部门的业务,其它大部分的业务还是可以正常进行的。
不过既然冒险重启了,收获就必定是可观的。在系统重启之后,LZ便再次详细查看了本次打印的日志,终于找到了一些蛛丝马迹,问题很可能出现在消息中间件上面了。但是此时还依旧不能够定性问题的根源,因为日志只是将问题缩小到了很小的范围,但具体系统与消息中间件的传输过程以及消息中间件与接口服务端的传输过程都还是不太明确的。
况且,令人纠结的是,就算与消息中间件的通信出现了问题,那也应该可以获得一个unchecked异常,从而导致事务回滚,而不是产生行级锁。而且行级锁的出现,一定是有很多人在修改一条数据,这从业务上来讲是不可能出现的,因为这些数据都是业务人员的业绩,有严格的权限控制,彼此之间都是互斥的。
水落石出
最终LZ从时间上发现了问题,从等待A组系统返回消息开始,到LZ的系统得到“无返回结果”(无返回结果的原因是消息的响应为null,而LZ也专门看了API的说明,上面的return项很清晰的写着null if time out)的异常为止,时间刚好是30秒,而LZ发现我们的MQ设置的超时时间恰好是30秒。这绝对不是一种巧合,因此LZ此时已经基本断定,是服务端(也就是A组系统)没有及时响应产生的问题。
在LZ跟项目经理说了原因之后,项目经理还问了LZ一句,“既然是超时引起的,为何会有行级锁?难道业务人员会操作一条数据?”。LZ脑子一转,忽然想到一种可能,就是业务人员很可能在等待时寂寞难耐,从而F5刷新之后再次点击,这样一来,由于上一个请求还在等待,并且将事务挂起,接下来的请求就需要等待行级锁,如果多次进行这种操作,那么最终挂起来的线程将会越多越多。如果有很多业务人员如此操作,产生的行级锁也将会越来越多。这是LZ能想到的唯一可以完美解释一切的原因。
这样一分析,上次的“unable to create new native thread”异常也就可以解释了。因为很多请求由于行级锁被挂起,这些线程都在等待数据库的行级锁,因此堆积了大量的线程,导致线程数巨大,最终无法再创建。
后续花絮
问题既然已经找到,那么接下来就是LZ找到A组项目经理以及开发人员,然后让他们检查一下监听消息中间件的线程是不是被什么意外给打断了,导致服务端不会再去处理任何消息。最终在后续的排查中,发现消息队列当中,确实堆积了大量的消息没有处理,而服务端的代码当中,是使用线程池处理的消息,目前还没有定论。不过LZ基本断定,这些监听的线程肯定在某种情况下会被终止。
之后LZ就只需要等待A组人员的排查结果了,事实证明,LZ的系统其实是没有问题的。不过有一点是应该改进的,那就是行级锁的问题。因此后来LZ将消息的发送和数据的更新调换了次序,这样可以彻底避免行级锁的产生,哪怕是再次出现这个问题,至少不会因为行级锁造成的数据库压力,而压垮整个数据库服务器。
未完待续
尽管此次基本上已经掌握了问题出现的原因,但其实最根源的地方还是没找到,那就是A组系统当中,究竟何时会中断监听线程。只要这个问题找到了,那么以后就可以防患于未然了。至今为止,A组成员还在寻找问题根源,不过最终的结果可能是,像LZ第一次碰到的时候一样,不了了之。但是还是那句话,“出来混,迟早是要还的”。
版权声明:本文版权归作者(左潇龙)所有,欢迎转载。但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
|
|
|
|
|
|
|
|
分类: 编程之路
下一篇:杂谈---回家之路
数据库死锁的解决办法
http://www.cnblogs.com/jacklondon/archive/2012/03/21/2409666.html
主要思路是,一旦检测到死锁,就立即 rollback 并退出,这样就不会有博主那样的"令人抓狂的数据库行级锁问题"了。
呵呵,可惜这里的问题集中在中间件了,其实真正的原因与行级锁关系不大,那个只是误导我们的表象,让我们以为是代码事务处理的问题,实则是消息中间件在搞鬼。
如果是因为用户不停操作同一功能的原因,你代码中应能做到:即使用户不停操作同一功能,程序也不应僵住。这是最基本的。
其次,如果不是数据库死锁,即使用户多次提交,如果发现没有成功结果,也会歇手,用户又不是机器人,难不成一直在毫无意义地重试?
再次,应由DBA分析出结果:当时有哪些SQL一直在运行没有结束,为什么运行这么长时间,博主好像也未分析。
另,if null then exception and rollback, 这个设计看上去没有问题,即使用户反复刷,最多也不过是不停给用户提示错误,怎么会执行不下去了?博主也未分析清楚。换句话讲,应做到,即使a组程序、消息中间件等完全不运行,你的程序也不过是不停报错,不应把线程挂住。
最后,如果是SQL server,请用 read _committed_snapshot 级别的数据库锁定机制,可以减少锁定等待
施主说的不应该僵住,文中已经提到了,估计施主没仔细看,“不过有一点是应该改进的,那就是行级锁的问题。因此后来LZ将消息的发送和数据的更新调换了次序,这样可以彻底避免行级锁的产生,哪怕是再次出现这个问题,至少不会因为行级锁造成的数据库压力,而压垮整个数据库服务器。”。就是这一句话说的。
其次,是行级锁不是死锁,因为DBA是有监控工具的,贫僧没必要去怀疑DBA的监控结果,而且贫僧公司的DBA组难道这么不专业?连行级锁和死锁分不清楚?而且事实上行级锁的产生贫僧已经解释的很清楚了,是由于用户的重复操作导致的行级锁,并不是死锁。
再次,那个SQL就是一个update,文中不是写了一个缩减版程序吗?先一句update,后一句消息发送。到底更新什么这个并不重要,就是更新数据库一行而已。
另,“if null then exception and rollback”这一句话施主是哪里弄的?如果空则异常回滚?我们的设计是如果产生unchecked异常就回滚,貌似和施主说的不是一个东西吧?另外这个问题同上面我写的文中那句话,已经解决了,不知道施主有否仔细看文章。。0.0
最后,不是SQLserver。。。是oracle。。。
而且贫僧已经说了,最根本的原因不在数据库,而是消息发送上面。在更新语句和消息发送调换顺序以后,那个update就是一个普通的update而已。
贫僧只是客气一下,没想到误导施主了,贫僧再强调一下。
“不过有一点是应该改进的,那就是行级锁的问题。因此后来LZ将消息的发送和数据的更新调换了次序,这样可以彻底避免行级锁的产生,哪怕是再次出现这个问题,至少不会因为行级锁造成的数据库压力,而压垮整个数据库服务器。”
文中这句话,其实贫僧这边的问题已经解决了。只是将二者调换下次序就行了。至于最根本的原因,还是得找到为什么A组没产生相应才能知道。施主不要再胡乱猜测了。。。误导施主实在不好意思。。。
随便上网搜索 数据库死锁,举例的都是说行级锁形式的死锁,极少有人举例说表级锁的。比如
a.数据库中死锁那些事儿
http://blog.csdn.net/eseaqyq/article/details/7795023
b.关于数据库死锁的了解
http://quietmadman.blog.51cto.com/3269500/1194630
“if null then exception and rollback”这一句话施主是哪里弄的====这行代码不是这个意思?
if(result == null){
throw new RuntimeException();
}
如果代码的逻辑或者事务管理是有问题的,那么在测试的时候,或者上一次上线的时候,就应该出现问题===错误,有的问题较难重现,开发、测试时未必能发现。
比如Windows、Oracle 卖出的软件后来的打补丁修复 bug, 难道不也是如此么?难道因为微软、Oracle 测试没有测出问题,就否认用户发现一个 bug?
后来LZ将消息的发送和数据的更新调换了次序,这样可以彻底避免行级锁的产生====这是不妥的。
消息发送与数据更新次序,是一个业务逻辑问题,如果因为这个逻辑顺序在技术实现上容易出问题,就自作主张调整次序,是不妥当的。
举例来说,有个软件,数据状态顺序是:待处理、软件处理、更新成处理成功状态,因为运行中碰到死锁,软件供应商(某国际IT公司)把顺序调整为:待处理、更新成处理成功状态、软件处理,后面碰到诡异的事情:状态为处理成功,但实际出错过程中报错。查了很久才查出来。然后软件供应商被狂批:还没有处理,你就知道处理成功啊?你是神仙啊?
其实贫僧这边的问题已经解决了====兄弟,难以重现的问题,你更改完代码后,并不能直接说“解决了”,说早了。
要等下次同样情况出现,你的代码确实挺过去了,才算解决了。
第一,是行级锁不是死锁 === 数据库死锁,大多是行级锁,兄弟!行级锁如果不能自行运行下去,就是死锁。
施主貌似不明白什么叫死锁,死锁是两个进程或线程由于互相等待对方的资源而永久等待的情况,很显然,这里不是这种情况。如果施主还是不清楚的话,建议百度百科数据库死锁。
第二,“if null then exception and rollback”这一句话施主是哪里弄的====这行代码不是这个意思?
if(result == null){
throw new RuntimeException();
}
这个没想到施主在秀英文,贫僧以为施主在说哪个API的文档。
第三,如果代码的逻辑或者事务管理是有问题的,那么在测试的时候,或者上一次上线的时候,就应该出现问题===错误,有的问题较难重现,开发、测试时未必能发现。
比如Windows、Oracle 卖出的软件后来的打补丁修复 bug, 难道不也是如此么?难道因为微软、Oracle 测试没有测出问题,就否认用户发现一个 bug?
这个不知道施主在说什么,貌似有点胡搅蛮缠。测试就代表要发现所有问题吗?这只是贫僧思考的一个过程。不知道施主想表达什么意思?
第四,后来LZ将消息的发送和数据的更新调换了次序,这样可以彻底避免行级锁的产生====这是不妥的。
消息发送与数据更新次序,是一个业务逻辑问题,如果因为这个逻辑顺序在技术实现上容易出问题,就自作主张调整次序,是不妥当的。
举例来说,有个软件,数据状态顺序是:待处理、软件处理、更新成处理成功状态,因为运行中碰到死锁,软件供应商(某国际IT公司)把顺序调整为:待处理、更新成处理成功状态、软件处理,后面碰到诡异的事情:状态为处理成功,但实际出错过程中报错。查了很久才查出来。然后软件供应商被狂批:还没有处理,你就知道处理成功啊?你是神仙啊?
施主,不懂就不要乱发评论了,你怎么知道业务逻辑有问题?施主有看过贫僧系统的代码?还是施主了解贫僧系统的业务逻辑?既然贫僧改了,就肯定会考虑业务逻辑的,难道施主以为贫僧在调换的时候不会考虑业务逻辑?
第五,其实贫僧这边的问题已经解决了====兄弟,难以重现的问题,你更改完代码后,并不能直接说“解决了”,说早了。
要等下次同样情况出现,你的代码确实挺过去了,才算解决了。
事实证明已经解决了,施主非要一口咬定有问题,贫僧无话可说。毕竟施主只是从文中了解了大概,因此产生错误的想法也是可以理解的。
如果博主的程序,不是 web 程序,而是自行管理线程,那么 web 服务器软件的一个线程池概念,可供参考。
web 服务器一般会配置最大并发线程数限制,如果大量用户请求,来不及处理,则 web 服务器会对后面的请求说:"我现在很忙,请稍后再试",并不实际运行。
这样的原理是:如果同时处理很多任务,可能 CPU 100% 并且每个任务处理时间都很长,比如3 小时,用户等不及,都会直接退出、或者重试,这样运行出来的结果,谁也看不到,毫无意义。
还不如,只并发运行一定量的任务,对其它用户说"我现在很忙",这样至少有一部分用户仍然能正常使用。
感觉博主没有用线程池。
-------------------------------------------
这个怎么感觉在秀线程池?难道施主以为贫僧不懂线程池?呵呵。况且,贫僧的程序很显然是web程序,因此下面的假设都不成立。不知道施主一直假设这些是想说明什么?说明贫僧不懂并发,不懂死锁,不懂线程池?如果是这样的话,贫僧承认便是。施主也不要再在这里误导别人了。
施主貌似不明白什么叫死锁,死锁是两个进程或线程由于互相等待对方的资源而永久等待的情况 === 博主貌似不知道什么叫数据库死锁,请点击我31楼提供的两个链接。
第三,如果代码的逻辑或者事务管理是有问题的,那么在测试的时候,或者上一次上线的时候,就应该出现问题===错误,有的问题较难重现,开发、测试时未必能发现。...
这个不知道施主在说什么,貌似有点胡搅蛮缠。测试就代表要发现所有问题吗?这只是贫僧思考的一个过程。不知道施主想表达什么意思?
我说的的是,你原文"如果代码的逻辑或者事务管理是有问题的,那么在测试的时候,或者上一次上线的时候,就应该出现问题"是在逻辑上是错误的,这个难道很难理解?
既然贫僧改了,就肯定会考虑业务逻辑的,难道施主以为贫僧在调换的时候不会考虑业务逻辑? =====这样的代码 100% 有问题!
public void methodWithAProblem(){
XXXDao.update();
sendMessage();
}
毕竟施主只是从文中了解了大概 =====呵呵,不只我一个人没有看明白。
施主貌似不太懂Java的web服务器和JVM,估计不是搞Java的吧。线程池在Java的web服务器当中是自带的,而且JVM可以限制栈大小,就相当于限制线程数量。
施主的链接贫僧就不看了,网络上很多这种误导的文章,估计施主就是被误导了。贫僧还是更相信百度百科以及书中的解释,如果施主死活不承认死锁的产生原因和条件,那贫僧无话可说。
我说的的是,你原文"如果代码的逻辑或者事务管理是有问题的,那么在测试的时候,或者上一次上线的时候,就应该出现问题"是在逻辑上是错误的,这个难道很难理解?
施主在挑字眼的毛病。。。这个只是一个思考过程,不是结果。贫僧没有说测试没问题,程序就OK了。无数次上线都是测试后上线的,但是上线还是会有问题,难道贫僧不懂这个道理?施主何必在此搞得好像别人都不懂似的?
既然贫僧改了,就肯定会考虑业务逻辑的,难道施主以为贫僧在调换的时候不会考虑业务逻辑? =====这样的代码 100% 有问题!
public void methodWithAProblem(){
XXXDao.update();
sendMessage();
}
毕竟施主只是从文中了解了大概 =====呵呵,不只我一个人没有看明白。
呵呵,好像还真是就你一个人没看明白。。。至于你说的不是一个人,估计是指14楼吧。那请施主自己问14楼吧。看看14楼是不是觉得业务逻辑有问题。
施主你可真逗,quartz在系统里都用烂了。。。难道施主以为贫僧不知道quartz ?所以贫僧就说了,施主了解的只是系统的一方面,毕竟文章就这么长,难道贫僧要把整个项目详细介绍一遍?所以贫僧才说,施主莫要根据文中的内容去揣测系统,这是不科学的。
况且quartz大部分时候用于晚上大批量跑批。这里严格意义上来讲,是个及时的webservice服务,需要及时通信,只不过加了层MQ而已。和quartz 如何能扯上关系?
施主,感觉你工作经验还是不足。有现成的框架,为何要自己写线程池,为了秀功力?这只是幼稚的做法。照施主这么说,我们岂不是要自己写web服务器,自己写应用平台替代spring,自己写MVC框架,自己写ORM框架?
施主以为贫僧不知道Java的并发包?又或者施主以为贫僧没看过并发包的API文档?
博主可真逗。
还百度百科,呵呵。搞技术的人,都知道要用 google 查资料的。
还说知道 quartz! 知道 quartz 的人,会在 java web 程序中创建新 thread? 开什么国际玩笑呀!
实时的东西,需要创建新 thread来运行?有趣呀,不懂呀,太高级了,非我等俗人所能理解。
哈哈,施主又在秀google?施主觉得贫僧不用google?贫僧还是只能呵呵了。
贫僧何时在程序中创建thread?施主又在胡搅蛮缠了吧。
你知不知道这些个名词,又有何用?用得不对,呵呵,知道又有什么用?
拜托博主,不要动不动就喷"感觉你工作经验还是不足",很无聊。我的工作经验么,呵呵,比我工作经验长、还在搞技术的的 IT人,整个中国也不多了。
--------------------
所以呢?施主发个API链接又有何用?你知不知道这个链接又如何?
贫僧也拜托施主,不要拿自己的那一套东西强加在别人身上,也不要妄加揣测别人的东西,施主有很多东西都是自己YY的而已,就比如刚才那个创建thread,这个不知道施主从哪里看到的?
还有施主说贫僧改了次序业务逻辑不对,这个施主又是从哪里看出来的?
等等
贫僧说的工作经验其实更多的是想表达的是个人的境界,贫僧觉得年数与工作经验其实没什么关系。至于贫僧为何说施主停留在新人的境界,是因为很多新手的一个典型表现,有很多都有,“我为什么要用框架,我自己能写,当然是我自己写的好了。”这样的思想。
施主真的是做Java WEB?
对于web服务器来说,web服务器的线程池只是有一个保底线程数,如果超过这个数就会创建新的线程,当然了,会有一个上限。
客户的请求数量如果超过线程的缓存个数,web服务器自然会自动创建新的线程,这个施主难道不知道?
如果施主是做的Java WEB,怎么会不知道“unable to create new native thread”这个问题的原因?要吗就是施主不太清楚JVM的机制与参数吧?
对于施主这种宁可相信网络上的东西,都不相信书中的解释的人来说,才是真的可怕。。。
施主还是不懂,线程池的原理不需要多说,贫僧简单的描述下。
比如缓存(或者说初始化时池的大小)10个线程,上限100。当10个不够用的时候,就会创建第11个,第12个,等等一直到100最高。
但是在创建的过程中(比如创建到第90个),如果内存(Xss可以控制栈大小,调小点可以增加线程上限数量)不够了,JVM就会爆出来 unable to create new native thread 这个异常。施主还是自己好好看看JVM的机制与参数吧,建议读一下JVM相关的书籍,然后看看openjdk的源码再来说话。
至于罢手,是施主不罢手而已,说好的闪人呢?
忽然间冒出的想法,就打开了施主的博客,准备瞄一瞄。进去才发现施主的胡搅蛮缠功底如此了得,与多人对战丝毫不虚,看来施主有这个癖好啊。施主还是活在自己的世界里吧,贫僧决定不打扰施主的世界了。
这次换贫僧罢手吧。施主还是继续活在自己的世界吧。
我去,施主非要误导别人的话,请去自己博客误导。
可怜的博主兄弟,还没有弄清楚,web 程序里,线程池是 web server 控制。
活在自己世界的施主啊,贫僧只是举个线程池的例子,贫僧早就说了Java web处理请求的线程池是web服务器提供的,是施主非说贫僧的系统没用线程池,还列出个API链接,让贫僧自己写。OK?
,如果是内存不够了,那可以调整 JVM 参数,或者查哪些地方可能导致内存没有释放掉。博主的文章,明显不是这个思路。呵呵。
如果你配置线程池最大 100个,那么就应该可以到 100 线程不报错。一旦报错,要么你不能配置线程池为 100个,你的服务器没有这么强;要么,有内存没有释放,要去调查。
是谁跟施主说的写100个,就要到100个不报错?难道又是网上的某篇文章?
要按这个逻辑,贫僧写上10000个,岂不是10000个也不能报错?
博主自称问题已解决,恕我愚昧,没有看出来了怎么解决了。
施主活在自己的世界,当然看不出来。
还是那句话,如果施主非要说贫僧的问题没解决,那施主就这么认为就是,贫僧说了,不想打扰施主的世界,施主认为没解决,就是没解决就是了,但是请走人?
或者不走也可以,请不要再谈技术方面的问题误导他人,施主可以说贫僧扣帽子什么的,类似于施主下面的一条评论,对贫僧进行人身攻击,贫僧不屑与你计较。但请不要再讨论技术问题,拿自己的世界误导他人,就算误导,请施主去自己的博客开博文。
施主真是爱狡辩。从施主博客深切的看出来。
你自己都说了,写100结果不到30就报错了,就得调小上限数。这是没错的。
你上面又说写100个就应该到100个不报错。自己仔细看看你说的话吧,孩子。都自己打自己巴掌了。
嗯,整个方法是一个事务,为了消息发送失败回退上面的事务,贫僧觉得之前写这段代码的同事应该是这个目的。
其实说到底,锁出现是因为贫僧系统里这里的更新和发送次序应该调换一下,这里已经处理过了。而消息发送失败,是A组的服务端没有及时处理消息。
按正常的处理逻辑,如果是从数据库表中读数据,发送消息后,根据发送结果是成功还是失败,应更新数据状态。所以应该是完整的 transaction.
特别需要指出的是,先更新状态成成功,再发送消息,是不可取的。
因为发送消息未必成功,没有人能保证网络 100% 可靠,另程序运行总有可能会出运行时异常。
施主自己理解有误就爱说别人看不懂,哈哈。真是和施主博客里的风格一模一样。
最大上限是人为设置的,换句话说,你写100还是写10000,服务器是不会管的。说到底,要想线程池上限合理,只能人为去控制。你却说设100就要到100不报错,你的设置都是这么准?系统的适宜参数都是在运行中根据用户和服务器压力等因素动态调整的,你敢保证你每次写的数目都能精准保证线程上限刚刚好?真是幼稚。
从你博文里写个where 1=1还沾沾自喜,跟别人狡辩就能看出来多么幼稚。贫僧还是那句话,误导请自己开博文吧,别在这里误导。
还在狡辩,文中都说了,次序调换以后,锁已经消失了,这点DBA组已经证实。锁一旦消失,就不会有线程再无故挂起,unable create thread自然就消失了。
根本原因不是上限数的问题,那只是一种表现。按照你说的意思,难道贫僧公司里百十个系统都要精确测试上限数?就算你测出来,如果换个服务器配置,你又得再测一遍,你以为这么大的人力物力以及时间是随便可以花费的?你觉得这么多工作就为了测试一个精确的上限数是值得的?
况且不论你的上限数是否精准无比,不精确,后台会出现unable的异常,无法响应客户请求,精确了,web服务器会把用户请求挂起,直到有线程被释放才能处理。两种情况对于用户来说,系统都是不可用的。所以大部分情况下,都是根据个人经验设置一个差不多的上限即可,最重要的是,没必要那么精确,像你说的精确到1个数字都不能差。
所以说施主幼稚,做技术追求完美是对的,但是过于追求不必要的完美就是幼稚了,孩子。
还是那句话,不要在这误导别人了,不服就自己开博文批判贫僧,或者表达你的观点,一直在这里咬人算怎么回事?看你博客里说什么Fish Li删你回复,哈哈。贫僧现在特别理解Fish Li。咬人的疯狗,估计都不太会欢迎。
真是越来越理解FishLi了,不能惹小人。不服去开博文吧,别在这里咬了。贫僧没时间跟你这种人耗,直言不讳,来一条删一条。你还真是第一个在园子里让贫僧忍无可忍到删你评论的人。贫僧已经让步,你可以人身攻击贫僧,但是你还在讨论技术问题误导他人,实在不可饶恕。你误导别人,贫僧管不着,毕竟网络上是自由发表观点的。但是在贫僧一再劝阻下还非在贫僧的博客里误导,搞的贫僧得一直疲于修正你的观点,避免误导他人。抱歉,贫僧没你这么悠闲,也不允许自己的博客出现这种垃圾观点。
站内搜索
用户中心
用户名: | 登录 | 注册 | |
密 码: | ||
用户名支持字母,数字,下划线和中文 |
最新评论
随机推荐
(十五)建造者模式详解...
左潇龙2013-08-16
一次sql性能的简单优...
左潇龙2015-06-08
记录2015年年初跳槽的...
左潇龙2015-03-25
(十九)组合模式详解
左潇龙2013-08-16
设计模式大杂烩(24种...
左潇龙2013-08-17