Writer:BYSocket(泥沙砖瓦浆木匠)

微博:BYSocket

豆瓣:BYSocket

一、前言

针对并发,老生常谈了。目前一个通用的做法有两种:锁机制:1.悲观锁;2.乐观锁。

但是这篇我主要用于记录我这次处理的经历,另外希望能看的大神,大牛,技师者,学长,兄长,大哥们能在评论中发表自己的看法和解决技巧等。

二、故事是这样的

一个表,暂且叫 wallet,其中3个字段是 金额。初始值为0,如下图所示:

然后我们写了一个极为简单的Controller,并写了下面的Service代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
    public void testLock(int lockId)
    {
        Wallet wallet = walletMapper.selectByPrimaryKey(4);
         
        BigDecimal one = new BigDecimal(1.00);
        BigDecimal two = new BigDecimal(2.00);
        BigDecimal three = new BigDecimal(3.00);
         
        wallet.setWalletAmount(wallet.getWalletAmount().add(one));
        wallet.setWalletAvailableAmount(wallet.getWalletAvailableAmount().subtract(two));
        wallet.setOldAmount(wallet.getOldAmount().add(three));     
         
        walletMapper.updateByPrimaryKeySelective(wallet);
    }

就简单的通过主键读取到一个对象,注意这个对象是没加锁的。也就是说,所对应的SQL如下:

1
2
3
4
SELECT
    <include refid="Base_Column_List" />
    FROM wallet
    WHERE wallet_id = #{walletId,jdbcType=INTEGER}

我这边是MyBiatis,大家应该看得懂的。然后一个增加1 一个减少2 一个增加 3。

三、测试是这样

我用了Web应用压力测试工具:Boomhttps://github.com/rakyll/boom Go编写的HTTP(S)负载生成器,ApacheBench(AB)的替代工具。Boom是一个微型程序,能够对Web应用程序进行负载测试。它类似于 Apache Bench ,但在不同的平台上有更好的可用性,安装使用也比较简单。

简单使用方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
boom -n 1000 -c 200 http://www.baidu.com
 
Options:
  -n  Number of requests to run.
  -c  Number of requests to run concurrently. Total number of requests cannot
      be smaller than the concurency level.
  -q  Rate limit, in seconds (QPS).
  -o  Output type. If none provided, a summary is printed.
      "csv" is the only supported alternative. Dumps the response
      metrics in comma-seperated values format.
  
  -m  HTTP method, one of GET, POST, PUT, DELETE, HEAD, OPTIONS.
  -h  Custom HTTP headers, name1:value1;name2:value2.
  -d  HTTP request body.
  -T  Content-type, defaults to "text/html".
  -a  Basic authentication, username:password.
  
  -allow-insecure Allow bad/expired TLS/SSL certificates.

所以我就如图进行压力测试,可见这个小工具还挺美的,这里我连接数1000,并发数100

可见后台程序报错了。什么错误呢?

1
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction

原来并发导致update死表了。数据库的数据不用看了肯定是错误的。

四、FOR UPDATE的使用

先补一下其知识:利用select * for update 可以锁表/锁行。自然锁表的压力远大于锁行。所以我们采用锁行。什么时候锁表呢?

假设有个表单products ,里面有id跟name二个栏位,id是主键。
例1: (明确指定主键,并且有此笔资料,row lock)
SELECT * FROM wallet WHERE id=’3′ FOR UPDATE;
例2: (明确指定主键,若查无此笔资料,无lock)
SELECT * FROM wallet WHERE id=’-1′ FOR UPDATE;
例2: (无主键,table lock)
SELECT * FROM wallet WHERE name=’Mouse’ FOR UPDATE;
例3: (主键不明确,table lock)
SELECT * FROM wallet WHERE id<>’3′ FOR UPDATE;
例4: (主键不明确,table lock)
SELECT * FROM wallet WHERE id LIKE ‘3’ FOR UPDATE;

因此我们更新了下Service层的Mapper方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
    public void testLock(int lockId)
    {
        Wallet wallet = walletMapper.selectForUpdate(4);
         
        BigDecimal one = new BigDecimal(1.00);
        BigDecimal two = new BigDecimal(2.00);
        BigDecimal three = new BigDecimal(3.00);
         
        wallet.setWalletAmount(wallet.getWalletAmount().add(one));
        wallet.setWalletAvailableAmount(wallet.getWalletAvailableAmount().subtract(two));
        wallet.setOldAmount(wallet.getOldAmount().add(three));     
         
        walletMapper.updateByPrimaryKeySelective(wallet);
    }

所对应的SQL如下:

1
2
3
4
5
6
7
<select id="selectForUpdate" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
  SELECT
  <include refid="Base_Column_List" />
  FROM wallet
  WHERE wallet_id = #{walletId,jdbcType=INTEGER}
  FOR UPDATE
</select>

自然大家可以看到,我这边加了锁,是通过主键锁行。

按着上面的测试连接数1000,并发数100,控制台没报错。

数据库结果也是很不错。

五、加大压力

按着上面的测试连接数5000,并发数350,控制台还是没报错。

数据库结果却是很出错了!!!

少update了很多值。为什么呢?

六、jvisualvm 小工具检测,发现Tomcat线程连接数默认不够

然后我用jvisualvm 小工具检测。多测了几次,发现连接数5000,并发数350,并发数上升。有一个图的值始终不变。如图:

发现图中 tomcat的守护线程一直在200左右。后来我去找了下tomcat的server.xml发现了,使用了默认,大概就是200左右。

所以就配置了一下,大致配置方法有两种如下:

第1种方式:配置Connector
maxThreads:tomcat可用于请求处理的最大线程数
minSpareThreads:tomcat初始线程数,即最小空闲线程数
maxSpareThreads:tomcat最大空闲线程数,超过的会被关闭
acceptCount:当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理

1
<Connectorport="8080"maxHttpHeaderSize="8192"maxThreads="150"minSpareThreads="25"maxSpareThreads="75"enableLookups="false"redirectPort="8443"acceptCount="100"connectionTimeout="20000"disableUploadTimeout="true"/>

第2种方式:配置Executor和Connector

name:线程池的名字
class:线程池的类名
namePrefix:线程池中线程的命名前缀
maxThreads:线程池的最大线程数
minSpareThreads:线程池的最小空闲线程数
maxIdleTime:超过最小空闲线程数时,多的线程会等待这个时间长度,然后关闭
threadPriority:线程优先级

1
2
3
<Executorname="tomcatThreadPool"namePrefix="req-exec-"maxThreads="1000"minSpareThreads="50"maxIdleTime="60000"/>
 
<Connectorport="8080"protocol="HTTP/1.1"executor="tomcatThreadPool"/>

maxThreads:线程池的最大线程数,直接配置1000,然后用连接数10000,并发数800测试。轻松见图:

七、总结

感谢帮助我的人。希望有大牛在此讨论相关。小生感激不尽。

JavaWeb 并发:FOR UPDATE 实战,监测并解决。的更多相关文章

  1. sqlserver默认隔离级别下并发批量update同一张表引起的死锁

    提到死锁,最最常规的场景之一是Session1 以排它锁的方式锁定A表,请求B表,session2以排它锁的方式锁定B表,请求A表之类的,访问顺序不一致导致死锁的情况本文通过简化,测试这样一种稍显特殊 ...

  2. Java生鲜电商平台-SpringCloud微服务架构高并发参数优化实战

    Java生鲜电商平台-SpringCloud微服务架构高并发参数优化实战 一.写在前面 在Java生鲜电商平台平台中相信不少朋友都在自己公司使用Spring Cloud框架来构建微服务架构,毕竟现在这 ...

  3. tk.mybatis通用插件updateByPrimaryKeySelective无法自动更新ON UPDATE CURRENT_TIMESTAMP列的解决办法

    tk.mybatis是一个很好用的通用插件,把CRUD这些基本的数据操作全都用动态SQL语句自动生成了,mapper和xml里十分清爽,但是昨天发现有一个小坑,记录在此: 有一张表,结构如下(已经简化 ...

  4. list的迭代器能解决并发问题,collection 的迭代器不能解决并发问题,for可以解决并发问题

    list的迭代器能解决并发问题,collection 的迭代器不能解决并发问题 为什么list支持add,collection不支持 例如有两个人同时添加第三个元素 list的迭代器能锁定线程 只有等 ...

  5. win7系统 windows update 总是更新失败解决方法:

    win7系统 windows update 总是更新失败解决方法: 右键单击桌面“计算机”选择“管理“. 进到“计算机管理“窗口后,展开”服务和应用程序“并双击”服务“,在窗口右侧按照名称找到”Win ...

  6. 去哪网实习总结:JavaWeb中文传參乱码问题的解决(JavaWeb)

    本来是以做数据挖掘的目的进去哪网的.结构却成了系统开发... 只是还是比較认真的做了三个月.老师非常认同我的工作态度和成果... 实习立即就要结束了,总结一下几点之前没有注意过的变成习惯和问题,分享给 ...

  7. .net core (领域事件,并发 for update) 工作内容记录

    这周工作,因为要对几个不同的表进行货币增加,锁定,所以 用了领域事件和并发 for update  ,先记录一下 领域事件 ,Dapper 事务 ,sql for update 这几个点 头大,最近工 ...

  8. mysql You can't specify target table for update in FROM clause解决方法

    mysql You can't specify target table for update in FROM clause解决方法出现这个错误的原因是不能在同一个sql语句中,先select同一个表 ...

  9. Java并发编程实战 02Java如何解决可见性和有序性问题

    摘要 在上一篇文章当中,讲到了CPU缓存导致可见性.线程切换导致了原子性.编译优化导致了有序性问题.那么这篇文章就先解决其中的可见性和有序性问题,引出了今天的主角:Java内存模型(面试并发的时候会经 ...

随机推荐

  1. openTSDB (rpm)安装 + Grafana 视图

    1.提前安装环境 操作系统:CentOS OpenTSDB版本:2.0.1 JDK版本:1.8.1_101 Apache HBase版本:1.1.2 2.安装Grafana yum安装grafana ...

  2. pandas库的学习笔记

    Environment pandas 0.21.0 python 3.6 jupyter notebook 开始 习惯上,我们导入如下: import pandas as pd import nump ...

  3. Spring 出现Could not resolve placeholder问题的解决方法

    项目开发中,使用@value注解获取不到配置文件里面的属性字段. 检查配置文件,在spring的配置文件中有配置读取,如下: <!-- 使用spring自带的占位符替换功能 --> < ...

  4. spring InitializingBean和DisposableBean init-method 和destroy-method @PostConstruct @PreDestroy

    对于初始化函数: @PostConstruct 注解的方法 InitializingBean接口定义的回调afterPropertiesSet() Bean配置中自定义的初始化函数 对于析构则与上相同 ...

  5. 高斯混合模型的EM算法

    高斯混合模型的EM算法 混合高斯模型 高斯混合模型的概率分布可以写成多个高斯分布的线形叠加,即 \[ p(\mathbf x) = \sum_{k=1}^{K}\pi_k\mathcal N(\mat ...

  6. HTML和CSS初级前端面试题汇总(持续补充)

    1.浏览器内核 IE:trident Firefox:gecko Safari:webkit Opera:以前是presto,现在是Blink Chrome:Blink 2.HTML文件开头的DOCT ...

  7. 当input中的type值为file时,各浏览器的表现形式不同

    如果想使各浏览器下的表现形式相同,需要对该input元素隐藏,然后再改元素下方添加标签.其html写法如下 <div class="input-file"> <i ...

  8. Java实现对文本文件MD5加密并ftp传送到远程主机目录

    需求描述: 客户出完账之后需要把出账的数据以文本文件的方式传送给收入管理系统,客户以前是通过本地的一个工具软件上传的,由于安全监管的原因,不允许在本地使用工具上传,因此客户希望我们在已经上线使用的系统 ...

  9. R_展示变量之间关系的图形

    #绘制普通矩阵散点图 plot(dataframe) #绘制带有拟合直线,最佳拟合曲线和直方图的矩阵散点图 library(car) attach(dataframe) scatterplotMatr ...

  10. all about

    1.三次握手和四次挥手?2.什么是OSI七层结构?3.http vs https?4.what is Ping?基于什么协议?使用方法?5.DNS?6.cookie vs session?7.LDAP ...