读数据分为快照读和当前读。java
快照读:(不加锁)读到的未必最新的,普通色select语句mysql
当前读:(加锁) 读到的都是最新的。有:程序员
一、select * from table ? lock in share mode;sql
二、select * from table where ? for update; (关于for update用法具体下面说)数据库
三、insert, update, delete并发
第3类update数据的操做步骤是:mysql server根据查询条件先读取知足条件的第一条记录,并加锁,返回mysql server,mysql server收到以后会执行update操做,一条完成,再下一条。delete同理,Insert操做会稍微有些不一样,简单来讲,就是Insert操做可能会触发Unique Key的冲突检查,也会进行一个当前读。性能
mysql写数据的时候都会进行当前读。spa
关于每条sql如何执行的时候如何加锁参照:http://hedengcheng.com/?p=771server
数据库事物的控制,数据库事物分四个级别从低到高分别索引
1:read uncommitted
读未提交,脏读,这个在应用中基本不用。
2:read committed
读提交,会产生重复读
事例:程序员拿着信用卡去享受生活(卡里固然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱所有转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额固然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…
分析:这就是读提交,如有事务对数据进行更新(UPDATE)操做时,读操做事务要等待这个更新操做事务提交后才能读取数据,能够解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不一样数据,这就是不可重复读。
3:repeatable read
重复读,就是在开始读取数据(事务开启)时,再也不容许修改操做。
可是会存在幻读,由于幻读是insert产生的
事例:程序员某一天去消费,花了2千元,而后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,彷佛出现了幻觉,这就是幻读。
四、serializable序列化
Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,能够避免脏读、不可重复读与幻读。可是这种事务隔离级别效率低下,比较耗数据库性能,通常不使用。
咱们写service的时候常常遇到先查询再修改的操做。好比 用手机号注册一个新用户,首先得用手机号查询用户表是否存在,若是不存在则插入一条,存在就给他登陆。 问题来了,当手机号相同的两个请求并发访问的时候,结果是否是在用户出现了两个手机号相同的记录(由于同时查都发现没有这个手机号),但咱们要保证手机号是惟一的。(先不说数据表给手机号创建惟一约束)
一开始我使用事物的第三个等级也就是repeatable read,发现不行,为何呢,细想一下,repeatable read只是防止一个事物开启的时候别的事物不被修改,可是仍是能够查呀,呵呵。
嗯,不能被修改,for update就出现了,for update会产生锁,具体产生什么锁得看查询条件,一个原则,对明确查询结果的结果加行锁,查询结果若是整个表数据都有可能,就表锁。
好比:根据主键id查询,确定是一条,行锁。
根据name查询(无索引),表锁,由于都有可能
根据name查询(有索引),行锁
举个例子:
假设有个表单products ,里面有id跟name二个栏位,id是主键索引。
例1: (明确指定主键索引,而且有此笔资料,row lock)
SELECT * FROM products WHERE id='3' FOR UPDATE;
例2: (明确指定主键索引,若查无此笔资料,无lock)
SELECT * FROM products WHERE id='-1' FOR UPDATE;
例2: (无主键索引,table lock)
SELECT * FROM products WHERE name='Mouse' FOR UPDATE;
例3: (主键索引不明确,table lock)
SELECT * FROM products WHERE id<>'3' FOR UPDATE;
例4: (主键索引不明确,table lock)
SELECT * FROM products WHERE id LIKE '3' FOR UPDATE;
锁,就针对写数据库的操做加锁,若是select for update固然也锁了。
for update就有效的解决了上述的问题,注意,事物里面不要写时间不可控的逻辑,好比调一个第三方的接口等待返回,这样事物的时间会变成,而表一直被锁了。具体会出现什么问题没遇到。。。
还有一个方法就是经过程序来控制,java里有个关键字synchronized,同步锁,能解决问题,就是效率很低,
for (int k = 0; k < 10; k++) { long start = System.currentTimeMillis(); for (int j = 0; j < 100000000; j++) { synchronized (lock) { int i = 1; } } System.out.println(System.currentTimeMillis() - start); start = System.currentTimeMillis(); for (int j = 0; j < 100000000; j++) { int i = 1; } System.out.println(System.currentTimeMillis() - start); }
2366 30 2407 33 2410 30 2371 31 2380 31 2371 30 2388 32 2402 33 2396 30 2385 33