场景如下:
    用户账户有余额,当发生交易时,需要实时更新余额。这里如果发生并发问题,那么会造成用户余额和实际交易的不一致,这对公司和客户来说都是很危险的。
那么如何避免,有以下两种方法:
    1、使用悲观锁
            当需要变更余额时,通过代码在事务中对当前需要更新的记录设置for update行锁,然后开始正常的查询和更新操作
            这样,其他的事务只能等待该事务完成后方可操作
            当然要特别注意,如果使用了Spring的事务注解,需要配置一下:
    <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />

在指定代码处添加事务注解

@Transactional
@Override
public boolean increaseBalanceByLock(Long userId, BigDecimal amount)
throws ValidateException {
long time = System.currentTimeMillis();
//获取对记录的锁定
UserBalance balance = userBalanceDao.getLock(userId);
LOGGER.info("[lock] start. time: {}", time);
if (null == balance) {
throw new ValidateException(
ValidateErrorCode.ERRORCODE_BALANCE_NOTEXIST,
"user balance is not exist");
}
boolean result = userBalanceDao.increaseBalanceByLock(balance, amount);
long timeEnd = System.currentTimeMillis();
LOGGER.info("[lock] end. time: {}", timeEnd);
return result;
}

通过db的悲观锁,实际测试该方法确实可以有效控制,不过在大并发量的情况下,可能会有性能问题

  <select id="getLock" resultMap="BaseResultMap" parameterType="java.lang.Long">
<![CDATA[
select * from user_balance where id=#{id,jdbcType=BIGINT} for update;
]]>
</select>
    2、使用乐观锁
            这个方法也同样可以解决场景中描述的问题(我认为比较适合并不频繁的操作):
            设计表的时候增加一个version(版本控制字段),每次需要更新余额的时候,先获取对象,update的时候根据version和id为条件去更新,如果更新回来的数量为0,说明version已经变更
            需要重复一次更新操作,如下:sql脚本

update user_balance set Balance = #{balance,jdbcType=DECIMAL},Version = Version+1 where Id = #{id,jdbcType=BIGINT} and Version = #{version,jdbcType=BIGINT}

这是一种不使用数据库锁的方法,解决方式也很巧妙。当然,在大量并发的情况下,一次扣款需要重复多次的操作才能成功,还是有不足之处的。

延伸:

乐观锁:对每次的数据操作都保持乐观的态度,因此不对数据进行上锁。那么就存在数据会被反复读写的情况,所以每次修改数据的时候需要对数据进行判断是否被修改过。

悲观锁:对每次的数据操作持悲观态度,操作时上锁,防止操作时数据被他人修改

使用场景:

乐观锁:由于不上锁,性能较好,适用于读大于写的情况,如果写较多,则会导致重复尝试写入均失败。

悲观锁:上锁,数据写入时会导致读被挂起,适合写大于读的场景

实现:

乐观锁:在db中,可以增加版本号控制,java中的CAS等

悲观锁:db的for update,java中的synchronize等

Java如何实现对Mysql数据库的行锁的更多相关文章

  1. 实现对MySQL数据库进行分库/分表备份(shell脚本)

    工作中,往往数据库备份是件非常重要的事情,毕竟数据就是金钱,就是生命!废话不多,下面介绍一下:如何实现对MySQL数据库进行分库备份(shell脚本) Mysq数据库dump备份/还原语法: mysq ...

  2. java(2014)实现对mysql数据库分页的代码

    package util; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultS ...

  3. C++实现对MySQL数据库的连接,以及增删改查

    安装好MySQL,建好数据表的前提下. 如果只是想简单实现添加数据或者其他一个操作数据,可以参考另一篇博客. https://www.cnblogs.com/ming-4/p/11544514.htm ...

  4. 利用shell脚本实现对mysql数据库的备份

    #!/bin/bash #保存备份个数 number=3 #备份保存路径 backup_dir=/root/mysqlbackup #日期 dd=`date +%Y%m%d` #备份工具 tool=m ...

  5. c++简单实现对mysql数据库操作

    1.连接数据库 #include <mysql.h> #include <iostream> #include<string> #include<vector ...

  6. 用shell 实现对MySQL数据库分页

    参考链接 http://mp.weixin.qq.com/s?__biz=MzAxMzE4MDI0NQ==&mid=208299533&idx=1&sn=4cab00793eb ...

  7. Java Web学习系列——Maven Web项目中集成使用Spring、MyBatis实现对MySQL的数据访问

    本篇内容还是建立在上一篇Java Web学习系列——Maven Web项目中集成使用Spring基础之上,对之前的Maven Web项目进行升级改造,实现对MySQL的数据访问. 添加依赖Jar包 这 ...

  8. ava基础MySQL存储过程 Java基础 JDBC连接MySQL数据库

    1.MySQL存储过程   1.1.什么是存储过程 带有逻辑的sql语句:带有流程控制语句(if  while)等等 的sql语句   1.2.存储过程的特点 1)执行效率非常快,存储过程是数据库的服 ...

  9. 【mysql】备份篇2:使用java程序定期备份mysql数据库

    承接备份篇1, 在备份篇1中,使用dat文件加+系统计划任务程序完成mysql定期备份任务 在这一篇,备份使用java程序定期备份mysql数据库. 下面代码和程序思想给出: package com. ...

随机推荐

  1. HDU 5727 Necklace(二分图匹配)

    [题目链接]http://acm.hdu.edu.cn/showproblem.php?pid=5727 [题目大意] 现在有n颗阴珠子和n颗阳珠子,将它们阴阳相间圆排列构成一个环,已知有些阴珠子和阳 ...

  2. [Leetcode][Python]32: Longest Valid Parentheses

    # -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com' 32: Longest Valid Parentheseshttps://oj ...

  3. dataGuard主备库角色切换

    切换顺序: 先主库后备库 --查看主库可切换状态: SQL> select switchover_status from v$database; SWITCHOVER_STATUS ------ ...

  4. 前端开发工具—fiddle

  5. Redis的持久化选项

    Redis提供了两种不同的持久化方法来将数据存储到硬盘里面.一种方法叫快照(snapshotting),它可以将存在于某一时刻的所有数据都写入硬盘里面.另一种方法叫只追加文件(append-only ...

  6. MySQL加强

    MySQL加强 Default Not null Unique Primary key Zerofill primary key auto_increment primary key auto_inc ...

  7. poj2186 Popular Cows --- 强连通

    给一个有向图,问有多少结点是其它全部结点都能够到达的. 等价于,在一个有向无环图上,找出度为0 的结点.假设出度为0的结点仅仅有一个,那么这个就是答案.假设大于1个.则答案是0. 这题有环.所以先缩点 ...

  8. 显示标题栏中标题左侧的小图icon

    如何显示网站logo,定义网站收藏夹图标 代码与解释 <link rel="shortcut icon" href="/path/favicon.ico" ...

  9. JavaScript之面向对象的概念,对象属性和对象属性的特性简介

    一.大家都知道,面向对象语言有一个标志,那就是他们都有类的概念,通过类我们可以创建任意多个具有相同属性和方法的对象.但ECMAScript(指定JavaScript标准的机构,也就是说JavaScri ...

  10. ASP.NET MVC View向Controller传值方式总结

    1:QueryString传值1)也可以使用new{}来为form的action增加querystring2)在controler里使用Request.QueryString["word&q ...