suchen

Heim  >  Fragen und Antworten  >  Hauptteil

java - mysql事务隔离的问题?多线程并发怎么保证可靠?

mysql采用默认事务隔离级别REPEATABLE-READ;
然后我分别执行以下内容:
1、


2、使用jdbc访问数据库:

3、这时候打印结果,发现线程进入阻塞,一直卡在这里

也就是说我开启的另一个事务仍然能读取到数据,只是在最后执行executeUpdate的时候才被锁定不能执行
4、命令行执行commit


5、结果命令行操作被jdbc操作替换掉,数据改为4。

这不就是说REPEATABLE-READ仍然出现了丢失更新,而且没有行锁定吗?
事务与并发到底是什么区别,我在测试多线程执行以上jdbc操作时,发现最后得到的结果总是不对。例我开十个线程,每个都对数据+1,结果可能只加了5.所以在并发情况下怎么做才能保证数据的安全?

高洛峰高洛峰2887 Tage vor538

Antworte allen(5)Ich werde antworten

  • PHPz

    PHPz2017-04-17 17:57:17

    如果只是增一的话,number的数值不通过select获取,select获取的可能是旧值;然后换成update test set number = number + 1 where id = ?。这里的number会是最新的,这种方式可以避免select for update的完全串行的性能丢失。

    Antwort
    0
  • 怪我咯

    怪我咯2017-04-17 17:57:17

    REPEATABLE-READ是有可能出现幻读的
    如果你要保证绝对的安全只能把隔离级别设置成SERIALIZABLE
    这样所有事务都只能顺序执行,自然不会因为并发有什么影响了,但是性能会下降许多。

    如果你既不想性能下降又想控制让他不出错,现在比较常用的做法是,使用更新的版本控制。
    维护一个字段作为UpdateVersion,修改时updateversion也作为一个参数传入,在条件语句中添加例如where id=? and update_version = ? 当然set里面要update_version+1。
    这样可以控制到每次只能有一个人更新一个版本。

    Antwort
    0
  • PHP中文网

    PHP中文网2017-04-17 17:57:17

    通过试验,发现某种程度上已经理解了自己的问题。

    我之前被关于事务隔离级别错误信息误导,认为REPEATABLE-READ级别和SERIALIZABLE级别能解决丢失更新的问题,然而实际是不能的。
    因为mysql的select ... from table;语句 不管哪种隔离级别都是不会阻塞的,

    事务A读取 id为1 number = 3 ,执行更新操作 number设为1;
    事务B读取 id为1 number = 3, 执行+1操作  更新--进入阻塞

    在隔离级别下只有在更新数据时才会等待写锁(排它锁)的释放,所以我多个线程可以同时读取到number=3,基于此进行修改就必然会导致丢失更新。

    所以解决办法为程序中加入悲观锁 或 乐观锁 机制。
    悲观锁采用的是 select ... for update,

    事务A:  select ... for update,执行更新操作,
    事务B:  select ... for update --阻塞

    这时事务B读取操作将无法执行,只有当事务A的commit完成,事务B才可以继续执行,相当于采用串行一个个执行。

    乐观锁常用的是版本控制或时间戳控制,

    事务A:执行 此时版本为 1;执行完update ...set version=2 where version = 1;
    事务B:执行 此时版本为 1;执行完update ...set version=2 where version = 1;

    这时候事务B发现version=1已经不存在了,因为事务A首先执行完更新了数据库,把version字段设为了2,这样就会导致该事务提交失败,需要我们在程序中判断异常情况下如何继续操作。

    以上就是我当前的理解,至于mysql本身支持的MVCC(多版本并发控制)还不知道怎么用,乐观锁的异常处理也不太清楚具体要怎么实现,可能是要根据具体的业务场景采用不同的处理手段吧。

    Antwort
    0
  • 怪我咯

    怪我咯2017-04-17 17:57:17

    如果是单纯的Select会被看做是单纯的查询操作,所以不会被挂起。
    如果要对某行进行事务,应该先在Select时就使用Select ... For Update这种格式。
    使用For Update会对所选择的行加锁,当另外的事务再进行时,会在那个事务的Select ... For Update时就挂起,等待当前事务完成后才会继续执行。这样才能保证查询和写入之间没有中间者。

    Antwort
    0
  • 巴扎黑

    巴扎黑2017-04-17 17:57:17

    通过锁来控制。

    Antwort
    0
  • StornierenAntwort