启动一个mysql
用docker很容易就起一个mysql的环境了,我的github repo docker-code,有例子
1 | cd mysql |
登陆mysql
用下面命令可以确认mysql所在的容器,在我机子是mysql_db_1
1
2
3
4
5sudo docker ps
[sudo] password for sky:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c7f451f632c0 adminer "entrypoint.sh doc..." 2 weeks ago Up 2 minutes 0.0.0.0:8080->8080/tcp mysql_adminer_1
e2645139c23b mysql "docker-entrypoint..." 2 weeks ago Up 2 minutes 3306/tcp mysql_db_1
进入容器
1 | sudo docker exec -it mysql_db_1 bash |
密码默认是example
创建测试数据库和表
1 | mysql> create database test; |
开始实验
mysql的默认隔离级别为可重复读
,所以是会出现幻读
的情况的。
还是验证下
1 | mysql> SELECT @@transaction_isolation; |
以前是SELECT @@tx_isolation;
,新版本要用SELECT @@transaction_isolation;
接下来插入一条数据1
mysql> insert into tx (num) values(100);
然后开启事务1
mysql> start transaction;
更新这条记录
1 | mysql> update tx set num=200 where num=100; |
留意上面的matched
是1的。
先不提交这个事务,开另外个终端按照上面的方法再打开个mysql,之后也开一个事务
1 | mysql> use test; |
由于隔离级别,所以看不到对方更新的
然后回到第一个终端,提交事务
1 | mysql> commit; |
再回到第二终端,
1 | mysql> select * from tx; |
还是因为隔离级别,还是看不到对方更新的。
接下来,重头戏来了,尝试在第二个终端上执行
1 | mysql> update tx set num=300 where num=100; |
居然更新不了,然后就幻读
了。这个很容易理解,在第二个终端的事务里,看到都是它启动事务那一刻的快照snapshot
,所以看不到其他事务的东西,可是一旦更新的时候,就会因为别人事务改变了原来的值,自己没办法再更新它以为的那个值了,所以这种以为就称为了幻觉(phantom)
,俗称幻读
。
那假如上面我不指定条件呢
1 | mysql> update tx set num=300; |
是可以更新的,因为这是全表更新,所以没问题。
1 | mysql> commit; |
提交下,方便下面继续做实验
虽说这个幻读
是问题,但是它也是人们用来做数据库CAS
的保证吧。
数据库CAS
我们在上面的表基础上再加一列version
1 | mysql> alter table tx add column version int null; |
设置个初始值
1 | mysql> update tx set version =0; |
然后用java跑个并发扣num
的程序
1 | package com.sky.code.mysql; |
运行结果为
1 | finally update 300 records |
数据库数据
1 | mysql> select * from tx; |
你会发现,只有300个成功更新的记录,数据库的记录也没有超扣。
所以利用这个,可以不用select for update
等锁的操作。
再试下锁
接下来试试锁吧
第一个事务执行
1 | mysql> start transaction; |
这里没有提交事务
然后第二个终端,直接运行下面命令
1 | mysql> update tx set num =400 where version=1; |
你会发现卡在那里,过一会就会响应
1 | ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
所以这里你就看到是有锁了,是不是很神奇呢,其实隔离级别就是靠锁来实现的
总结
上面列子和代码可以很好的说明了,数据库会在适当的时候加锁,保证数据不会有问题,当然前提是隔离级别设置够高,mysql默认是可重复读,所以足够保证了,还有上面例子可以用来做扣库存的代码哦😄,java代码可以在这里拿到。