1.Java处理大并发动态请求优化的问题
Java和通用的Web服务器( Nginx或 Apache)相比,在处理大并发的HTP请求时要弱一点,所以一般我们都会对大流量的Web系统做静态化改造,让大部分请求和数据直接在 Nginx I服务器或者Web代理服务器( Varnish、 squid等)上直接返回(可以减少数据的序列化与反序列化),Java层只处理少量数据的动态请求。针对这些请求可以使用以下优化手段:
直接使用 Servlet处理请求。避免使用传统的MVC框架,这样可以绕过一大堆复杂且用处不大的处理逻辑,节省1毫秒的时间一取决于对MVC框架的依赖程度;
直接输出流数据。使用 resp. getoutputstreamo而不是 resp. get Writer)可以省掉一些不变字符数据的编码,提升性能;数据输出时,推荐使用JSON而不是模板引擎(一般都是解释执行)来输出页面。
2.同一商品被大并发读的问题
也许有读者会觉得这个问题很容易解决,无非就是将热点数据放到Tair缓存里。集中式Tair缓存为了保证命中率一般都会采用一致性Hash,所以同一个key会落到同台机器上。虽然单台Tair缓存机器也能支撑1秒30万次的请求,但还是远不足以应付大秒级别的热点商品,该如何彻底解决单点的瓶颈呢?答案是采用应用层的Localcache,即在秒杀系统的单机上缓存商品相关的数据。那么如何 Cache数据?答案是划分成动态数据和静态数据分别处理。
像商品的标题和描述这此机器上、并一直缓存到秒杀结束像库存这类动态数据会采用被动失效的方式缓存一定时间(一般是数秒),失效后再去Tai缓存拉取最新的数据。
读者可能还会有疑问,像库存这种频繁更新的数据一旦数据不一致会不会导致超卖?这就要用到前面介绍的读数据的分层校验原则了,读的场景可以允许一定的脏数据,因为这里的误判只会导致少量原本无库存的下单请求被误认为有库存,可以等到真正写数据时再保证最终的一致性,通过在数据的高可用性和一致性之间的平衡来解决高并发的数据读取问题。
3.同一数据大并发更新问题
采用 Localcache和数据的分层校验可以一定程度上解决大并发读问题,但是无论如何还是避免不了减库存这类的大并发写问题,这也是秒杀场景中最核心的技术难题。
同一数据在数据库里肯定是一行存储( MYSQL),所以会有大量的线程来竞争INNODB行锁,并发度越高时等待的线程也会越多,TPS会下降而RT会上升,数据库的吞吐量会严重受到影响。这里会出现一个问题,即单个热点商品会影响整个数据库的性能,出现我们不愿意看到的0.01%商品影响9999的商品的情况。此处的解决思路也是要遵循前面介绍的第一个原则“隔离” 把热点商品放到单独的热点库中尽管这会带来维护的麻烦(要做热点数据的动态迁移以及单独的数据库等)把热点商品分离到单独的数据库并没有解决并发锁的问题,要解决并发锁问题有以下两种办法。
第一种是在应用层做排队。按照商品维度设置队列顺序执行,这样能减少同一台机器对数据库同一行记录操作的并发度,也能控制单个商品占用数据库连接的数量防止热点商品占用太多的数据库连接。
第二种是在数据库层做排队。应用层只能做到单机的排队,但是应用层机器数量很多,用这种排队方式控制并发仍然是很有限的,如果能在数据库层做全局排队是最理想的。数据库团队开发了 MYSQL的 INNODB层上的 patch,可以做到在数据库层上对单行记录并发排队。
你可能会有疑问:排队和锁竞争不都是要等得吗,有何区别?如果熟悉 MYSQL的话,应该知道 INNODB内部的死锁检测以及 MYSQL Server和 INNODB的切换会比较耗性能, MYSQL核心团队还做了很多其他方面的网站制作优化,如 COMMIT_ON_ SUCCESS和 ROLLBACK ON FAIL的 patch,配合在SQL里面加hint,在事务里不需要等待应用层提交 COMMIT而在数据执行完最后一条SQL后,根据 TARGET AFFECT ROW结果就直接提交或回滚,这样可以减少网络的等待时间(平均约0.7毫秒)。
本文地址://www.gogoparty.cc//article/4540.html