脏读(数据)

[align=center][size=large]脏读[/size][/align]

一、概念

数据写入与数据读出不一致

原因:未对写入与读出做整体性的处理,导致与预期结果不一致

二、例子

假设:
写入两个数据,第一个数据写入需4s,然后写入第二个数据
线程启动后,1s,需读取数据。
若:只对写进行 synchronized 处理,未对 读进行同样处理,就会出现脏读

package com.study.current.thread.day01;

/**
* 脏读
* 在数据写入过程未完成的情况下,去读取数据
*
* 假设写入需 4s
* 等待1s后读取数据
*
* 结论:
* 考虑问题一定要考虑整体性
*/
public class DirtyRead extends Thread {

private String name = "abc";
private String pass = "123";

public synchronized void setValue(String name ,String value){
this.name = name ;

try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}

this.pass = value ;
System.out.println("setValue name:"+name +" pass:"+value);
}

/**
* 若读取数据时未加 synchronized
* 输出结果:
* name:bcd pass:123
setValue name:bcd pass:345

写入的数据与读出的数据会不一致
*/
public synchronized void getValue(){
System.out.println("name:"+this.name + " pass:"+this.pass);
}

/**
* @param args
*/
public static void main(String[] args) {
final DirtyRead thread = new DirtyRead();
Thread t1 = new Thread(new Runnable() {

public void run() {
thread.setValue("bcd", "345");
}
});

t1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}

thread.getValue();
}

}



三、实例总结:
在我们对一个对象的方法加锁的时候,需要考虑业务的整体性,即为 setValue getValue
方法同时加锁 synchronized 同步关键字,保证业务 service 的原子性,不然会出现脏读
也从侧面保证业务的一致性

四、拓展

关系型数据的ACID原则:

假设
9:00 开始查询一个列表,数据量在1000W,未加索引,全表扫描,需10min处理完毕
9:05 执行一个DML 语句,修改某个字段值,由100改为200
9:10 的查询结果中是100 or 200 ?

答案: 100
数据查询会保留一份数据的快照,9:00查询就是9:00这一刻的数据的快照,而不会因为9:05修改数据,导致查询结果的变化

数据读的一致性。

另外:关系型数据库会拷贝旧值,如果commit 操作失败,会还原原有的数据
### 什么是数据 数据指的是在一个事务中取到了其他并发事务尚未提交的数据,这可能导致取到的数据并不准确或有效[^1]。具体来说,在分布式系统或数据库环境中,当两个或多个进程试图同时访问并修改相同的数据项时,如果没有适当的控制机制,则可能出现这种情况。 对于Redis这样的内存缓存系统而言,数据通常指其内部存储的内容与底层持久化数据库之间的状态不同步所造成的差异。例如,由于网络延迟或其他因素影响,使得某些更新未能及时同步至所有副本节点上,从而引发一致性问题[^2]。 ### 如何数据 在默认情况下,大多数关系型数据库管理系统(RDBMS),如MySQL,默认采用可重复隔离级别来防止现象的发生。然而,在较低级别的隔离模式下——比如已提交(Read Committed)或者更宽松的情况下,应用程序确实有可能遇到的情况。这意味着如果一个事务正在对某条记录做更改但还未提交,另一个查询该记录的事务就可能看到这些未确认的变化[^3]。 为了模拟这种场景,可以通过设置特定的会话参数降低事务隔离度来进行测试: ```sql SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT * FROM table_name WHERE id = specific_id; ``` 上述命令将允许当前连接下的后续查询以最低限度的安全保障运行,即它们能够查看任何其它正在进行中的变更,即使那些变更是由未完成的事物引起的。 请注意,这样做仅用于实验目的;生产环境应始终遵循最佳实践指南配置合适的隔离等级。 ### 数据产生的原因 数据主要源于以下几个方面的原因: - **缺乏有效的锁定策略**:当多个客户端尝试在同一时间点写入同一份资源而没有实施必要的排他性措施时,很容易发生冲突,进而产生数据。 - **高并发环境下竞争条件的存在**:尤其是在多线程或多服务器架构里,如果不采取适当手段协调各参与者的行为顺序,那么极短时间内发生的交错操作也可能破坏整体的一致性和准确性。 - **异步复制过程中的不确定性**:像Redis这类支持主从结构的服务端软件,在主节点向备份数量传播新版本的信息过程中,难免会出现短暂的时间窗口期,在此期间内部分实例间存在信息差,这就是所谓的“最终一致性”。 ### 解决方案 针对以上提到的各种成因,有如下几种常见的应对办法: - **提高事务隔离级**:通过调整SQL语句前缀`SET SESSION TRANSACTION ISOLATION LEVEL ...`指定更高的隔离标准,可以有效地减少乃至杜绝的风险。不过需要注意的是,增强安全性往往伴随着性能上的牺牲,因此需权衡利弊做出合理抉择。 - **引入乐观/悲观锁机制**:这两种不同的并发控制模型各有优劣,前者适用于低争用率的应用场合,后者则更适合于频繁发生互斥请求的情形。无论是哪种方式都能很好地保护共享对象免受非法篡改的危害。 - **优化缓存刷新频率及失效策略**:特别是面对诸如Redis之类的高速缓冲层时,应当精心设计相应的淘汰算法以及过期标记规则,确保每次获取的结果都是最新的且可靠的。此外还可以考虑启用双写回填技术,即每当检测到潜在风险信号便立即触发强制同步流程,以此快速修复偏差状况.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值