mysql 散记:innodb引擎和memory引擎对比 sql语句少用函数 事务与长事务
文章目录
- innodb引擎和memory引擎对比
- 对比
- sql 语句:尽可能不使用函数条件
- 隐式转换
- 隐式类型转换
- 隐式字符编码转换
- 补充问题
- 事务与长事务
- ACID
- read view
- MVCC 一致性视图
- 当前读
- view 虚拟表
- 长事务的影响与排查
- 影响
- 排查方法
- 预防
innodb引擎和memory引擎对比
innodb引擎是索引组织表(B+树),memory引擎是堆组织表(数组)
memory引擎数据结构
![]() |
---|
图片来自极客时间 丁奇 MySQL实战45讲 |
对比
- 内存表按照写入顺序 顺序存放,innodb表数据有序存放
- 内存表重启mysql就没了,innodb重启了还在
- 内存表锁粒度粗,表锁;innodb有行锁
- 写入数据:内存表里有空位就可以随便放数据,innodb表要保证树的有序性,要在指定位置插入数据
- 数据位置变化时,内存表所有索引都要改(因为每个索引都直接指向数据的物理位置),innodb只用改主键索引(二级索引存的是主键索引位置,不用改)
- 内存表所有索引地位相同,arr[index]取数据;innodb表二级索引地位低于主键索引
- 内存表每个元素类型相同,都是arr item的统一类型;innodb表每个字段类型可以不同
sql 语句:尽可能不使用函数条件
使用函数条件会导致索引优化不能用,引起全表扫描。因为函数字段破坏索引的有序性。比如:
select id, name from t where id+1 > 200 (where部分是函数表达式,no no no)
select count(*) from t where month(time)=7;
隐式转换
隐式类型转换
隐式类型转换也相当于函数处理,会导致索引无效,比如:
select * from tradelog where tradeid=110717; // tradeid是varchar类型
// 这个操作就相当于在做数值比较,tradeid会先转成int类型再和110717比较 等价 CAST(tradid AS signed int) = 110717;
// 这个语句会走全表扫描
隐式字符编码转换
隐式字符编码转换,比如:t1的字符集是utf8mb4,t2的字符集是utf8;utf8mb4是utf8的超集。两个表字段进行比较时,utf8会先调用函数转utf8mb4再比较
如果调用时函数作用在参数上就不影响索引选择,但函数作用在列上,就会导致索引失效
// 索引失效
select t2.* from t1, t2 where t1.aid=t2.aid and t1.id=2;
// 步骤:在t1表上拿到t1.id这行aid(utf8mb4);到t2表上找到t2.aid=utf8mb4 aid对应的行,转换字符集 => 找CONVERT(t2.aid USING utf8mb4) = $(t1.aid).value(参数常量)
// 函数作用在索引字段上,索引失效// 不影响索引
select t1.c from t1, t2 where t1.aid=t2.aid and t2.id=4;
// 步骤:在t2表上拿到t2.id这行的aid(utf8);到t1表上找t1.aid=utf8 aid对应的行,转换字符集 => 找t1.aid=CONVERT($(t2.aid).value USING utf8mb4) (参数常量)
补充问题
`b` varchar(10) DEFAULT NULLselect * from table_a where b='1234567890abcd';
// 不是直接找不到,而是扫描行 找 然后 对比,最后发现找不到。执行步骤:截断查找1234567890(10位),innodb找到了回到server层对比,不相等,循环 最后失败
事务与长事务
ACID
A | atomicity | 原子性 | 事务中的所有操作统一成功,统一失败 // 部分失败就需要回滚 undolog |
C | consistency | 一致性 | |
I | isolation | 隔离性 | mvcc一致性视图 |
D | durability | 持久性 | 永久保存,故障恢复(redo log) |
-
存储引擎执行语句时默认都会开启事务;只读操作不开启事务
-
读(select … for update, select … lock in share mode)写操作默认开启事务
-
默认set autocommit=1 // 自动提交;如果业务设置了set autocommit=0需要显示的commit才能提交事务
- set autocommit=0容易导致意外的长事务
-
begin;时只是做了记录,到执行第一条读写语句时才真正开启事务,分发事务ID
- 只读事务部分发事务ID,事务ID是为了处理写操作冲突问题
-
马上启动事务语句:start transaction with consistence snapshot; // 创建一致性视图
- 可重复读级别下才有一致性视图,读已提交下该语句等价start transaction;
read view
innodb中有两种视图:mvcc 一致性视图、view 虚拟表
MVCC 一致性视图
- MVCC是为了解决事务ACID中的I isolation隔离性出现的
- 历史数据不是直接存在的,而是通过undo log版本链在需要时计算出来的
- undo log 实现秒级快照
go 原子操作、加锁、MVCC多版本并发控制_mvcc golang-CSDN博客
当前读
begin; // 假设创建可重复读级别的视图// Q1:在undo log上,拿着活跃列表走过不可见事务直至找到第一个可见事务,读这条数据
select * from t where id = 5; // 可重复读// Q2:更新数据前都要查一次数据,读是当前读:读最新的数据
update table t set c = 6 where id = 5; // 当前读// 自己的update对自己可见,所以此时读到的数据是Q2更新后的数据,与Q1时不同
select * from t where id = 5; commit;
select … for update(写锁)、select … lock in share mode(读锁)也是当前读。
view 虚拟表
查询语句定义的虚拟表,相当于持久化的查询语句别名。在调用时执行查询语句并生成结果。
虚拟表只存储查询定义,不存储实际数据。
长事务的影响与排查
影响
-
长期占用连接,内存不释放,变大
- 内存不是在使用完后释放,而是在连接断开或重置时释放
-
长事务阻塞后续操作,
- 可能导致线程用满 // 都等着执行,但长事务占着资源
- 可能引起后续查询慢,比如长事务update id=1的数据10万次,没执行完成时select * from t where id = 5;需要在undo log链表上往前找10万多次才能找到需要返回的数据
-
主从延迟 主库执行长事务10分钟,再发到从库去执行
- 无法应用从库并行复制能力
-
崩溃恢复时间长 可能因为 长事务回滚
排查方法
-
是否set autocommit=0
- 随便查个业务,通过general_log看是不是没有commit
-
information_schema.Innodb_trx 监控这个表,看长事务阈值
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60
预防
-
业务执行设置超时时间
-
长事务会导致undo log很大,innodb_undo_tablespaces = 2(2个独立的表空间文件), 方便回滚清理