精华内容
下载资源
问答
  • 在SpringBoot项目中通常我们没有处理并发问题,但是使用项目本身还是支持一定的并发量,因为在SpringBoot中内嵌Tomcat容器,而Tomcat在spring-configuration-metadata.json文件中设置了关于并发的默认配置: ...

    SpringBoot项目中通常我们没有处理并发问题,但是使用项目本身还是支持一定的并发量,因为在SpringBoot中内嵌Tomcat容器,而Tomcatspring-configuration-metadata.json文件中设置了关于并发的默认配置:
    在这里插入图片描述

    {
      "name": "server.tomcat.max-connections",
      "description": "“服务器在任何给定时间接受和处理的最大连接数。",
      "defaultValue": 8192
    },
    {
      "name": "server.tomcat.accept-count",
      "description": "当所有可能的请求处理线程都在使用时,传入连接请求的最大队列长度。",
      "defaultValue": 100
    },
    {
      "name": "server.tomcat.max-http-form-post-size",
      "description": "http使用Post请求时候的表达最大容量。",
      "defaultValue": "2MB"
    },
    {
      "name": "server.tomcat.max-keep-alive-requests",
      "description": "在连接关闭之前可以管道化的最大HTTP请求数。",
      "defaultValue": 100
    },
    {
      "name": "server.tomcat.threads.max",
      "description": "工作线程的最大数量。",
      "defaultValue": 200
    },
    {
      "name": "server.tomcat.threads.min-spare",
      "description": "工作线程的最小数量。",
      "defaultValue": 10
    },
    {
      "name": "server.servlet.session.timeout",
      "description": "会话超时。如果未指定持续时间后缀,将使用秒。",
      "defaultValue": "30m"  // 这里m表示分钟,s表示秒
    },
    

    在默认配置下:
    在默认情况下当连接数超过8192时,就会出现拒绝连接。
    最小线程数量为10,最大线程数量为200,等待队列长度设置为acceptCount=100,当可分配的线程数全部用完之后,后续的请求将进入等待队列等待,等待队列满后则拒绝处理。

    而这些默认配置可以在配置文件中进行修改,比如:

    ## 服务器最大连接数,默认8192
    server.tomcat.max-connections=10000
    ## 等待队列长度,默认100。
    server.tomcat.accept-count=1000
    ## 最大工作线程数,默认200。(4核8g内存,线程数经验值800。)
    server.tomcat.threads.max=800
    ## 最小工作空闲线程数,默认10。(适当增大一些,以便应对突然增长的访问量)
    server.tomcat.threads.min-spare=100
    

    可以用Jmeter测试工具来测试下配置前后的并发量的性能,比如我这里先使用默认配置:
    在这里插入图片描述
    Jmeter怎么使用可以查看博客:Jmeter教程(一) - 入门

    配置服务器,然后重启后,在运行以下Jmeter压力测试工具:
    在这里插入图片描述
    其中:

    Label:取样器名称(或者是事务名)。
    #Samples:取样器运行次数(提交了多少笔业务)。
    Average:请求(事务)的平均响应时间,单位为毫秒。
    Min:请求的最小响应时间,单位为毫秒。
    Max:请求的最大响应时间,单位为毫秒。
    Std.Dev:响应时间的标准方差。
    Error%:事务错误率。
    

    在第二次中,提交次数为第一次的3倍:

    • 平均响应时间小于3倍,大约为2倍,也就是说直观上来说加快了效率;
    • 请求的最小响应时间变得更小了,请求的最大响应时间这里却增大了,所以可能设置的配置值不合理。
    • 总的来说就是确实在略微变大服务器的并发配置的时候,效率上有一些提升。

    我们知道在Http1.1之后就默认支持TCP长连接,已达到连接复用,提高资源复用率和请求效率。而我们通常在请求的时候适用的请求头部字段为Connection:keep-alive。如果说用户在一次请求后,并没有其余的操作了,而使用长连接来保持这个请求,很明显这里就会导致资源的浪费问题。

    那么,我们可以在后台设置其超时时长,可以从两个方面设置:

    • session超时,我们知道默认的session超时为30分钟,所以我们可以设置一个较短的时间来达到断开连接的目的;可以在用户登录成功后,就设置一个Session的超时时长,比如:session.setMaxInactiveInterval(6 * 60); // 默认单位为秒,这里为6分钟。
    • keep-alive超时,每个连接可以设置其超时时长,可以参考博客:springboot 长连接 keepalive 记录
    展开全文
  • 接下来, 在分析高并发事务的问题时, 你可能已经了解了一些关于锁的概念, 但是在分析这些问题的时候, 先不要带入锁的概念, 本小节只会列出问题, 并直接告诉你各个问题是使用事务隔离性的哪个隔离级别解决掉的, 锁是...

    事务的概念

    事务 可以理解为一个 独立的工作单元, 在这个独立的工作单元中, 有一组操作; 放在事务(独立工作单元)中的多个操作, 要么全部执行成功, 要么全部执行失败。

    不免俗套, 这还是通过最经典的银行转账应用来解释一下

    假设有两个角色 'Iron Man'(余额500), 'Wolverine'(余额15), 现在 Iron Man 通过该银行应用给 Wolverine 转账100元, 那么本次转账操作至少需要三个步骤:

    检查`Iron Man`余额`>=100`元

    从`Iron Man`余额中`-100`元

    给`Wolverine`余额`+100`元

    注意: 上面的三个步骤的操作必须打包在一个事务中, 从而可以作为一个 独立的工作单元 来执行。在这个 独立工作单元(即事务) 中的这三个操作, 只要有任何一个操作失败, 则事务就整体就是失败的, 那就必须回滚所有的步骤。

    假设第二步操作成功, 但是第三步操作失败, 那么整个事务也就应该是失败的, 那就必须将第二步的操作也回滚。(到这里我们也看到了事务最基本的特性之一: 保证数据的一致性)

    要知道, 在真实的高并发场景下, 事务需要做的事情其实很多很多, 因为高并发会出现很多意想不到的问题, 接下来会分析这些问题。

    事务的ACID特性

    在分析高并发事务的问题前, 我们要先知道事务的几个标准特性, 因为一个运行良好的事务处理系统必须具备这些标准特性, 而且这些问题的解决离不开事务的这几个标准特性!!!

    Atomicity 原子性

    一个事务必须被视为一个不可分割的最小工作单元, 整个事务中的所有操作要么全部提交成功, 要么全部失败回滚。对于一个事务来说, 不能只成功执行其中的一部分操作, 这就是事务的原子性。

    Consistency 一致性

    虽然可数据表中的数据可能一直在变化, 但是事务的一致性特性会保证 数据库总是从一个一致性的状态 转换到 另一个一致性的状态;

    比如在之前的转账例子:

    转账前的一致性状态是: 'Iron Man'(余额500), 'Wolverine'(余额15)

    转账成功后的一致性状态是: 'Iron Man'(余额400), 'Wolverine'(余额115)

    转账如果失败的话, 一致性的状态应该回滚到转账前的状态: 'Iron Man'(余额500), 'Wolverine'(余额15)

    Isolation 隔离性

    通常来说, 一个事务所做的修改在最终提交以前, 对其他事务是不可见的;

    比如在之前的转账例子中, 在执行完成第二步, 但是第三步还没开始的时候, 此时有另一个账户汇总的程序开始运行, 那么这个程序所拿到的A账户余额应该是没有被 -100 的余额才对

    后面我们还会详细讨论事务隔离性的 隔离级别, 到时候就知道这里为什么说通常来说对其他事务是不可见的; (也就是还有特例, 比如最低隔离级别 READ UNCOMMITTED, 对其他事务的可见就造成了脏读问题的出现)

    事务有四种隔离级别(从低到高: READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE)

    Durability 持久性

    一旦事务被最终提交, 则在事务这个独立单元中的所有操作所做的修改将会 `永久保存到数据库中`; (这里所说的`永久`应该可以理解为 被事务修改的数据 是真正存放到了表中, 而不是存放在了诸如临时表之类的地方)

    高并发事务的问题

    在并发量比较大的时候, 很容易出现 多个事务同时进行 的情况。假设有两个事务正在同时进行, 值得注意的是: 它们两者之间是互相不知道对方的存在的, 各自都对自身所处的环境过分乐观, 从而并没有对自己所操作的数据做一定的保护处理, 所以最终导致了一些问题的出现;

    接下来, 在分析高并发事务的问题时, 你可能已经了解了一些关于锁的概念, 但是在分析这些问题的时候, 先不要带入锁的概念, 本小节只会列出问题, 并直接告诉你各个问题是使用事务隔离性的哪个隔离级别解决掉的, 锁是解决方案, 如果带入锁的概念, 是无法去分析这些问题的。所以本节不需要带入锁!

    [下一篇文章]()将会分析这些解决方案(各隔离级别)具体是如何解决问题的。

    脏读

    如果mysql中一个事务A读取了另一个并行事务B未最终提交的写数据, 那事务A的这次读取就是脏读。(因为事务A读取的是'脏数据', 是'非持久性'的数据)

    之所以说是'非持久性数据', '脏数据', 是因为事务B最终可能会因为内部其他后续操作的失败或者系统后续突然崩溃等原因, 导致事务最终整体提交失败, 那么事务A此时读取到的数据在表中其实会被回滚, 那事务A拿到的自然就是脏的数据了。

    图示:

    45be3b5d7411389a5e1404cbfdd30fe3.png

    事务A在T4阶段读取库存为20, 这个库存其实就属于脏数据, 因为事务B最终会回滚这个数据, 所以如果事务A使用库存20进行后续的操作, 就会引发问题, 因为事务A拿到的数据已经和表中的真实数据不一致了。

    那么这个问题如何解决呢?

    在MySQL中, 其实事务已经用自身特性(隔离性的 -- READ COMMITED或以上隔离级别)解决了这个问题;

    **`READ COMMITED`级别保证了, 只要是当前语句执行前已经提交的数据都是可见的**。注意和`REPEATABLE READ`级别的区!!!

    不可重复读

    假设现在上面的 脏读问题 已经被完全解决了, 那就意味着事务中每次读取到的数据都是 持久性 的数据(被别的事务最终 提交/回滚 完成后的数据)。

    但是你还需要知道的是: 解决了脏读问题, 只是能保证你在事务中每次读到的数据都是持久性的数据而已!!!!

    如果在一个事务中多次读取同一个数据, 正好在两次读取之间, 另外一个事务确实已经完成了对该数据的修改并提交, 那问题就来了: 可能会出现多次读取结果不一致的现象。

    1165b94a7ce35f487719a39a5d463ef5.png

    那么这个问题如何解决呢?

    在MySQL中, 其实事务已经用自身特性(隔离性的 -- REPEATABLE READ或以上隔离级别)解决了这个问题;

    REPEATABLE READ级别保证了, 只要是当前事务执行前已经提交的数据都是可见的。注意和READ COMMITED级别的区!!!

    幻读 (间隙锁)

    对于幻读, 可能很多人经常和不可重复读区分不开, 详情可以参考本人写的此篇文章https://segmentfault.com/a/11...

    更新丢失

    最后聊一下高并发事务的另一个问题 -- 丢失更新问题, 该问题和之前几个问题需要区分开, 因为解决方案不是一类!

    第一类丢失更新: A事务撤销时, 把已经提交的B事务的更新数据覆盖了。

    5095c8733d3a998171ce05cd0d52cf2f.png

    不过, 通过后面MVCC相关文章最后的小结你会了解到, 这类更新丢失问题是不会出现的, 因为InnoDB存储引擎的隔离级别都使用了排他锁, 即使是 MVCC也不是纯MVCC, 也用到了排他锁! 这样的话事务A在未完成的时候, 其他事务是无法对事务A涉及到的数据做修改并提交的。

    第二类丢失更新: A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失。

    9554a81f592dc669a5fd0b6c7896b335.png

    此类更新丢失问题, 无法依靠前三种隔离级别来解决, 只能用最高隔离级别 Serializable 或者手动使用乐观锁, 悲观锁来解决。

    最高隔离级别Serializable在实际应用场景中并不被采用, 对于手动使用乐观锁, 悲观锁的方案, 将会在以后关于锁的文章中一并给出!

    参考资料:

    《高性能MySQL》

    展开全文
  • 线程安全 add 方法使用的是 synchronized 修饰,但其效率并不 //List<String> list = new Vector<>(); //2. Collections 工具类转换成线程安全 //List<String> list = Collections....
    /**
    	   * 解决ArrayList的三种方法
    	   */
    	        //1. 线程安全 add 方法使用的是 synchronized 修饰,但其效率并不高
    	        //List<String> list = new Vector<>();
    	        //2. Collections 工具类转换成线程安全
    	        //List<String> list = Collections.synchronizedList(new ArrayList<>());
    	        //3. 复制在写入  , 内部源码使用新锁 Lock(推荐使用)
    	        List<String> list = new CopyOnWriteArrayList<>();
    
    	        for (int i = 0;i<20;i++){
    	            new Thread(()->{
    	                list.add(UUID.randomUUID().toString().substring(0,5));
    	                System.out.println(list);
    	            }).start();
    	        }
    
    展开全文
  • 并发安全问题概述

    2021-08-09 15:39:06
    并发安全问题概述 什么时候数据在多线程并发的环境下会存在安全问题呢? 三个条件: 条件1:多线程并发。 条件2:有共享数据。 条件3:共享数据有修改的行为。 满足以上3个条件之后,就会存在线程安全问题。 ...

    并发的安全问题概述

    1. 什么时候数据在多线程并发的环境下会存在安全问题呢?

      1. 三个条件:
        1. 条件1:多线程并发。
        2. 条件2:有共享数据。
        3. 条件3:共享数据有修改的行为。
        4. 满足以上3个条件之后,就会存在线程安全问题。
    2. 怎么解决线程安全问题呢?

      1. 当多线程并发的环境下,有共享数据,并且这个数据还会被修改,此时就存在线程安全问题,怎么解决这个问题?
        1. 线程排队执行。(不能并发)。用排队执行解决线程安全问题。这种机制被称为:线程同步机制。
        2. 专业术语叫做:线程同步,实际上就是线程不能并发了,线程必须排队执行。
      2. 怎么解决线程安全问题呀?使用“线程同步机制”。
    3. 线程同步就是线程排队了,线程排队了就会牺牲一部分效率,没办法,数据安全第一位,只有数据安全了,我们才可以谈效率。数据不安全,没有效率的事儿。

    4. 说到线程同步这块,涉及到这两个专业术语:

      1. 异步编程模型:

        线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,谁也不需要等谁,这种编程模型叫做:异步编程模型。其实就是:多线程并发(效率较高。)异步就是并发。

      2. 同步编程模型:

        线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,两个线程之间发生了等待关系,这就是同步编程模型。效率较低。线程排队执行。同步就是排队。

    5. Java中的三大变量与线程的问题?

      1. 成员变量:

        1. 实例变量:在堆中。
        2. 静态变量:在方法区。
      2. 局部变量:在栈中。

      3. 以上三大变量中:

        局部变量永远都不会存在线程安全问题。因为局部变量不共享。**(一个线程一个栈。)**局部变量在栈中。所以局部变量永远都不会共享。

        实例变量在堆中,堆只有1个。静态变量在方法区中,方法区只有1个。堆和方法区都是多线程共享的,所以可能存在线程安全问题。

      4. 局部变量+常量:不会有线程安全问题。

      5. 成员变量:可能会有线程安全问题。

    6. 局部变量对于线程安全的类与线程不安全的类如何选择:

      1. 对于StringBuilder和StringBuffer类:

        建议使用:StringBuilder。因为局部变量不存在线程安全问题。选择StringBuilder。StringBuffer效率比较低。

      2. ArrayList是非线程安全的。

      3. Vector是线程安全的。

      4. HashMap与HashSet是非线程安全的。

      5. Hashtable是线程安全的。

    7. 以后开发中应该怎么解决线程安全问题?

      1. 是一上来就选择线程同步吗?使用synchronized吗?

        不是,synchronized会让程序的执行效率降低,用户体验不好。系统的用户吞吐量降低。用户体验差。在不得已的情况下再选择线程同步机制。

      2. 解决方案:

        1. 第一种方案:尽量使用局部变量代替“实例变量和静态变量”。
        2. 第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样实例变量的内存就不共享了。(一个线程对应1个对象,100个线程对应100个对象,对象不共享,就没有数据安全问题了。)
        3. 第三种方案:如果不能使用局部变量,对象也不能创建多个,这个时候就只能选择synchronized了。线程同步机制。
    展开全文
  • redis之所以能解决高并发的原因是它可以直接访问内存,而以往我们用的是数据库(硬盘),提高了访问效率,解决了数据库服务器压力。为什么redis的地位越来越高,我们为何不选择memcache,这是因为memcache只能存储字符串...
  • 但是发现当业务量突然加大的情况下,突然发现,线上服务器出现CPU居不下,服务接口调用速度越来越慢,研究JVM日志分析,发现出现很多的RUNNABLE问题: ...
  • 今天就来为大家介绍一下java高并发下怎么保障数据安全以及有哪些办法?一起看看吧。一、首先说一下怎么保障数据安全。我们知道在多线程写入同一个文件的时候,会存现“线程安全”的问题。秒杀和抢购的场景中,还有...
  • 大家好,我是冰河~~ 在【精通高并发系列】的《实践出真知:全网最强秒杀系统架构解密!!》一文中,冰河详细的阐述了高并发秒杀系统的架构设计,也简单提到了如何扣减商品的库存。 也许不少小伙伴会问:扣减商品的...
  • 在cache层的高速缓存,MySQL的主从复制,读写分离的基础上,这时MySQL主库的写压力开始出现瓶颈,而数据量的持续猛增,由于MyISAM使用表锁,在高并发下会出现严重的锁问题,大量的高并发MySQL应用开始使用InnoDB...
  • 01、说一个基本的问题高并发下的操作mysql数据产生的数据冲突问题 --并发控制:当程序中可能出现并发的情况时,就需要保证在并发情况下数据的准确性,以此确保当前用户和其他用户一起操作时,所得到的结果和他单独...
  • 高并发下如何保证单例模式的线程安全性呢?如何保证序列化后的单例对象在反序列化后任然是单例的呢?什么是单例模式?在文章开始之前我们还是有必要介绍一下什么是单例模式。单例模式是为确保一个类只有一个实例,并...
  • 高并发中的集合类的主要问题就是线程安全问题,我们可以通过JDK中集合类的发展来观察集合类都有哪些问题以及如何解决。 第一代线程安全集合类 Vector、HashTable 是线程安全的:使用synchronized修饰方法。 缺点:...
  • JVM创建对象时的并发安全问题CAS加失败重试的原理:TLAB(Thread Local Allocation Buffer): CAS加失败重试的原理: CAS为compare and swap缩写,意为比较并交换 问题:为对象分配空间的时候,出现了两个线程抢占...
  • 好处:在高并发情况下,你会需要一个全局锁来保证整个平衡树的线程安全,而对于跳表,只需要部分锁即可。在高并发下,就可以拥有更好的性能,而查询性能而言,跳表的时间复杂度也是 O(logn),所以在并发数据结构中...
  • 原标题:更新库存时,你是如何用mysql锁解决高并发问题的文章内容利用Mysql的锁来解决高并发问题,先看没有利用事务的时候并发的后果创建库存管理表CREATETABLE`storage`(`id`int( 11) unsignedNOTNULLAUTO_...
  • Mysql高并发解决方案

    2021-02-05 21:20:45
    Mysql高并发解决方案 前言 随着近些年来分布式的应用,其伴随而来的是系统的数据量也越来越大,为了可以提升系统的整体性能,我们对以Mysql为代表的关系型数据库也提出了“分布式”的要求。 MyCat入门可参考我的博客...
  • 随着业务量的增加,目前的系统都朝着高并发、高可用的方向发展,同时带来了分布式数据的一致性问题。例如: 数据库主从架构、读写分离,存在访问时的数据一致性问题 为了进一步提高并发量,在数据库上层又引入一层...
  • 一、ArrayList并发安全 二、集合类Set并发安全 三、Map线程不安全 写在前面 很多小伙伴使用ArrayList、HashMap等集合类,大多数情况下都不是用在多线程环境下的。 那么,多线程环境下是否仍然可以使用...
  • 在多并发情况下使用SimpleDateFormat需格外注意 SimpleDateFormat除了format是线程不安全以外,parse方法也是线程不安全的。parse方法实际调用alb.establish(calendar).getTime()方法来解析,alb.establish(calendar...
  • 高并发点赞项目案例

    千次阅读 2021-02-04 12:35:14
    高并发点赞问题 高并发请求下,服务器频繁创建线程。 高并发请求下,数据库连接池中的连接数有限。 高并发请求下,点赞功能是同步处理等。 解决办法: 第一步 我们通过引入Redis缓存避免高并发写数据库而造成...
  • 高并发相信大家都不陌生了,高并发是java核心技术之一,下面我们就来了解一下高并发下如何做到安全的修改同一行数据吧。乐观锁,采用了相对于“悲观锁”更为宽松的加锁机制,它的实现就是:这个数据所有请求都有资格...
  • 高并发下实现线程安全的i++操作 一、使用synchronized 这个比较简单,就是在进行i++操作时,直接使用synchronized加锁,也可以使用Lock加锁,本质都是一样的(锁原理不同),最终都是通过加锁来保证多线程安全的。 ...
  • 列举Java高并发面试题附答案解析

    千次阅读 2021-03-17 21:45:05
    Java高并发面试题是程序员面试过程中的必修课,只有熟练掌握这些技术要点,在我们的学习中才会脱颖而出,在这里,达内石家庄Java培训老师作深入解答。Java并发面试题附答案1. 什么是原子操作?在Java Concurrency API...
  • mysql高并发写入

    2021-01-19 02:47:55
    "btn2":"数据库紧急救援服务","tip":"还有更多专家帮助您解决云上业务问题:立即查看","btn1":"云上数据库优化服务","link2":"https://www.aliyun.com/service/databaserescue","title":"数据库专家服务"}],"search...
  • 余额操作在大多数系统都是不可缺少和不允许出现问题的 如何修改余额 , 这个问题可能在实际项目中 没那么简单;如何修改余额假设一个用户数据 :id⇒12 | user_name⇒放放 | fee⇒ 30 | updated_at ⇒ 2019-09-06 15:51...
  • 记录之前公司微信投票...高并发页面:投票主页,选手详情页面(含投票活动信息,选手信息,票数,礼物等) 压力并发:单机+单库 (8pvc 16g) QPS<150 公司要求:QPS 3000+,投票时间一个月,不缺钱 优化方案: 一:架构修改 1:数
  • redis之所以能解决高并发的原因是它可以直接访问内存,而以往我们用的是数据库(硬盘),提高了访问效率,解决了数据库服务器压力。 为什么redis的地位越来越高,我们为何不选择memcache,这是因为memcache只能存储字符...
  • 0.源代码github-简易高并发框架注:本篇博客知识来自于网课。1.问题来源以及w对于一个题库系统。考试组要有批量的离线文档要生成。题库组批量的题目要进行排重,且要根据条件批量修改题目内容。对于痛点:批量任务...
  • 有一种对象一旦发布了,那么他就是安全对象,这就是不可变对象。不可变对象需要满足的条件:对象创建以后其状态就不能修改对象所有的域都是final类型对象是正确创建的(在对象创建期间,this引用没有逸出)final关键字...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 398,695
精华内容 159,478
关键字:

高并发安全问题