首页 > 编程知识 正文

python使用mysql数据库,java内存占用高排查

时间:2023-05-06 13:55:04 阅读:167256 作者:4429

根据MySQL记录同时出现的脏数据问题。 问题的说明(银行操作员的例子)例如,A、B的操作员同时读取余额1000元的账户,A的操作员向该账户追加100元,B的操作员同时向该账户扣除50元,A先提出,B后提出。 最后实际账户余额为1000-50=950元,但原应为1000 100-50=1050。

首先,分析如何发生问题。

让我们来看看一些伪代码。 a表示操作员操作和执行的流程。

useruser=userdao.getbyid(1; ---------- (步骤1 )

user.set account (user.get account ) ) 100; ----------(步骤2 ) )。

userdao.update(user; --- (步骤3 )

B操作员执行以下步骤:

useruser=userdao.getbyid(1; --(步骤1 )

user.set account (user.get account ) (-50 ); ----------(步骤2 ) )。

userdao.update(user; --- (步骤3 )

a操作的程序执行到步骤1后,在步骤3的前一时间点b执行了步骤1。 然后,a执行步骤3,b执行步骤2和3。 在这个时候发生了问题。 这是典型的并发导致的脏数据问题。 也有人说要在代码中添加事务呢。 如果在方法中放入@Transational的话就不会发生这个问题了吧。 在这里我想说这样做是徒劳的。 当然,添加事务也可以解决此问题,但为了避免出现幻读,必须将事务的隔离级别设置为可序列化。 但是,这在并发时效率特别低。 对该数据行的每个请求(读和写请求)都必须等待,直到执行了所有前一读和写操作。 但是,MySQL的缺省事务隔离级别为REPEATABLE。 此隔离级别允许b在执行步骤1时读取数据行,即使a在执行步骤1后对数据行施加了读取锁定。 在这种情况下,会发生幻读问题。 所以单纯地在方法中加入@Transational注释并不能解决问题。

相关知识点:

事务的四个特性:原子性、一致性、隔离性和持久性

原子:事务中包含的所有操作要么全部执行完毕,要么全部执行失败。 事务中包含的所有操作的设计数据行要么显示为所有执行完成的结果,要么显示为所有执行完成之前的结果。 也就是说,fzdhb转了100张,wsdhk转了10张,fzdhb转了50张。 那么,对其他事务来说,能看到的只有fzdhb美元,同时wsdhk美元。 或者可能是FZ dhb 100张同时wsdhk张。 fzdhb不是50美元,wsdhk不是10美元。 这称为一致性持久性。 事务执行完成后,数据将永久存储在磁盘上。 隔离性:隔离性有四个隔离级别:

可避免出现串行脏读、不可重复读、幻读。

可重复读取:可避免脏读、不可重复读取的发生。

可以避免出现读命令(已提交)脏读。

Read uncommitted (阅读未提交)最低水平,不能保证任何情况。

那么肮脏的阅读、幻读是什么呢?

1、脏读

脏读是指读取一个事务期间未提交的另一个事务中的数据。

如果一个事务多次更改了数据,但在该事务中未提交多次更改,则当并发事务访问数据时,两个事务中的数据会不一致。 例如,用户a将100元转账给用户b,对应的SQL命令如下

updateusersetaccount=account 100 where name=’b’; (在这种情况下,a通知b )

updateusersetaccount=account-100 where name=’a’;

只有第一条的SQL被执行时,a通知b查看账户,b发现钱确实到账了(此时发生了脏读)。 然后,无论是否已执行第二条SQL,只要没有提交事务,所有操作都会回滚。 这样,b稍后再次查看账户时,会发现钱其实并没有周转。

2、不可重复阅读

不可重复的读取是指在一个事务的范围内,对数据库中的一个数据多次查询返回不同的数据值。 这是因为在查询间隔内由另一个事务修改并提交了。

例如,如果事务T1正在读取数据,事务T2立即修改该数据并将其提交到数据库,则事务T1再次读取该数据会产生不同的结果,并发送不可重复的读取。

非可重复读取与脏读取的区别在于,脏读取允许一个事务读取另一个事务未提交的脏数据,而非可重复读取允许前一事务读取已提交的数据。

在某些情况下,不能重复阅读不是问题。 例如,多次查询某个数据当然是围绕最后一次查询得到的结果。 但是,在某些情况下可能会出现问题。 例如,如果对同一数据按顺序查询,A和B可能会不同,A和B可能会战斗。

3、虚读(幻读)。

幻读是事务未独立执行时发生的现象。 例如,在对一个表中所有行的某个数据项执行从"1"改变为"2"的操作的情况下,事务T1表示

T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。

幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

巴拉巴拉了一大堆,现在来讲讲解决方案(自己的想法,有不足之处请大家帮忙指出~)

对于这个问题我们可以使用乐观锁的方式来实现:在user表中加入一个version字段,每次执行update时version++;

操作员A执行的sql操作:

select id, account,version from user where id="1";

查询结果:id=1, account=1000, version=1

update user

setaccount=account+100, version=version+1

whereid="1" and version=1

select id, account,version from user where id="1";

查询结果:id=1, account=1100, version=2

操作员B执行的sql操作

select id, account, version from user where id="1";

查询结果:id=1, account=1000, version=1

#操作员A已修改成功,实际user.account=1100、user.version=2,操作员B也将版本号加一(version=2)试图向数据库提交数据(account=950),但此时比对数据库记录版本时

发现,操作员B提交的数据版本号为2,数据库记录当前版本也为2,不满足 “提交版本必须大于记录当前版本才能执行更新 “的乐观锁策略,因此,操作员B的提交被驳回。

update userset account=account-50, version=version+1 where id="1" and version=1

select id, account, version from user where id="1";

查询结果:id=1, account=1100, version=2

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。