mysql怎么保证隔离性 mysql56默认隔离级别( 二 )


持久性是指一个事务一旦被提交了 , 那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作 。例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成 , 当我们程序执行完成直到看到提示后,就可以认定事务已经正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成 。否则的话就会造成我们虽然看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误 。这是不允许的 。
在数据库操作中,在并发的情况下可能出现如下问题:
正是为了解决以上情况 , 数据库提供了几种隔离级别 。
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted(未授权读取、读未提交)、Read committed(授权读取、读提交)、Repeatable read(可重复读?。erializable(序列化),这四个级别可以逐个解决脏读、不可重复读、幻象读这几类问题 。
虽然数据库的隔离级别可以解决大多数问题,但是灵活度较差,为此又提出了悲观锁和乐观锁的概念 。
悲观锁,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度 。因此,在整个数据处理过程中,将数据处于锁定状态 。悲观锁的实现,往往依靠数据库提供的锁机制 。也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则 , 即使在本系统的数据访问层中实现了加锁机制,也无法保证外部系统不会修改数据 。
商品t_items表中有一个字段status , status为1代表商品未被下单,status为2代表商品已经被下单(此时该商品无法再次下单) , 那么我们对某个商品下单时必须确保该商品status为1 。假设商品的id为1 。
如果不采用锁,那么操作方法如下:
但是上面这种场景在高并发访问的情况下很可能会出现问题 。例如当第一步操作中,查询出来的商品status为1 。但是当我们执行第三步Update操作的时候,有可能出现其他人先一步对商品下单把t_items中的status修改为2了,但是我们并不知道数据已经被修改了,这样就可能造成同一个商品被下单2次,使得数据不一致 。所以说这种方式是不安全的 。
在上面的场景中,商品信息从查询出来到修改 , 中间有一个处理订单的过程,使用悲观锁的原理就是,当我们在查询出t_items信息后就把当前的数据锁定,直到我们修改完毕后再解锁 。那么在这个过程中 , 因为t_items被锁定了,就不会出现有第三者来对其进行修改了 。需要注意的是,要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后 , MySQL会立刻将结果进行提交 。我们可以使用命令设置MySQL为非autocommit模式:set autocommit=0;
设置完autocommit后,我们就可以执行我们的正常业务了 。具体如下:
上面的begin/commit为事务的开始和结束,因为在前一步我们关闭了mysql的autocommit,所以需要手动控制事务的提交 。
上面的第一步我们执行了一次查询操作:select status from t_items where id=1 for update;与普通查询不一样的是,我们使用了select…for update的方式 , 这样就通过数据库实现了悲观锁 。此时在t_items表中,id为1的那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行 。这样我们可以保证当前的数据不会被其它事务修改 。需要注意的是,在事务中,只有SELECT ... FOR UPDATE或LOCK IN SHARE MODE操作同一个数据时才会等待其它事务结束后才执行,一般SELECT ...则不受此影响 。拿上面的实例来说,当我执行select status from t_items where id=1 for update;后 。我在另外的事务中如果再次执行select status from t_items where id=1 for update;则第二个事务会一直等待第一个事务的提交,此时第二个查询处于阻塞的状态 , 但是如果我是在第二个事务中执行select status from t_items where id=1;则能正常查询出数据,不会受第一个事务的影响 。

推荐阅读