文章目录
事务
什么是事务
事务:
数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;
事务是一组不可再分割的操作集合(工作逻辑单元);
典型事务场景(转账):
update user_account set balance = balance - 1000 where userID = 3;
update user_account set balance = balance +1000 where userID = 1;
mysql中如何开启事务:
begin / start transaction -- 手工
commit / rollback -- 事务提交或回滚
set session autocommit = on/off; -- 设定事务是否自动开启
JDBC 编程:
connection.setAutoCommit(boolean);
Spring 事务AOP编程:
expression=execution(com.gpedu.dao.*.*(..))
事务ACID特性
原子性(Atomicity)
最小的工作单元,整个工作单元要么一起提交成功,要么全部失败回滚
一致性(Consistency)
事务中操作的数据及状态改变是一致的,即写入资料的结果必须完全符合预设的规则,
不会因为出现系统意外等原因导致状态的不一致
隔离性(Isolation)
一个事务所操作的数据在提交之前,对其他事务的可见性设定(一般设定为不可见)
持久性(Durability)
事务所做的修改就会永久保存,不会因为系统意外导致数据的丢失
事务并发带来的问题
脏读
不可重复读
幻读
事务的4种隔离级别
SQL92 ANSI/ISO标准:
http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt
- Read Uncommitted(未提交读) --未解决并发问题
事务未提交对其他事务也是可见的,脏读(dirty read) - Read Committed(提交读) --解决脏读问题
一个事务开始之后,只能看到自己提交的事务所做的修改,不可重复读(nonrepeatable
read) - Repeatable Read (可重复读) --解决不可重复读问题
在同一个事务中多次读取同样的数据结果是一样的,这种隔离级别未定义解决幻读的问题 - Serializable(串行化) --解决所有问题
最高的隔离级别,通过强制事务的串行执行
innodb引擎对隔离级别的支持程度
锁
表锁、行锁
锁是用于管理不同事务对共享资源的并发访问
表锁与行锁的区别:
-
锁定粒度:表锁 > 行锁
-
加锁效率:表锁 > 行锁
-
冲突概率:表锁 > 行锁
-
并发性能:表锁 < 行锁
InnoDB存储引擎支持行锁和表锁(另类的行锁)
MySQL InnoDB锁类型
https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html
-
共享锁(行锁):Shared Locks
-
排它锁(行锁):Exclusive Locks
-
意向锁共享锁(表锁):Intention Shared Locks
-
意向锁排它锁(表锁):Intention Exclusive Locks
行锁的算法
-
自增锁:AUTO-INC Locks
-
记录锁 Record Locks
-
间隙锁 Gap Locks
-
临键锁 Next-key Locks
共享锁(Shared Locks) VS 排它锁(Exculsive Locks)
共享锁(S):
又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,
都能访问到数据,但是只能读不能修改;
-- 加锁释锁方式:
select * from users WHERE id=1 LOCK IN SHARE MODE;
commit/rollback
排他锁(X):
又称为写锁,简称X锁,排他锁不能与其他锁并存,如一个事务获取了一个数据行的排他
锁,其他事务就不能再获取该行的锁(共享锁、排他锁),只有该获取了排他锁的事务是可以对
数据行进行读取和修改,(其他事务要读取数据可来自于快照)**
-- 加锁释锁方式:
delete / update / insert 默认加上X锁
SELECT * FROM table_name WHERE ... FOR UPDATE
commit/rollback
innodb-行锁到底锁什么?
InnoDB的行锁是通过给索引上的索引项加锁来实现的。
只有通过索引条件进行数据检索,InnoDB才使用行级锁,否则,InnoDB将使用表锁(锁住索引的所有记录)
表锁:lock tables xx read/write;
理解行锁机制,才能设计高效的sql
意向共享锁(IS)&意向排它锁(IX)
意向共享锁(IS)
表示事务准备给数据行加入共享锁,即一个数据行加共享锁前必须先取得该表的IS锁,
意向共享锁之间是可以相互兼容的
意向排它锁(IX)
表示事务准备给数据行加入排他锁,即一个数据行加排他锁前必须先取得该表的IX锁,
意向排它锁之间是可以相互兼容的
意向锁(IS 、IX) 是InnoDB 数据操作之前 自动加的,不需要用户干预
意义:
当事务想去进行锁表时,可以先判断意向锁是否存在,存在时则可快速返回该表不能
启用表锁
在表上面打个标记,有点像java的偏向锁 (CAS操作)
自增锁AUTO-INC Locks
针对自增列自增长的一个特殊的表级别锁
show variables like 'innodb_autoinc_lock_mode';
# 默认取值1 ,代表连续,事务未提交ID
行锁的算法
临键锁(Next-Key)
Next-key locks :
锁住记录+ 区间(左开右闭)
当sql执行按照索引进行数据的检索时,查询条件为范围查找(between and、<、>等)并有数
据命中则此时SQL语句加上的锁为Next-key locks, 锁住索引的记录+ 区间(左开右闭)
临键锁解决了幻读问题
间隙锁(Gap)
Gap locks:
锁住数据不存在的区间(左开右开)
当sql执行按照索引进行数据的检索时,查询条件的数据不存在,这时SQL语句加上的锁即为
Gap locks, 锁住索引不存在的区间(左开右开)
记录锁(Record)
Record locks:
锁住具体的索引项
当sql执行按照唯一性(Primary key、Unique key)索引进行数据的检索时,查询条件等值匹
配且查询的数据是存在,这时SQL语句加上的锁即为记录锁Record locks ,锁住具体的索引项
利用锁解决并发问题
利用X锁解决脏读问题
利用S锁解决不可重复读
利用邻键锁解决幻读问题
死锁
- 多个并发事务(2个或者以上);
- 每个事务都持有锁(或者是已经在等待锁);
- 每个事务都需要再继续持有锁;
- 事务之间产生加锁的循环等待,形成死锁
死锁的避免
- 1)类似的业务逻辑以固定的顺序访问表和行。
- 2)大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。
- 3)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概
率。 - 4)降低隔离级别,如果业务允许,将隔离级别调低也是较好的选择
- 5)为表添加合理的索引。可以看到如果不走索引将会为表的每一行记录添
加上锁(或者说是表锁)
Undo log
- Undo Log 是什么:
undo 意为取消,以撤销操作为目的,返回指定某个状态的操作
undo log 指事务开始之前, 在操作任何数据之前, 首先将 需操作的 数据备份到一个地方 (Undo Log)
-
UndoLog 是为了实现事务的原子性而出现的产物
-
Undo Log 实现事务 原子性 :
事务处理过程中 如果出现了错误或者用户执行了 ROLLBACK 语句,Mysql 可以利用Undo Log 中的备份
将数据恢复到事务开始之前的状态 -
UndoLog 在Mysql innodb 存储引擎中用来实现多版本并发控制
-
Undo log 实现多版本并发控制:
事务未提交之前,Undo 保存了未提交之前的版本数据,Undo 中的数据可作为数据旧版本快照供
其他并发事务进行快照读MVCC:
Multiversion concurrency control (多 多 版本并发 控制)
普通话解释:
并发访问(读或写)数据库时,对正在事务内处理的数据做
多版本的管理。以达到用来避免写操作的堵塞,从而引发读操
作的并发问题。
快照读:
SQL 读取的数据是快照版本,也就是历史版本 , 普通的SELECT 就是快照读
innodb 快照读,数据的读取将由 cache( 原本数据) + undo( 事务修改过的数据) 两部分组成
当前读:
SQL 读取的 数据 是最新版本 。通过锁机制来保证读取的数据无法通过其他事务进行修改
UPDATE 、DELETE 、INSERT 、SELECT … LOCK IN SHARE MODE 、SELECT … FOR UPDATE 都是
当前读
Redo log
-
Redo Log 是什么:
Redo ,顾名思义就 是 重做。以恢复操作为目的,重现操作 ;
Redo log 指事务中 操作 的 任何数据,将 将 最新的 数据备份到一个地方 (Redo Log) -
Redo log 的持久:
不是随着事务的提交才写入的,而是在事务的执行过程中,便开始写入redo 中。具体
的落盘策略可以进行配置
RedoLog 是为了实现事务的持久性而出现的产物
- Redo Log 实现事务 持久性 :
防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql 服务的时候,根据redo
log 进行重做,从而达到事务的 未入磁盘数据进行 持久化
Redo log补充知识
指定Redo log 记录在{datadir}/ib_logfile1&ib_logfile2 可通过innodb_log_group_home_dir 配置指定
目录存储
一旦事务成功提交且数据持久化落盘之后,此时Redo log 中的对应事务数据记录就失去了意义,所
**以 Redo log 的写入是日志文件循环写入的 **
-
指定Redo log 日志文件组中的数量
innodb_log_files_in_group
默认为2 -
指定Redo log 每一个日志文件最大存储量
innodb_log_file_size
默认48M -
指定Redo log 在cache/buffer 中的buffer 池大小
innodb_log_buffer_size
默认16M
Redo buffer 持久化Redo log 的策略, Innodb_flush_log_at_trx_commit
:
- 取值 0 每秒提交 Redo buffer --> Redo log OS cache -->flush cache to disk[ 可能丢失一秒内
的事务数据] - 取值 1 默认值,每次事务提交执行Redo buffer --> Redo log OS cache -->flush cache to disk
[ 最安全,性能最差的方式] - 取值 2 每次事务提交执行Redo buffer --> Redo log OS cache 再每一秒执行 ->flush cache to
disk 操作
配置优化
Mysql服务器参数类型
基于参数的作用域:
- 全局参数
set global autocommit = ON/OFF;
- 会话参数( 会话参数不单独设置则会采用全局参数)
set session autocommit = ON/OFF;
注意:
- 全局参数的设定对于已经存在的会话无法生效
- 会话参数的设定随着会话的销毁而失效
- 全局类的统一配置建议配置在默认配置文件中,否则重启服务会导致配置失效
寻找配置文件
mysql --help #寻找配置文件的位置和加载顺序
Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf
mysql --help | grep -A 1 'Default options are read from the following
files in the given order'
全局配置文件配置
- 最大连接数配置
max_connections - 系统句柄数配置
/etc/security/limits.conf
ulimit -a - mysql 句柄数配置
/usr/lib/systemd/system/mysqld.service
常见全局配置文件配置
port = 3306
socket = /tmp/mysql.sock
basedir = /usr/local/mysql
datadir = /data/mysql
pid-file = /data/mysql/mysql.pid
user = mysql
bind-address = 0.0.0.0
max_connections=2000
lower_case_table_names = 0 # 表名区分大小写
server-id = 1
tmp_table_size=16M
transaction_isolation = REPEATABLE-READ
ready_only=1
Mysql内存参数配置
每一个connection 内存参数配置:
-
sort_buffer_size connection 排序缓冲区大小
建议256K( 默认值)-> 2M 之内
当查询语句中有需要文件排序功能时,马上为connection 分配配置的内存大小 -
join_buffer_size connection 关联查询缓冲区大小
建议256K( 默认值)-> 1M 之内
当查询语句中有关联查询时,马上分配配置大小的内存用这个关联查询,所以有可能在一个查询语句中会分配很多个关联查询缓冲区 -
上述配置4000 连接占用内存:
4000*(0.256M+0.256M) = 2G
Mysql内存参数配置
- Innodb_buffer_pool_size
innodb buffer/cache 的大小(默认128M) - Innodb_buffer_pool
数据缓存
索引缓存
缓冲数据
内部结构
大的缓冲池可以减小多次磁盘I/O 访问相同的表数据以提高性能
参考计算公式:
Innodb_buffer_pool_size = (总物理内存 - 系统运行所用 - connection 所用)* 90%
Mysql其他参数配置
- wait_timeout
服务器关闭非交互连接之前等待活动的秒数 - innodb_open_files
限制Innodb能打开的表的个数 - **innodb_write_io_threads
- innodb_read_io_threads
innodb使用后台线程处理innodb缓冲区数据页上的读写 I/O(输入输出)请求 - innodb_lock_wait_timeout
InnoDB事务在被回滚之前可以等待一个锁定的超时秒数
https://www.cnblogs.com/wyy123/p/6092976.html 常见配置的帖子
数据库表设计
- 第一范式( 1NF):
字段具有原子性,不可再分。 所有关系型数据库系统都满足第一范式数据库表中的字
段都是单一属性的, 不可再分; - 第二范式( 2NF):
- 要求实体的属性完全依赖于主键。 所谓完全依赖是指不能存在仅依赖主键一部分的属性,
如果存在, 那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体, 新实体与原
实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。
简而言之, 第二范式就是属性完全依赖主键。 - 第三范式( 3NF):
满足第三范式( 3NF) 必须先满足第二范式( 2NF)。 简而言之, 第三范式( 3NF)
要求一个数据库表中不包含已在其它表中已包含的非主键信息。
简单一点 :
1 , 每一列只有一个 单一的 值 ,不可再拆分
2 , 每一行都 有主键能进行 区分
3 , 每一个表都不包含其他表已经包含的非 主键 信息。
- 充分的满足第一范式设计将为表建立太量的列
数据从磁盘到缓冲区,缓冲区脏页到磁盘进行持久的过程中,列的数量过多
会导致性能下降。过多的列影响转换和持久的性能
- 过分的满足第三范式化造成了太多的表关联
表的关联操作将带来额外的内存和性能开销 - 使用innodb 引擎的外键关系进行数据的完整性保证
外键表中数据的修改会导致Innodb引擎对外键约束进行检查,就带来了额外
的开销
SELECT
*
FROM
XXXXX
WHERE
(convert((price_full * 100 - price * 100) , SIGNED) - convert(coupon_price*100,SIGNED)
AND
is_del = 0)
ORDER BY
id
desc
limit 100
-- 设计冗余字段,在添加联合索引解决问题