MySQL事务

问题的提出 数据库是要被广大客户所共享访问的,那么在数据库操作过程中很可能出现以下几种不确定情况。 更新丢失 两个事务都同时更新一行数据,一个事务对数据的更新把另一个事务对数据的更新覆盖了。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。 脏读 一个事务读取到了另一个事务未提交的数据操作结果。这是相当危险的,因为很可能所有的操作都被回滚。 不可重复读 不可重复读(Non-repeatable Reads):一个事务对同一行数据重复读取两次,但是却得到了不同的结果。 包括以下情况: (1) 虚读:事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。 (2) 幻读(Phantom Reads):事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据(这里并不要求两次查询的SQL语句相同)。这是因为在两次查询过程中有另外一个事务插入数据造成的。 解决方案 为了避免上面出现的几种情况,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。 未授权读取 也称为读未提交(Read Uncommitted):允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。 授权读取 也称为读提交(Read Committed):允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。 可重复读取(Repeatable Read) 可重复读取(Repeatable Read):禁止不可重复读取和脏读取,但是有时可能出现幻读数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。 序列化(Serializable) 序列化(Serializable):提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。 隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。

 

事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。

如果这一组的业务都能成功处理,我们就可以把这个事务提交来保存你已做的行为结果。但如果一组中有任何的差错出现的话,我们就认为这事务不成功,需要回滚来撤消之前的操作。

 

事务的四大特性(ACID):

 

1.原子性(Atomicity) 
原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败。

 

2.一致性(Consistency) 
官网上事务一致性的概念是:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。还要一种说法是事务前后数据的完整性必须保持一致。以转账为例子,A向B转账,假设转账之前这两个用户的钱加起来总共是2000,那么A向B转账之后,不管这两个账户怎么转,A用户的钱和B用户的钱加起来的总额还是2000,这个就是事务的一致性。

 

3.隔离性(Isolation) 
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

 

4.持久性(Durability) 
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

 

事务隔离性问题:

 

如果不考虑事务的隔离性,会出现以下问题:

  • 脏读:指一个事务读取到了另外一个事务中未提交的数据。
  • 不可重复读:指一个事务读取到了另外一个事务提交的数据。
  • 幻读:指一个事务读取到了另外一个事务修改表后的数据。

脏读:

b欠a 100元,所以b向a转账100元

update from account set money = money +100where name = ‘a’

update from account set money = money - 100where name = ‘b’

 

事务A为b向a转账100元,事务B为a查询自己账户余额

如果事务不隔离,事务A正在操作,那么在执行第一条SQL语句后,a的账户多了100块,这时事务B开始查询操作,a去查询自己的账户,那么会查到自己的账户多了100块,认为b已经还了100块。但如果事务A回滚,一切操作都没有进行,那么b并没有向a转账100元,a却认为已经收到了b的转账,造成a损失100元。

 

不可重复读:

不可重复读,是指在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。比如事务A对于a用户的余额进行了两次查询,第一次查询结果为100块,在执行第二次查询之前,事务B修改了a用户的余额,那么事务A第二次查询时,便查询到了与第一次查询不同的值。

 

幻读:

事务A为查询表中工资大于5000的人,事务A会进行两次查询。第一次查询到共有10人工资大于5000,在进行第二次查询之前,事务B修改了表中数据,增加或者删除了一条记录,那么事务A在进行第二次查询时,查询到的人数会多或者少,就跟幻觉一样。

使用pymysql的commit即可达到事务的效果,增删改操作,在没有执行commit语句之前都不会写入数据库。

import pymysql

config = {
        'host': 'localhost',
        'port': 3306,
        'user': 'username',
        'password': 'password',
        'db': 'test',
        'charset': 'utf8',
        'cursorclass': pymysql.cursors.DictCursor
    }
conn = pymysql.connect(**config)
cursor = conn.cursor()
try:
    cursor.execute('INSERT INTO test(msg,times) VALUES ("eq", 22)')
    conn.commit()  # 提交修改,成功插入一条数据
    cursor.execute('INSERT INTO test(msg,times) VALUES ("eq", 22)')  # 该次修改未提交
    raise Exception
    cursor.execute('INSERT INTO test(msg,times) VALUES ("eq", 22)')  # 报错跳出try语句
    conn.commit
except:
    print('1')
finally:
    cursor.close()
    conn.close()

如果一系列数据库的操作需要保证同步,那么将commit语句放在所有需要更新数据库的语句的后面,就能保证数据一致性。只要有一条语句执行失败,该次操作就不会commit

 

题外话:

事务是解决一个线程需要执行多个操作的问题,也就是多个SQL语句,保证所有SQL执行成功才保存执行结果。

但无法解决多线程情况下,多线程同时CURD同一数据的问题,这时需要用到锁的机制。

https://www.cnblogs.com/deliver/p/5730616.html#4047518

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值