事务是最小的工作单位,这个单位由对数据库的若干操作组成
事务的特性
原子性(Atomicity)
事务中的操作,要么完成,要么取消
一致性(Consistency)
事务中的操作保证数据库中的数据不会出现逻辑上的不一致
隔离性(lsolation)
指当前事务和其他事务的操作是隔离的
永久性(Durability)
对事务发出commit命令后,事务的效果是永久的
控制语句
BEGIN 或者(START) TRANSACTION:开启事务
COMMIT:也可以使用COMMIT WORK,这两个是等价的,COMMIT会提交事务,并且对数据库进行的所有修改都是永永久的
ROLLBACK:也可以使用ROLLBACK WORK,不过二者是等价的。回滚会结束用户事务,并撤销所有正在进行未提交的修改
MYSQL的两种处理事务的方法
BEGN或 start transaction :开始一个事务
ROLLBACK:回滚事务
COMMIT:确认事务
使用指令查看正在进行事务
SELECT * FROM information_schema.INNODB_TRX;
每个线程都是独立的(每开启一个事务就会创建一个独立的线程 )
在JDBC中使用事务
JDBCconnection接口有提供了两个事务模式:自动提交和手动提交
在java中使用事务
定义一个全局变量Connection 第一个方法设置为false就是手动提交,这种方法适用于我们删除东西后重新再添加东西,类似权限管理系统这种可以用得上
如果没有问题就可以提交事务
全局变量.commit();
传统JDBC的操作流程
1)获取JDBC连接 2)声明sql 3)预编译sql 4 )执行sql 5)处理sql集 6)释放结果集 7)释放Statement 8)提交事务 9)处理异常并回滚事务 10)释放JDBC连接
JDBC的优缺点
1冗长,重复 2显示事务控制 3 每个步骤不可取 4 显示处理受检查异常
关于connection关闭的问题
无论是否使用线程池,线程都是固定的
如果connection不close那么能使用的connection的数量是有上限的,一但不管的话很容易超过上限
如果每次都在dao关闭connection,可能会导致事务失效,因为每一次打开的connection可能不是同一个
可以在service操作成功之后,见connection关闭,还需要将Threadlocal中的connection移除
提高插入的效率
可以在多行插入的时候开启事务,然后再全面插入之后在提交事务
使用executeBatch进一步提升插入速度
需要配置rewriteBatchedStatements=true提高性能
在mysql中insert支持一次性插入多条数据
事务的隔离性
根据隔离的可见性可以分为四个级别
Read Uncommitted(读取未提交的内容)
这个时候把读取的数据称为脏读(脏读又称无效的数据读出,指某一个事务更新了数据,在这个时候另一个事务读取了这个数据,因为前面这个修改了数据,所以这个时候读的数据是不对的,这个就叫脏读)
Read Committed(读取提交内容)
这个不会出现脏读.但是不能重复读取(在一个事务中前后读取的数据不一样)
Repeatable Read(可重读)
一个connection的事务开启之后,无论什么情况,前后读取的数据都是一致的
默认情况下,虽然可重复读取,但是会出现幻读
幻读:可以感觉到一行数据的存在,但是就是看不见
在mysql中使用的是MVCC,在开启事务的同时,就使用一个快照将当前的表的情况进行储存
这个是mysql的默认额事务隔离级别
Serializable (可串行化)
它通过强制事务排序,让他不可能相互冲突,性能低下
在mysql中需要打开
锁
主要是防止并发问题,在事务开启时,通过锁来解决并发问题
在mysql中有一下几种锁
全局锁
表锁
行锁
全局锁
全局锁可以将整个数据库锁定,主要是在备份的还是使用
启动全局锁
flush tables with read lock;
释放锁
unlock tables;
在导出的时候为了提高性能,也可以通过single transaction代替全局锁
mysqldump --single-transaction -uroot -p8888 htgl > bak.sql
表锁
只锁一张表
可以分为read和whrite
使用read锁
基本语法
lock tables tb_user read;
自己和别的线程都可以读取
自己在写入数据后直接失败
其他的线程在写入数据的时候,会进入阻塞,直到释放锁
使用写锁
基本语法
lock tables tb_user write;
自己可以读,自己可以写
其他线程进行读和写都会进入阻塞
metadata lock(MDL)元数据锁
保证并发访问 数据库 对象场景下的一致性而设定的。
在对一张表进行增,删,改的时候,会自动为表添加元数据锁
以防止其他的connection对当前表的字段进行修改
行锁
行锁只有一行
在增删改查的时候要配合事务进行触发,在事务提交后锁就会释放
行锁必须要有条件,.指定锁住的行
如果执行的条件不是索引,那么行锁会自动升级成表锁
行锁的分类
共享锁
排他锁:增删改都是排他锁
死锁
出现情况
当线程A修改1数据的时候,给1添加了一个锁
当线程B修改2数据的时候,给2添加了一个锁
接下来线程A去修改2数据,进入阻塞状态
接下来线程B去修改1数据,进入阻塞状态
理论上出现死锁会进入阻塞状态,默认死锁时间是50秒,,由于时间太长,mysql加入了一个自动检查死锁的功能
在出现高并发的情况下,很容易出现死锁,所以自动检测死锁在事务开启后,会严重影响效率
可以将自动检测死锁功能关闭,并把死锁时间调为1秒
set GLOBAL innodb_deadlock_detect = off;
比较好的方法是借用其他缓存框架,如redis,将数据先在redis中进行更新,然后每个一段时间插入到数据库中