搜索
查看
编辑修改
首页
UNITY
NODEJS
PYTHON
AI
GIT
PHP
GO
CEF3
JAVA
HTML
CSS
搜索
笔触狂放9
这个屌丝很懒,什么也没留下!
关注作者
热门标签
jquery
HTML
CSS
PHP
ASP
PYTHON
GO
AI
C
C++
C#
PHOTOSHOP
UNITY
iOS
android
vue
xml
爬虫
SEO
LINUX
WINDOWS
JAVA
MFC
CEF3
CAD
NODEJS
GIT
Pyppeteer
article
热门文章
1
uniapp 微信小程序:RecorderManager 录音DEMO_微信小程序录音机样式
2
YOLOv8改进 添加大核卷积序列注意力机制LSK_大核卷积注意力机制
3
数据结构学习笔记——查找算法中的树形查找(二叉排序树)_二叉排序树的平均查找长度
4
int,long,long long类型的范围
5
怎样设置网络工作组计算机,如何建立计算机工作组?
6
The MathType DLL cannot be found.问题解决方法
7
阿里巴巴中间件性能挑战赛(RPC篇 同步阻塞模型)_com.alibaba.middleware
8
毕业设计:python新能源汽车数据分析可视化系统+Django框架(源码+文档)_新能源汽车数据分析可视化毕业设计
9
UE5俯视角游戏案例代码查看
10
【无标题】_大厂机试是acm吗
当前位置:
article
> 正文
平台转账的设计_转账系统安全描述
作者:笔触狂放9 | 2024-04-11 10:35:08
赞
踩
转账系统安全描述
做个平台间余额的互相转账,重点在于如何解决并发访问数据错乱的问题.比如俩个人之间同时都在给对方转账.以及很多人同时给一个人转账.必须保证数据库里面的数据是随时正确的.
我本没有想到过这一层面,被提醒的.
比如有个转账的方法transfer(业务逻辑主要写在这里)
1.首先想到的用多线程加锁,看了资料.是可以实现,但是问题是对方法加锁或者代码块加锁,那么每次只能执行一个线程,a转给b执行了,可c却不能同时转给d,二者根本不会影响啊.所以多线程白看了.
2.然后就取决在数据库上面了.用的是mysql innodb,所以找了这方面锁的知识.
这篇博客写的不错,感谢:
http://www.cnblogs.com/Arlen/articles/1756616.html
结合其他的参考学习,我总结了一下:
mysql innodb主要有表级锁和行级锁,转账之间不能互相影响,所以选择行级锁.
行级锁有两种:共享锁和排他锁
如何加锁:共享锁:select xxxx lock in share mode
排他锁:select xxx for update
顾名思义共享锁的重点在于多个事务可以同时共享同一资源上的共享锁,如果一个事务获取了一个数据的共享锁,那么别的事务也可以再获取这个锁.获取共享锁的事务可以对数据进行读操作.,读完了就释放.
排他锁就是一旦事务获取了这个锁,他就必须持有这个锁到结束,并不准其他事务获取这个锁.持有排他锁的事务可以对数据进行写操作.
然后明确一点一个事务在一个时刻里只能拥有一把锁
在转账时如果我先查找用户的账户信息,如果我用共享锁的话,别的事务也可以共享这个锁,这时候就会产生死锁.
这里的死锁产生的原因在于:
当A向B转账的时候,B也在向C转账,A开始转账的时候获取A,B账户信息,A开始转账的时候获取C,B账户信息,这里是共享锁,然后我需要对账户进行更新,这时候问题就出现了;
假如A转账在准备更新B的信息时,发现B转账也准备更新B,A就等B释放共享锁,B也等A在释放共享锁,也就产生死锁了.
如果没有,那么在查询完后,A事务就释放了共享锁,然后这时候别的B事务就可以获取这个用户账户信息的共享锁,
假如B获取了.那A了刚刚查出来了用户信息,想改的时候(mysql修改默认加上排他锁)发现资源又被锁了.给别人挪用了.别人对他干了啥我哪知道,以后数据就肯能和我刚读出来的不一样了.
排它锁事务一旦获取了就一直到事务结束都会拥有这把锁,这样就很好的杜绝了数据被修改的可能性.那么这时候重点就在于这里的事务怎么开始和啥时候结束了.我的项目用的是springMVC,就是spring的事务是怎么确定的.
这篇博客写的不错,感谢:
http://www.cnblogs.com/davidwang456/p/3832949.html
这里不需要知道spring事务的深层原理,只需知道spring事务是在进入sercice层执行方法的时候开始定义一个事务,然后在这个方法结束后事务才结束(事务提交).就是说在这个方法里面不管调用了多少个dao方法,都不会影响锁的丢失.也就是说我进入方法查询用户加了排他锁,在这个方法结束之前该数据就是安全的.
好了理论说完了,看程序了:
这是我的service方法
@Transactional(readOnly=false)
public int transfer(String userId, String money, String remarks,
String otherId) {
HcUser me = this.userDao.getUserForUpdate(userId);//获取用户信息
HcUser other = this.userDao.getUserForUpdate(otherId);//获取用户信息
//这里写转账的一些操作(修改/插入等)
return 0;
}
这是mybatis的sql:
<select id="getUserForUpdate" parameterType="java.lang.String" resultType="HcUser">
select *
from hc_user
where id=#{id}
for update <!-- 这里加锁-->
</select>
写的是web项目,需要在浏览器敲地址测试,但是无法实现多线程,于是可以这样:
用java写访问url的程序(网上找的拉):
package examples;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.commons.lang3.RandomUtils;
public class TEST {
public static void main(String args[]) {
for(int i = 0;i <400; i++) {//设置400个线程
String money =RandomUtils.nextInt(1, 5)+"";
System.out.println(money);
//模拟几个用户
String userId = "2aefe0c4c88f4ea1b39a68b6e33208d8";
String otherId="1bb4fc7d35c342a3a19bc23171aa3d22";
String userId1="1b638bd7dbb445a3bfe1efbec1a61e4e";
String userId2="551926783cb64524b3127ec02529d371";
Account a = null;
//设置不同的转账
if("1".equals(money)) {
a = new Account(money,userId,otherId);
}
else if("2".equals(money)) {
a = new Account(money,userId1,userId);
}else if("3".equals(money)) {
a = new Account(money,userId2,userId);
}
else {
a = new Account(money,userId1,otherId);
}
Thread t = new Thread(a);
t.start();
}
}
static class Account implements Runnable {
String money = "0";
String userId = "";
String otherId="";
public Account(String money,String userId,String otherId) {
this.money = money;
this.otherId = otherId;
this.userId = userId;
}
@Override
public void run() {
long begintime = System.currentTimeMillis();
try {
URL url = new URL("
http:xxx?userId=
"
+userId+"&otherId="+otherId+"&remarks=xxx"
+"&money="+money);
HttpURLConnection urlcon = (HttpURLConnection)url.openConnection();
urlcon.connect(); //获取连接
InputStream is = urlcon.getInputStream();
BufferedReader buffer = new BufferedReader(new InputStreamReader(is));
StringBuffer bs = new StringBuffer();
String l = null;
while((l=buffer.readLine())!=null){
bs.append(l).append("/n");
}
System.out.println(bs.toString());//打印出信息
System.out.println("总共执行时间为:"+(System.currentTimeMillis()-begintime)+"毫秒");
}catch(IOException e){
System.out.println(e);
}
}
}
}
这样写的程序可能出现有两个线程同时进行A给B转账,请先不要在意这种情况.
这是我数据库账户初始值,每人设置100:
1,在不加锁的情况下,跑完后:
总额变小了扇
2,加了共享锁,跑完后:
总额是对的,但是报错了:
Deadlock found when trying to get lock; try restarting transaction;
产生死锁了,但是mysql会自己强制性重启事务,所以不会一直死锁下去,程序之后还会运行.
3加了排他锁后:
没有错误,正确运行
这样就可以保证转账正确运行了,
声明:
本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:
https://www.wpsshop.cn/w/笔触狂放9/article/detail/404476
推荐阅读
article
MySQL
锁
机制
:高
并发
场景下该如何
保证数据
读写的
安全
性?_
sql
本身
并发
安全
吗...
看到这里,相信大家对
MySQL
中提供的锁
机制
有了全面的认识,但以目前情况而言,虽对每种锁类型有了基本认知,但本篇的内容更...
赞
踩
article
linux
开发工具
:
git
_
linux
git
工具...
Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。Git是Linus Torvalds为了帮助...
赞
踩
article
【强化学习】
确定性
策略
强化学习-
DPG
&D
DPG
算法
推导及分析_
ddpg
算法
如何收敛...
一、DGP推导本篇介绍
确定性
策略
梯度
算法
,该
算法
主要用于off-policy(on-policy也能用)。在DQN等值函...
赞
踩
article
MySQL8.0
安装
教程_
mysql8.0
.36
安装
requirement
...
msi版本的
安装
比较简单,个人推荐使用1.软件下载:1.1百度云连接:链接:https://pan.baidu.com/...
赞
踩
article
网络
工程师 名词解释_
网络
工程
专有名词
...
网络
工程师 名字解释_
网络
工程
专有名词
网络
工程
专有名词
1.1 ...
赞
踩
article
Redis
持久
化的两种
方式
_
redis
持久
化两种
方式
...
Redis
是一个内存数据库,而内存中的数据及易丢失,所以
Redis
持久
化变得非常重要。在
Redis
中提供了两种
持久
化的方...
赞
踩
article
My
SQL
常用
函数
...
一、数学
函数
数学
函数
主要用于处理数字,包括整型、浮点数等。ABS(x) 返回x的绝对值 SELECT ABS(-...
赞
踩
article
python
绝对
导入
_
Python
:相对
导入
与
绝对
导入
(
import
)、os.
path
、__
file
...
Python
在
导入
import
包的时候,有
绝对
导入
和相对
导入
方式。
绝对
导入
:
import
p1.m1 或者 from p...
赞
踩
article
【
FPGA
入门教程
】(二)
FPGA
学习
路线及开发流程_
fpga
语法
教程...
一、
FPGA
学习
路线 工具使用 ->
语法
学习
-> 逻辑设计 -> IP使用 ->接口设计 -> 时序分析 -> 片...
赞
踩
article
欧拉角
的
理解...
关于
欧拉角
的
概念,
欧拉角
的
定义(动态,静态,内旋、外旋),以及内旋和外旋
的
公式推导。_
欧拉角
欧拉角
...
赞
踩
article
理解
分布式
事务
...
文章首发于51CTO技术栈公众号作者 陈彩华文章转载交流请联系 caison@aliyun.com复制代码这篇文章将介绍...
赞
踩
article
java
大
并发
金额
更新
_
java
高
并发
情况下做金钱的加减怎么保证不会出错...
展开全部//刚写的,你看看吧,注释都写的很详细了!import
java
.util.concurrent.locks.*;...
赞
踩
article
主流的
开源
bi
工具
_
市面上
开源
的
bi
...
下面列出相对成熟和完整,并且现在
市面上
主流的
开源
bi
工具
。1、FineBI国内做的一流的BI
工具
,很炫酷,也比较实用。主...
赞
踩
article
Git
Credential
Manager
Git
凭据管理器...
(对于每个
Git
主机,此过程看起来会略有不同,甚至在某些情况下,无论你是连接到本地还是云托管的
Git
主机。
Git
...
赞
踩
article
springboot
获取
ip
地址方法...
springboot
获取
ip
地址方法_
springboot
获取
ip
地址
springboot
获取
ip
地址 ...
赞
踩
article
MySQL
-
创建
和管理
表
:
基础知识
、
创建
和管理
数据库
、
创建
表
、修改
表
、重命名
表
、删除
表
、清空
表
、拓展...
因为从系统架构的层次上看,
MySQL
数据库
系统从大到小依次是
数据库
服务器、
数据库
、数据
表
、数据
表
的行与列。
MySQL
-...
赞
踩
article
Neo4j
Desktop
管理工具
的安装和应用_
neo4j
管理工具
...
安装和启动
Neo4j
桌面如果您还没有,请下载
Neo4j
。使用提供的说明(下载时显示),按照步骤1安装并启动
Neo4j
...
赞
踩
article
mysql
字符串
函数_
select
char
(
77
,
77
.3)...
文章目录ASCII(str)BIN(N)BIT_LENGTH(str)CHAR(N,... [USING
char
set...
赞
踩
article
android
数据库
的
增删
改查
,【
Android
】
数据库
的
简单应用——
增删
改查
的
操作...
还记得getReadableDatabase()和getWritableDatabase()方法吧?在调用它们
的
时候会返...
赞
踩
article
keil
C51使用
printf
函数
_
keil
c
printf
...
在使用
printf
函数
之前需要注意亮点,一是调用头文件stdio.h,二是重定义put
c
har发送单个字符
函数
。这一点和...
赞
踩
相关标签
数据库
开发语言
缓存
java
redis
linux
git
网络
python绝对导入
欧拉角
旋转矩阵
大数据
java 大并发 金额更新
开源
bi工具
tcp/ip
网络协议
mysql
sql