《一块儿学mysql》2

事务
 
爸妈让往他们银行卡里转点儿钱,但是我这钱全在支付宝里,爸妈又没有支付宝,只能从支付宝里转给他
们了,假如转帐过程当中,支付宝扣款成功了,可是银行系统崩溃了,钱没转进去,这咋整?个人大洋就这样
打水漂了?确定不能够,为了不这种状况的发生,就用到了事务,在转钱过程当中遇到了任何差错,就回到
没转钱以前的状态,这个就叫作事务
 
事务四大特性(ACID)
如下特性纯属我的理解
原子性(Atomicity):转帐前 -> 转帐  -> 转帐成功,以上述转帐为例,转帐这个动做包括【从个人支付宝扣
除money,在我爸妈的卡上增长money】,中括号里的内容要么所有执行(转帐成功),若是没有所有执行就
回到转帐前的状态(跟没执行一个效果),不能停留在转帐的中间过程——个人支付宝扣了钱,爸妈银行卡没
多钱
 
一致性(Consistency):个人理解是能量守恒,转帐先后,个人支付宝金额+我爸妈卡内金额是一致的
 
隔离性(Isolation):这个通常用在并发,两个线程的操做会互相影响的状况下,隔离性又分为若干个隔离级
别,下面具体讨论
 
永久性(Durability):只要事务提交了,就成了事实。
 
隔离级别
在说隔离级别以前先来讲几个概念
 
脏读:读到了另外一个事务未提交的更改
 
例如 
 事务一:包工头给农民工转帐
 事务二:农民工查看工资
 
 事务一:开始事务
 事务一:包工头给农民工转帐1000
 事务二:开始事务
 事务二:农民工查看帐户,多了1000块
 事务二:提交
 事务一:包工头回滚,转帐失败
 
不可重复读:一个事务对同一记录的读取结果不一致,由于另外一个事务更新了该记录,并提交
 
例如
 事务一:查看宾馆8301 的状态
 事务二:预订8301房间
 
 事务一:开始事务
 事务一:查看8301状态,未预约
 事务二:开始事务
 事务二:预约8301房间
 事务二:提交
 事务一:再次查看8301 状态,被预约
 
幻读:一个事务执行同一个查询结果不同,由于另外一事务插入了新的记录,并提交
 
例如
 事务一:统计网站的注册用户数
 事务二:注册新的用户
 
 事务一:开始事务
 事务一:查看注册用户数为10000
 事务二:开始事务
 事务二:新增一个用户(插入一条记录)
 事务二:提交
 事务一:查看注册用户数为10001
 
四种隔离级别
 
是否容许脏读
是否不可重复读
是否幻读
read uncommitted
read committed
repeatable read
serializable
事务的使用:
MariaDB [jason]> begin;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> insert into test values(2);
Query OK, 1 row affected (0.00 sec)
 
MariaDB [jason]> select * from test;
+------+
| i    |
+------+
|    1 |
|    2 |
+------+
2 rows in set (0.00 sec)
 
MariaDB [jason]> rollback;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> select * from test;
+------+
| i    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)
 
MariaDB [jason]> insert into test values(2);
Query OK, 1 row affected (0.00 sec)
 
MariaDB [jason]> commit;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> select * from test;
+------+
| i    |
+------+
|    1 |
|    2 |
+------+
2 rows in set (0.00 sec)
 
mysql默认自动开启事务,能够经过begin 手动开启事务,若是想要回到begin以前的状态,则rollback,
操做完成后记得commit,不然推出窗口会就等同rollback 了
 
mysql 中的锁 
 
行级别锁只对事务安全的表(innodb,bdb)有效
 
for update 行级锁
首先咱们先建一张表,插入两条数据
 
create table test(
f1 int,
f2 char(10),
index (f1)
)engine=innodb;
 
insert into test (f1) values
(1),
(2);
 
假设咱们如今有个需求,f1 必须是惟一的,不可重复,每次插入只能插入当前最大值加 1,假设有两个窗
口各有一个事务,在读取最大值并插入
 
窗口1:
MariaDB [jason]> begin;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> select max(f1) from test;
+---------+
| max(f1) |
+---------+
|       2 |
+---------+
1 row in set (0.00 sec)
 
MariaDB [jason]> insert into test (f1) values(3);
Query OK, 1 row affected (0.00 sec)
 
窗口2:
MariaDB [jason]> begin;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> select max(f1) from test;
+---------+
| max(f1) |
+---------+
|       2 |
+---------+
1 row in set (0.00 sec)
 
MariaDB [jason]> insert into test (f1) values(3);
Query OK, 1 row affected (0.00 sec)
 
窗口1:
MariaDB [jason]> commit;
Query OK, 0 rows affected (0.01 sec)
 
窗口2:
MariaDB [jason]> commit;
Query OK, 0 rows affected (0.01 sec)
 
如今查看数据:
MariaDB [jason]> select * from test;
+------+------+
| f1   | f2   |
+------+------+
|    1 | NULL |
|    2 | NULL |
|    3 | NULL |
|    3 | NULL |
+------+------+
4 rows in set (0.00 sec)
 
结果咱们插入了重复的数据,这与咱们的要求相背离了,mysql 提供了锁,能够解决这个问题
咱们先把错误数据删掉
 
MariaDB [jason]> delete from test where f1=3;
Query OK, 2 rows affected (0.00 sec)
 
咱们能够用一个 for update 锁来达到这个目的, 只能有一个会话能够拥有这个锁,当另外一个会话也申请这个锁
时,会暂时卡住,直到前一个会话commit  释放锁以后,
 
窗口1:
MariaDB [jason]> begin;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> select max(f1) from test for update;
+---------+
| max(f1) |
+---------+
|       2 |
+---------+
1 row in set (0.00 sec)
 
窗口2:
MariaDB [jason]> begin;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> select max(f1) from test;
+---------+
| max(f1) |
+---------+
|       2 |
+---------+
1 row in set (0.00 sec)
 
MariaDB [jason]> select max(f1) from test for update;
窗口2 暂时没有查询结果,要等到 窗口1 commit 以后才行
 
窗口1:
MariaDB [jason]> insert into test (f1) values (3);
Query OK, 1 row affected (0.00 sec)
 
MariaDB [jason]> commit;
Query OK, 0 rows affected (0.01 sec)
 
i窗口2:
+---------+
| max(f1) |
+---------+
|       3 |
+---------+
1 row in set (0.00 sec)
 
MariaDB [jason]> insert into test (f1) values (4);
Query OK, 1 row affected (0.00 sec)
 
MariaDB [jason]> commit;
Query OK, 0 rows affected (0.01 sec)
窗口2 的等待可能会超时,报错:Lock wait timeout exceeded;
 
咱们再来看看
MariaDB [jason]> select * from test;
+------+------+
| f1   | f2   |
+------+------+
|    1 | NULL |
|    2 | NULL |
|    3 | NULL |
|    4 | NULL |
+------+------+
4 rows in set (0.00 sec)
 
共享读锁:lock in share mode 行级锁
若是一个事务正在修改f1 的值,而另外一个窗口想读取有关f1 的最新值,能够用共享读锁
 
窗口1:
MariaDB [jason]> begin;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> select max(f1) from test;
+---------+
| max(f1) |
+---------+
|       4 |
+---------+
1 row in set (0.00 sec)
 
MariaDB [jason]> insert into test (f1) values(5);
Query OK, 1 row affected (0.00 sec)
 
窗口2;
 
MariaDB [jason]> select max(f1) ,f2 from test lock in share mode;
上面语句会阻塞直到 窗口1 commit;一样会超时跑异常
 
表级锁
 
读锁
只能对表进行读操做,不能进行写操做,写操做被锁
 
窗口1:申请test 表的读锁,在窗口1 中能够读,写test
窗口2:容许读test,可是写操做会阻塞,直到窗口1 unlock
 
 
写锁
上锁的那段时间没有读或写操做
上锁的窗口能够执行插入和insert 操做,其余窗口执行的命令会遇到阻塞
 
lock table 不是事务安全的,在锁表以前会提交所有的活动事务,
在更新比较频繁的表中应该尽可能避免表级锁,以免拥堵
相关文章
相关标签/搜索