问答详情
源自:1-2 高并发优化分析(下)

数据库只能解决事务但是无法解决并发量大的问题

我有个疑问,数据库的连接数是有限的(几百个?),也就是从服务层透入1000个QPS数据库就要挂了,那么如何应对成千上万的秒杀访问量?剩下的几万个访问请求不就挂了,如何做到马上回复请求?你说的4万QPS是如何测出来的呢?还有一点,如果数据库是集群的,每太数据库保存的是一模一样的数据,又如何做事务控制,如何做各个主数据库间的数据同步,还有主从数据库间的数据同步?

提问者:qq_空山听雨_0 2017-03-10 11:21

个回答

  • 精学而为
    2018-01-10 17:20:34

    CDN的特点和相关信息:
    使用CDN 获取公共js http://www.bootcdn.cn/
    CDN特点:CDN是和请求对应的资源是不变化的,比如静态资源和JavaScript(URL对应的结果不变)
    CDN是什么:
         1:CDN是(内容分发网络)加速用户获取数据的系统,例如视频资源
         2:部署在离用户最近的网络节点上 3:命中CDN不需要访问后端服务器
         4:互联网公司自己搭建或租用CDN
    使用CDN的好处:
         01 不用去官网直接下载 02 当我们的服务上线一些稳定可靠的CDN比直接发布到我们的服务器更有效
         03 CDN也是web最重要的一个加速的功能点 怎样让系统抗住很高的并发的时候CDN也是一个点

    高并发出现的点:
    1.获取系统时间(但是不用优化,访问一次内存(Cacheline)大约10ns)
    2.秒杀地址接口分析:
       1.无法使用CDN缓存
       2.但是它适合放在服务器缓存:Redis等->内存的服务器端的缓存可以抗很高的QPS,10^5/sQPS,
         现在Redis可以做集群,做了之后QPS可以达到10^6/s。
       3.为什么要做后端缓存 -> 因为后端缓存可以用我们的业务系统来控制。
         比如先访问数据库拿到秒杀的数据放到Redis缓存里面去,当一次访问的时候直接去缓存里面查找,
         缓存有就直接返回,而不去访问我们数据库了。
       4.一致性维护成本比较低:当我们秒杀的东西或秒杀的对象改变了的时候,
         我们可以修改我们的数据库,同时在改一下我们的缓存,或者干脆不改,等超时之后在改

    秒杀地址接口优化:请求地址 -> Redis[一致性维护(超时穿透到SQL语句/当SQL语句更新时主动更新)] -> SQL语句

    3.秒杀操作优化分析(是最重要的一个秒杀按钮操作):
       1.也是不能使用CDN缓存的,CDN不可能把你最核心的东西给缓存(大部分写操作或最核心的数据请求一般没办法使用CDN)
       2.后端缓存困难:库存的问题,不可能在缓存里面减库存,否则会产生数据不一致问题。所以要通过事务来保证数据的一致性
       3.一行数据竞争:热点商品,会对数据库表中的那一行数据产生大量的update减库存竞争

    1:瓶颈分析:
    update 减库存:客户端会执行update,根据拿到结果是否更新了,当我们的SQL通过网络发送给数据库时本身就有网络延迟,
                  除了网络延迟还有java GC(garbage collection,垃圾回收)操作 -> 不需要手动去回收,
                  GC自动就帮我们回收了,新生代GC会暂停所有的事务代码(Java代码)后,执行GC(一般在几十ms),
                  并且,同一行事务是做串行化的。

    ----》insert 购买明细:也会存在网络延迟和GC
    ----》commit/rollback
    也就是说如果是Java客户端去控制这些事务会有什么问题:update 减库存(网络延迟,可能的GC,GC不一定每次都出现,但一定会出现)
    --> 执行insert 购买明细(在网络延迟等待insert语句的返回,然后也可能会GC) --> 最后commit/rollback。
    当前面的这些操作都执行完之后,第二个等待行锁的线程菜能够有机会拿到这一行的锁在去执行update减库存

    特点:
       根据上面的拆分,所以QPS很好分析了 --->(我们所有的SQL执行时间 + 网络延迟时间 + 可能的GC)
       这一行数据就是当前可以执行的时间.比如时间是2ms,概念是1s之内只能有500次减库存的秒杀操作,但是对于
       秒杀系统,特别是热点系统来说其实是不能满足我们的要求的,特别是排队特别长的时候,性能会呈现指数级别下降
    得到的点是:行级锁是在commit/rollback之后释放的;
    优化方向:怎样减少行级锁持有的时间 ---> (当你update表中一行数据的时候,一定要快速的commit/rollback,
             因为其他还在等待,因为这是一个热点的数据);

    2:延迟分析:
    延迟问题是很关键的;
    优化思路:
    -把客户端逻辑放到MySQL(数据库)服务端,避免网络延迟和GC影响
    -如何放到MySQL服务端:

    --两种解决方案:
    ---定制SQL方案: 早期的阿里巴巴的天猫做了一个MySQL的源码层的修改 --->update/*+[auto_commit]*/,
       但是执行完这句update之后,会自动进行回滚(条件是:当update影响的记录数是1,它就会commit,如果等于0就会rollback)
       也就是说它不给Java客户端和MySQL之间网络延迟,然后在由Java客户端去控制commit还是rollback,而是直接用这条语句直接发过去
       告诉它是commit还是rollback。本质上也是降低了网络延迟和GC的干扰,但是成本很高 --> 需要修改MySQL源码,大公司可以这样的团队

    --使用存储过程: 整个事务在MySQL端完成;存储过程设计出来的本质就是想让我们的一组SQL组成一个事务,然后在服务器端完成,
      而避免客户端去完成事务造成的一个性能的干扰。一般情况下像是spring声明式事务或我们手动控制事务都是客户端控制事务,
      这个事务在行级锁没有那么高的竞争情况下是完全OK的,但秒杀是一个特殊的应用场景,它会在同一行中产生热点,大家都竞争同一行父,
      那么这个时候存储过程就发挥作用了,它把整个这条SQL执行过程完全放在MySQL中完成了,
      MySQL执行的效率非常高,因为我们都是通过主键去执行的,查询或更新

    优化总结:
    1:前端控制:暴露接口,按钮防重复
    2:动静态数据分离:CDN缓存,后端缓存
    3:事务竞争优化:减少事务锁时间 ---> 这是秒杀用MySQL解决秒杀问题的很重要的一个关键点;
       因为用事务有一个很大的优点是:保证原子性、隔离性、一致性、持久性。

  • 孟熙武
    2017-03-10 15:54:50

    你说的数据库同步问题,现在在做分布式数据库的时候是有中间件来对数据进行同步的,如果是主从备份的话,只需要在主库中做好事务,在主数据库中提交事务后,从数据库的数据会自动同步的,不存在你所说的数据同步的问题。