文档章节

数据库锁和高并发系统

满小茂
 满小茂
发布于 2017/02/17 16:03
字数 1647
阅读 465
收藏 3

1.QPS      

      Web系统开发中,会有一种常见的高并发系统,对系统吞吐量要求很高,一般的管理系统用户访问量不大对高并发要求并不够,如果对用户访问量很大的系统,如电商,搜索引擎等API接口,要求会特别的高。其中QPS(Quest per seconds)会有一个计算公式:

             QPS(TPS)= 并发数/响应时间(单位秒);  

       并发数: 可以理解为单位时间内请求接口的用户数量。 系统能同时处理的request/(事务数),可以理解为网络能同时打开的通道,最直接的是Linux系统,每个进程能开启的网络通道个数,比如默认可以打开1024个网络文件描述符。

      响应时间:  一般取平均响应时间,是每个http请求从请求开始到结束花销的时间,一般取系统各个接口的平均时间。

      举个例子,我们假设处理一个业务请求平均响应时间为100ms,同时,系统内有20台Web服务器,配置MaxClients为500个(表示服务器的最大连接数目)。那么,我们的Web系统的理论峰值QPS为(理想化的计算方式):20*500/0.1 = 100000 (10万QPS)。

2.高并发系统设计

      现在的web系统基本都是基于关系型数据库的,比如互联网最喜欢的MySql系统,当高并发系统中,最简单的是使用悲观锁的方式,不管是数据库还是代码当中,但是这并不是比较好的解决方式。锁的性能是低效的。

       例子:

      当用户请求涉及数据库值修改时,多个用户同时修改值,可能会造成错误,当使用悲观锁时,每次只允许一个用户修改值,比如商品出货这个操作。

        a.查询商品剩余的数量 : select numbers from goods where good_id=${good_id}

        b.生成订单: insert into orders(user_id,good_id) values('12345',${good_id})

        c.减少商品剩余数量: update goods set numbers=numbers-1 where good_id=${good_id}

       一个商品订单操作可以分为这几步,但是如果在多线程情况下,当商品数量剩余数量为1的时候,多个用户同时下单,同时发现商品用户数量为1,都生成了一条订单,实际上商品只有一个了,造成数据不一致错误。

(1) 队列方案

         将用户的所有请求都放入放入队列中,然后消费者线程从队列中取数据,一个一个的处理,这种方式可以一定程度减少并发度,但是如果用户数量暴增,队列入队数量会远高于出队数量,最后导致系统出错,消费堵塞。

(2) 悲观锁方案

       要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。

     我们可以使用命令设置MySQL为非autocommit模式:        

 set autocommit=0;
// 0.开始事务
 begin;
// 1.查询出商品信息
 select numbers from goods where good_id=${good_id} for update;
// 2.根据商品信息生成订单
 insert into orders(user_id,good_id) values('12345',${good_id})
// 3.修改商品剩余数量减少一个
 update goods set numbers=numbers-1 where good_id=${good_id}
// 4.提交事务
 commit;

       上面的begin/commit为事务的开始和结束,因为在前一步我们关闭了mysql的autocommit,所以需要手动控制事务的提交,在这里就不细表了。

        在事务中,只有SELECT ... FOR UPDATE  同一笔数据时会等待其它事务结束后才执行,一般SELECT ... 则不受此影响。

      MySQL select…for update的Row Lock与Table Lock

     使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认Row-Level Lock,所以只有明确地指定主键或者索引,MySQL 才会执行Row lock 只锁住被选取的数据),否则MySQL 将会执行Table Lock 将整个数据表单给锁住。

      但是悲观锁并不是适用于任何场景,它也有它存在的一些不足,因为悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。如果加锁的时间过长,其他用户长时间无法访问,影响了程序的并发访问性,同时这样对数据库性能开销影响也很大,特别是对长事务而言,这样的开销往往无法承受。

(3)乐观锁方案

              

      为了减少锁竞争,我们可以采用悲观锁的方式来实现高并发操作,为需要高并发访问的数据表建立一个version字段,然后操作。

a.查询商品剩余的数量 : select numbers,version from goods where good_id=#{good_id};

b.生成订单: insert into orders(user_id,good_id) values('12345',#{good_id})

c.减少商品剩余数量: update goods set numbers=numbers-1 where good_id=#{good_id} and version=#{version}

  (4)CAS高并发系统

       Compare And Swap 为了降低锁竞争带来的低效率,我们在高并发系统中应该尽量减少锁的使用,而使用CAS无锁操作.

     独占锁是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁用到的机制就是CAS,Compare and Swap。

        在Java并发包中有这样一个包,java.util.concurrent.atomic,该包是对Java部分数据类型的原子封装,在原有数据类型的基础上,提供了原子性的操作方法,保证了线程安全。下面以AtomicInteger为例,来看一下是如何实现的。

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}
public final int decrementAndGet() {
    for (;;) {
        int current = get();
        int next = current - 1;
        if (compareAndSet(current, next))
            return next;
    }
}

 GCC4.1+版本中支持CAS的原子操作(完整的原子操作可参看 GCC Atomic Builtins

bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)

type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)

高并发队列推荐使用Disruptor,CAS无锁队列
另外秒杀抢购业务可以参考这篇博文,写得不错

 https://my.oschina.net/wangjie404/blog/815455

   

© 著作权归作者所有

共有 人打赏支持
满小茂
粉丝 73
博文 119
码字总数 131754
作品 0
成都
程序员
mysql中select for update锁的问题

mysql中select for update锁的问题 无标题2017-12-053 阅读 mysql 在日常关于资金业务的开发过程中,涉及到数据库的操作时我们经常按照一查二锁三写的套路,可能在加锁后还需要查询一些数据,...

无标题
2017/12/05
0
0
阿里P8架构师谈:MySQL行锁、表锁、悲观锁、乐观锁的特点与应用

我们在操作数据库的时候,可能会由于并发问题而引起的数据的不一致性(数据冲突)。如何保证数据并发访问的一致性、有效性,是所有数据库必须解决的一个问题,锁的冲突也是影响数据库并发访问...

Java高级技术
09/18
0
0
如此牛逼?双11背后的秘密-支付宝app双11最佳实践

近来,FF项目的运营活动越来越多,对于架构设计以及程序研发有了更高的要求,参考国内互联网公司对于营销活动app的设计思路,我们找到了最具有代表性的支付宝双11活动,阐述运营活动类高并发...

丁小晶
2016/04/20
0
0
大数据下高并发的处理详解

对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了。而并发问题是绝大部分的程序员头疼的问题,但话又说回来了,既然逃避不掉,那我们就要想想应对措...

商者
2016/07/18
33
0
探索并发编程(七)------分布式环境中并发问题

在分布式环境中,处理并发问题就没办法通过操作系统和JVM的工具来解决,那么在分布式环境中,可以采取一下策略和方式来处理: 避免并发 时间戳 串行化 数据库 行锁 统一触发途径 避免并发 在...

老先生二号
2017/07/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

io流

码农屌丝
12分钟前
0
0
SpringBoot基础篇之重名Bean的解决与多实例选择

更多Spring文章,欢迎点击 一灰灰Blog-Spring专题 当通过接口的方式注入Bean时,如果有多个子类的bean存在时,具体哪个bean会被注入呢?系统中能否存在两个重名的bean呢?如果可以,那么怎么...

小灰灰Blog
22分钟前
0
0
记录一次dubbo项目实战

一、案例说明 存在2个系统,A系统和B系统,A系统调用B系统的接口获取数据,用于查询用户列表。 二、环境搭建 安装zookeeper,解压(zookeeper-3.4.8.tar.gz)得到如下: 然后进入conf将zoo_s...

Java烂猪皮
26分钟前
0
0
拜托,别再问怎么深入学习分布式架构了!

由于分布式系统所涉及到的领域众多,知识庞杂,很多新人在最初往往找不到头绪,不知道从何处下手来一步步学习分布式架构。 本文试图通过一个最简单的、常用的分布式系统,来阐述分布式系统中...

Java架构资源分享
27分钟前
0
0
《netty入门与实战》笔记-05:心跳与空闲检测

本小节,我们一起探讨最后一个话题:心跳与空闲检测 首先,我们来看一下,客户端与服务端之间的网络会存在什么问题? 1. 网络问题 下图是网络应用程序普遍会遇到的一个问题:连接假死 连接假...

Funcy1122
33分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部