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

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

  1. @Transactional
  2. @Override
  3. public boolean increaseBalanceByLock(Long userId, BigDecimal amount)
  4. throws ValidateException {
  5. long time = System.currentTimeMillis();
  6. //获取对记录的锁定
  7. UserBalance balance = userBalanceDao.getLock(userId);
  8. LOGGER.info("[lock] start. time: {}", time);
  9. if (null == balance) {
  10. throw new ValidateException(
  11. ValidateErrorCode.ERRORCODE_BALANCE_NOTEXIST,
  12. "user balance is not exist");
  13. }
  14. boolean result = userBalanceDao.increaseBalanceByLock(balance, amount);
  15. long timeEnd = System.currentTimeMillis();
  16. LOGGER.info("[lock] end. time: {}", timeEnd);
  17. return result;
  18. }

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

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

  1. 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. ios jsbrige

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  2. OAuth认证的过程

    在认证和授权的过程中涉及的三方包括:     服务提供方,用户使用服务提供方来存储受保护的资源,如照片,视频,联系人列表.     用户,存放在服务提供方的受保护的资源的拥有者.     客户端,要访 ...

  3. 面向对象程序设计-C++_课时21引用

    数据类型 & 别名=对象名; #include <iostream> using namespace std; int * f(int * x) { (*x)++; return ...

  4. 使用sphinx索引mysql数据

    数据库表如下 mysql> select * from tb_account; +----+-------+------+ | id | name | age | +----+-------+- ...

  5. UI_拖动View

    方法一 在touchesMoved中 // 获取到触摸的手指 UITouch *touch = [touches anyObject]; // 获取集合中对象 // 获取開始时的触摸点 CGPoint ...

  6. 君子性非异也,善假于物也 - Threejs 引入TrackballControls 查看场景

    君子性非异也,善假于物也 - Threejs 引入TrackballControls 查看场景 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循" ...

  7. 潜在语义分析Latent semantic analysis note(LSA)原理及代码

    文章引用:http://blog.sina.com.cn/s/blog_62a9902f0101cjl3.html Latent Semantic Analysis (LSA)也被称为Latent S ...

  8. 2014.8.30.ref,out,params,enum,递归

    (一)ref 函数形参变量的输入有两种方式:传值,传址.而ref则为传址.eg: static int Add(ref int n) { Console.WriteLine("Add---- ...

  9. ##DAY11 UITableView编辑

    ##DAY11 UITableView编辑 每一个视图控制器都有一个编辑按钮,因为项目中编辑的应用场景非常多,所以系统预留了一个编辑按钮供我们使用 self.navigationItem.leftBa ...

  10. oracle 与sql server 部分内置函数替换

    1,trunc--convert oracle: select trunc(sysdate) from dual   输出:2016/5/24 sql server:select CONVERT(va ...