2019独角兽企业重金招聘Python工程师标准>>>
事务定义:
访问并可能更新数据库:一句或一组SQL,或者是一段程序,反正update了就是事务
ACID的4原则:
原子性:
一致性:
隔离性:不被干扰
持久性:永久改变
COMMIT:提交所有操作
ROLLBACK:回滚(撤销)
表的类型:
MYSQL 的INNODB和BDB表支持事务,但MyISAM(默认)不支持事务
多事务可能的问题:
第一类丢失更新:撤销一个事务,影响已提交事务
脏读:读到别的事务未提交的数据,而那个事务恰巧回滚了……
虚读(幻读):读到别的事务新插入的数据
不可重复读:读到别的事务更新的数据,结果两次读取的数据不一样了。
第二类丢失更新:覆盖别的事务更新的数据
隔离级别:
Serializable(串行化):事务全看不到其他事务的更新,就是一个事务接着一个事务,没啥并发性可言。当然它是隔离最高的了!
Repeatable Read(可重复读):读写都会阻塞,所以事务间读和写互相看不到,但能看到其他事务新插入的记录,解决不了虚读问题。
Read Commited(读已提交数据):写的时候阻塞写和读,读的时候不阻塞写。结果事务看到其他事务新插入记录和更新
Read Uncomitted(读未提交数据):事务看到其他事务未提交的插入记录和更新。也就是说隔离最弱,没啥解决能力。
当然Read Commited用的最多,因为它安全、并发也好。但为了避免错误的读写,还需要用到乐观锁或者悲观锁。
悲观锁:其他事务会同时访问资源,就是上锁。它是数据库的锁机制,不是程序模块实现的,因为可能多个系统在动数据库嘛。
select * from person for update
update person set name='ff' where id='1'
非得提交了之后锁才能释放。所以第一句只要不执行完,其他的事务休想动符合条件的记录。
在hibernate里实现悲观锁:
String hqlStr ="from TUser as user where user.name='Erica'";
Query query = session.createQuery(hqlStr);
query.setLockMode("user",LockMode.UPGRADE); // 加锁
List userList = query.list();// 执行查询,获取数据
类名是TUser 字段是user,原理上还是数据库的 for update 子句
乐观锁:访问时其他事务修改的概率很低,大不了重读一次。完全靠隔离级别,。如果一次操作开销很大,建议用悲观锁。数据库里用版本号实现,说白了就是有个version字段嘛
比如:
select * from person
select * from person
update person set name='xiaoming',version=version+1 where id='1' and version=0;
找不到版本号就重新来呗!这就避免了第二类丢失和不可重复读。
举个例子,比如AB都在修改账户里的钱,版本号0,当前100¥。A读了100,扣了20,交上去就是80,版本号1;B如果没同时读,版本号改完就是2,也就没问题了。如果真和A同时读的,也读了个100¥,那它修改后的版本号就是1,不大于当前版本号,所以不能更新,只好重新操作一遍咯!这个不难理解吧?
当然乐观锁也会出点小麻烦,因为它是程序实现的,不是数据库实现的,所以脏读是没法避免的。比如这个系统回滚了,别的系统可不知道!
在hibernate里实现
<hibernate-mapping>
<class
name="org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
optimistic-lock="version"
>
……
</class>
</hibernate-mapping>
例子选自http://www.blogjava.net/loocky/archive/2006/11/15/81138.html
这样对象TUser写入关系型数据库用的就是乐观锁了。optimistic-lock="version"表示用版本号控制。
相互学习,欢迎补充