MySQL MVCC

news2024/11/26 14:43:20

1.隔离级别

1.1.理论

1.1.1.序列化(SERIALIZABLE)

如果隔离级别为序列化,则用户之间通过一个接一个顺序地执行当前的事务,这种隔离级别提供了事务之间最大限度的隔离;

1.1.2.可重复读(REPEATABLE READ,MySQL默认的隔离级别)

在可重复读在这一隔离级别上,事务不会被看成是一个序列.不过,当前正在执行事务的变化仍然不能被外部看到,也就是说,如果用户在另外一个事务中执行同条SELECT语句数次,结果总是相同的.(因为正在执行的事务所产生的数据变化不能被外部看到);

1.1.3.读已提交(READ COMMITTED)

READ COMMITTED隔离级别的安全性比REPEATABLE READ隔离级别的安全性要差.处于READ COMMITTED级别的事务可以看到其他事务对数据的修改.也就是说,在事务处理期间,如果其他事务修改了相应的表,那么同一个事务的多个 SELECT语句可能返回不同的结果;

1.1.4.读未提交(READ UNCOMMITTED)

READ UNCOMMITTED提供了事务之间最小限度的隔离.除了容易产生虚幻的读操作和不能重复的读操作外,处于这个隔离级别的事务可以读到其他事务还没有提交的数据,如果这个事务使用其他事务未提交的变化作为计算的基础,然后那些未提交的变化被它们的父事务撤销,这就导致了大量的数据变化;

1.2.实践

1.2.1.查看隔离级别

1>.MySQL8.0之前,通过如下命令查看全局隔离级别和当前session的隔离级别:

SELECT @@GLOBAL.tx_isolation, @@tx_isolation;

在这里插入图片描述

2>.MySQL8.0开始,通过如下命令查看MySQL默认的隔离级别以及当前session隔离级别:

SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;

在这里插入图片描述

3>.修改当前session隔离级别

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

在这里插入图片描述

注意:只是修改了当前session的隔离级别,其他session的隔离级别还是默认的!

1.2.2.读未提交

1.2.2.0.准备测试数据

读未提交是最小限度的隔离级别,这种隔离级别中存在脏读,不可重复读以及幻读等问题;
在这里插入图片描述

1.2.2.1.脏读

一个事务读到另一个事务还没有提交的数据,称之为脏读;

1>.首先打开两个SQL操作窗口,假设分别为A和B,在A窗口中输入如下几条SQL (注意:输入完成后不用执行);

START TRANSACTION;
UPDATE account set balance=balance+100 where name='zhangsan';
UPDATE account set balance=balance-100 where name='lisi';
COMMIT;

2>.在B窗口执行如下SQL,修改默认的事务隔离级别为READ UNCOMMITTED,如下:

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

3>.在B窗口中输入如下SQL,输入完成后,首先执行第一行开启事务(注意:只需要执行一行即可):

START TRANSACTION;
SELECT * from account;
COMMIT;

4>.接下来执行A窗口中的前两条SQL,即开启事务,给zhangsan这个账户添加100元(事务未提交):

5>. 进入到B窗口,执行B窗口的第二条查询SQL(SELECT * from user;),结果如下:
在这里插入图片描述

可以看到,A窗口中的事务,虽然还未提交,但是B窗口中已经可以查询到数据的相关变化了,这就是脏读的问题;

1.2.2.2.不可重复读

不可重复读是指一个事务先后读取同一条记录,但是两次读取的数据不同,称之为不可重复读;

1>.首先打开两个查询窗口A和B,并且将B窗口的数据库事务隔离级别设置为READ UNCOMMITTED,再将数据恢复到原始状态:

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

2>.在B窗口中输入如下SQL,然后只执行前两条SQL开启事务并查询zhangsan的账户:

START TRANSACTION;
SELECT * from account where name='zhangsan';
COMMIT;

在这里插入图片描述

3>.在A窗口中执行如下SQL,给zhangsan这个账户添加100块钱,如下:

START TRANSACTION;
UPDATE account set balance=balance+100 where name='zhangsan';
COMMIT;

4>.再次回到B窗口,执行B窗口的第二条SQL查看zhangsan的账户,结果如下:
在这里插入图片描述

可以看到,此时zhangsan的账户已经发生了变化,即前后两次查看zhangsan账户,结果不一致,这就是不可重复读;

脏读和不可重复读的区别:

脏读是看到了其他事务未提交的数据,而不可重复读是看到了其他事务已经提交的数据(由于当前SQL也是在事务中,因此有可能并不想看到其他事务已经提交的数据);

1.2.2.3.幻读

幻读和不可重复读非常像,看名字就是产生幻觉了;

1>.首先打开两个查询窗口A和B,并且将B窗口的数据库事务隔离级别设置为READ UNCOMMITTED,再将数据恢复到原始状态:

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

2>.在A窗口中输入如下SQL:

START TRANSACTION;
insert into account(id,name,balance) values(3,'wangwu',1000);
COMMIT;

3>.在B窗口输入如下SQL:

START TRANSACTION;
SELECT * from account;
delete from account where name='wangwu';
COMMIT;

4>.执行步骤:

①.执行B窗口的前两行SQL,开启一个事务,同时查询数据库中的数据,此时可以查询到原始的两个用户;

②.执行A窗口的前两行,向数据库中插入一个名为wangwu的用户,注意不用提交事务;

③.执行B窗口的第二行,由于脏读的问题,此时可以查询到wangwu的用户;

④.执行B窗口的第三行,去删除name为wangwu的记录,这个时候删除就会出现问题,虽然可以在B窗口中查询到wangwu,但是这条记录还没有提交,是因为脏读的原因才看到,所以无法删除(一直等待).此时就产生了幻觉,明明可以看到wangwu这条数据,但是却无法删除,这就是幻读;

1.2.3.读已提交

和读未提交相比,读已提交这种隔离级别主要解决了脏读的问题,对于不可重复读和幻读则未解决;

将事务的隔离级别改为"READ COMMITTED"之后,重复上面关于脏读案例的测试,发现已经不存在脏读的问题了;再重复上面关于不可重复读案例的测试,发现不可重复读的问题依然存在;

1.2.3.1.幻读

1>.打开两个窗口A和B,将B窗口的隔离级别改为READ COMMITTED,再将数据恢复到原始状态:

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

2>.在A窗口输入如下SQL:

START TRANSACTION;
insert into account(id,name,balance) values(3,'wangwu',1000);
COMMIT;

3>.在B窗口输入如下SQL:

START TRANSACTION;
SELECT * from account;
insert into account(id,name,balance) values(3,'wangwu',1000);
COMMIT;

4>.执行步骤:

①.执行B窗口的前两行SQL,开启事务并查询数据,此时可以查到原始的两个用户;

②.执行A窗口的前两行SQL,插入一条记录,但是并不提交事务;

③.执行B窗口的第二行SQL,由于现在已经没有了脏读的问题,所以此时查不到在A窗口添加的数据;

④.执行B窗口的第三行SQL,由于name字段唯一,因此这里会出现无法插入的情况(一直等待).此时就产生幻觉了,明明没有wangwu这个用户,却无法插入wangwu的数据,这就是幻读;

1.2.4.可重复读(InnoDB引擎默认的数据库事务隔离级别)

和读已提交相比,可重复读进一步解决了不可重复读的问题,但是幻读则未解决;

1>.可重复读中关于幻读的测试和上一小节基本一致,不同的是第二步中执行完插入SQL之后需要提交事务;

2>.由于可重复读已经解决了不可重复读的问题,因此第二步即使提交了事务,第三步也查不到已经提交的数据,第四步继续插入仍然不成功;

1.2.5.序列化

1>.序列化提供了事务之间最大限度的隔离,在这种隔离级别中,事务一个接一个顺序的执行,不会发生脏读,不可重复读以及幻象读的问题,最安全;

2>.如果设置当前事务的隔离级别为序列化,那么此时开启其他事务时就会阻塞,必须等待当前事务提交了,其他事务才能开启成功,因此前面的脏读,不可重复读以及幻象读的问题这里都不会发生;

1.3.总结

总的来说,隔离级别和脏读,不可重复读以及幻象读的对应关系如下:
在这里插入图片描述

性能关系图如下:
在这里插入图片描述

2.快照读和当前读

2.1.快照读

1>.快照读(SnapShot Read)是一种一致性不加锁的读,是InnoDB存储引擎并发如此之高的核心原因之一;

2>.在默认的可重复读的隔离级别下,事务开启的时候,就会针对当前库生成一个快照,快照读读取到的数据要么就是生成快照时的数据,要么就是当前事务自身插入/修改过的数据;

3>.日常所用的不加锁的查询,包括上一小节中涉及到的所有查询,都属于快照读;

2.2.当前读

与快照读相对应的就是当前读,当前读就是读取最新的数据,而不是历史数据,换言之,在可重复读的隔离级别下,如果使用了当前读,也可以读到其他事务已经提交的数据;

2.2.1.案例

1>.MySQL事务开启两个会话窗口A和B;

2>.首先在A窗口中开启事务并查询id为1的记录:

BEGIN;
SELECT * FROM account WHERE id = 1;

在这里插入图片描述

3>.在B窗口中对id为1的数据进行修改:

UPDATE account SET balance = 1000 WHERE id = 1;

注意: B会话中不要开启事务或者开启了事务要及时提交,否则update语句会占用一把排它锁导致之后在A会话中用锁时发生阻塞;

4>.回到A窗口继续做查询操作:

SELECT * FROM account WHERE id = 1;

在这里插入图片描述

SELECT * FROM account WHERE id = 1  FOR UPDATE;

在这里插入图片描述

SELECT * FROM account WHERE id = 1 LOCK in SHARE MODE;

在这里插入图片描述

可以看到,A会话中第一个查询是快照读,读取到的是当前事务开启时的数据状态,后面两个查询则是当前读,读取到了当前最新的数据,即B会话中修改后的数据;

3.undo log日志

1>.数据库事务有回滚的能力, 既然能够回滚,那么就必须要在数据改变之前先把旧的数据记录下来,作为将来回滚的依据,那么这个记录就是undo log日志;

2>.当我们要添加一条记录的时候,就把添加的数据id记录到undo log中,将来回滚的时候就据此把(添加的)数据删除;当我们要删除或者修改数据的时候,就把原数据记录到undo log中,将来据此恢复数据.查询操作因为不涉及到回滚操作,所以就不需要记录到undo log中;

3>.redo log日志和undo log日志区别:

1>.redo log: 记录的是物理级别上的页修改操作,比如页号,偏移量,写入的数据,主要是为了保证数据的可靠性(持久化);

2>.undo log: 记录的是逻辑操作日志,比如对某一行数据进行了insert操作,那么undo log就记录一条与之相反的delete操作.主要用于事务的回滚和一致性非锁定读;

4.行格式

1>.行格式就是InnoDB存储引擎在保存每一行数据的时候,究竟是以什么样的格式来保存这行数据;

2>.数据库中的行格式有好几种,例如COMPACT、REDUNDANT、DYNAMIC、COMPRESSED等,不过无论是哪种行格式,都绕不开以下几个隐藏的数据列:
在这里插入图片描述

上图中的列1,列2,列3…列N,就是我们数据库中表的列,保存着我们正常的数据,除了这些保存数据的列之外,还有三列额外加进来的数据,这也是我们需要重点关注的DB_ROW_ID、DB_TRX_ID、DB_ROLL_PTR三列;

①.DB_ROW_ID: 该列占用6个字节,是一个行ID,用来唯一标识一行数据.如果用户在创建表的时候没有设置主键,那么系统会根据该列建立主键索引;

②.DB_TRX_ID: 该列占用6个字节,是一个事务ID.在InnoDB存储引擎中,当我们要开启一个事务的时候,会向InnoDB的事务系统申请一个事务id,这个事务id是一个严格递增且唯一的数字,当前数据行是被哪个事务修改的,就会把对应的事务id记录在当前行中;

③.DB_ROLL_PTR: 该列占用7个字节,是一个回滚指针,这个回滚指针指向一条undo log日志的地址,通过这个undo log日志可以让这条记录恢复到前一个版本;

5.MVCC

1>.MVCC(Multi-Version Concurrency Control)多版本并发控制,其核心思想就是保存数据行的历史版本,通过对数据行的多个版本进行管理来实现数据库的并发控制;

2>.简单来说,我们平时看到的一条一条的记录,在数据库中保存的时候,可能不仅仅只有一条记录,而是有多个历史版本;

如图:
在这里插入图片描述

下面结合不同的隔离级别来说这张图;

5.1.可重复读

1>.首先,当我们通过INSERT/DELETE/UPDATE去操作一行数据的时候,就会产生一个事务ID,这个事务ID也会同时保存在行记录中(DB_TRX_ID),也就是说,当前数据行是哪个事务修改后得到的,是有记录的;

2>.INSERT/DELETE/UPDATE操作都会产生对应的undo log日志,每一行记录都有一个DB_ROLL_PTR指向undo log日志,每一行记录,通过执行undo log日志,就可以恢复到前一个记录,前前记录,前前前记录…;

3>.当我们开启一个事务的时候,首先会向InnoDB的事务系统申请一个事务id,这个事务id是一个严格递增的数字,在当前事务开启的一瞬间系统会创建一个数组,数组中保存了目前所有的活跃事务id,所谓的活跃事务就是指已开启但是还没有提交的事务;

这个数组中的最小值好理解就是本次第一个申请的事务id,但是最大值并不一定是当前事务的id.因为从申请好trx_id到创建数组这个过程是需要时间的,期间可能有其他会话也申请到了trx_id;

4>.如果当前事务想要去查看某一行数据,会先去查看该行数据的DB_TRX_ID:

①.如果这个值等于当前事务id,说明这就是当前事务修改的,那么这行数据对当前事务可见;

②.如果这个值小于数组中的最小值,说明当我们开启当前事务的时候,这行数据修改所涉及到的事务已经提交了,当前数据行对当前事务是可见的;

③.如果这个值大于数组中的最大值,说明这行数据是我们在开启事务之后,还没有提交的时候,有另外一个会话也开启了事务.并且修改了这行数据,那么此时这行数据对当前事务就是不可见的;

④.如果这个值的大小介于数组中最大值与最小值之间(闭区间),且该值不在数组中,说明这也是一个已经提交的事务修改的数据,这行数据对于当前事务也是可见的;

⑤.如果这个值的大小介于数组中最大值与最小值之间(闭区间),且该值存在数组中(不等于当前事务id),说明这是一个未提交的事务修改的数据,那么该行数据对当前事务是不可见的;

例如:

我们有 A、B、C、D 四个会话,首先A、B、C分别开启一个事务,事务ID是3、4、5,然后C会话提交了事务,A、B未提交.接下来D会话也开启了一个事务,事务ID是6,那么当D会话开启事务的时候,数组中的值就是[3,4,6].现在假设有一行数据的DB_TRX_ID是5(第四种情况),那么该行数据对会话D的事务就是可见的(因为会话D事务开启的时候它已经提交了),如果有一行数据的DB_TRX_ID是4,那么该行数据对会话D的事务就不可见(因为还未提交);

注意: 如果当前事务中涉及到数据的更新操作,那么更新操作是在当前读的基础上更新的,而不是在快照读的基础上更新的,如果是后者(快照读)则有可能导致数据丢失;

5>.案例
①.开启两个会话窗口A和B,首先在A窗口中开启事务:

BEGIN;
SELECT * FROM account;

在这里插入图片描述

②.在B窗口中做一次修改操作(不用显式开启事务,更新操作MySQL内部会开启事务,更新完成后事务会自动提交):

UPDATE account SET balance = balance + 100 WHERE id = 1;
SELECT * FROM account;

在这里插入图片描述

③.回到A窗口中,查询这行数据:
在这里插入图片描述

可以看到,该行数据并没有发生变化,符合预期(目前数据库事务隔离级别为可重复读);

④.在A窗口中做一次修改操作,然后再去查询:

UPDATE account SET balance = balance + 200 WHERE id = 1;
SELECT * FROM account;

在这里插入图片描述

可以看到该行数据是基于当前读(最新数据)做的修改操作,符合预期.如果基于快照读的来做修改,那么会话B的修改就会丢失,这显然是不对的;

其实MySQL中的update就是先读再更新,读的时候默认就是当前读,即会加锁,所以在上面的案例中,如果会话B中显式的开启了事务并且没有提交事务,那么会话A中的update语句就会被阻塞;这就是MVCC,一行记录存在多个版本.实现了读写并发控制,读写互不阻塞;同时MVCC中采用了乐观锁,读数据不加锁,写数据只锁行,降低死锁概率,并且还能据此实现快照读;

5.2.读已提交

读已提交和可重复读类似,区别主要是后者在每次事务开始的时候创建一致性视图(创建数组列出活跃事务id),而前者则每一个语句执行前都会重新算出一个新的视图,所以可重复读这种隔离级别会看到别的会话已经提交的数据(即使别的会话比当前会话开启的晚);

6.小结

1>.MVCC在一定程度上实现了读写并发,不过它只在读已提交和可重复读两个隔离级别下有效;

2>.读未提交总是会读取最新的数据行,序列化则会对所有读取的行都加锁,这两个隔离级别都和MVCC不兼容;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/83785.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录刷题记录 day42 打家劫舍 1 2 3

代码随想录刷题记录 day42 打家劫舍 1 2 3 参考:代码随想录 198. 打家劫舍 思想 1.dp[i]表示偷取[0,i]房间内获取的最高的金额 2.递推公式 偷取第i号房间时的价值 dp[i]dp[i-2]nums[i]; 不偷取第i号房间时的价值 dp[i]dp[i-1] 所以递推公式 dp[i]Math.max(d…

【Linux】一文简单了解操作系统在硬件中的作用,解析操作系统是做什么的?

目录前言一.操作系统的介绍二.计算机软件体系结构接口各层接口三.操作系统做什么1.不要让CPU打盹多道程序分时系统多任务系统2.设备驱动操作系统对软硬件进行合理的管理,以达到为上层用户提供良好的,稳定的,安全的运行环境的目的!…

AppScan使用教程

一、安装 IBM AppScan 该产品是一个领先的 Web 应用安全测试工具,曾以 Watchfire AppScan 的名称享誉业界。Rational AppScan 可自动化 Web 应用的安全漏洞评估工作,能扫描和检测所有常见的 Web 应用安全漏洞,例如 SQL 注入(SQL-i…

多时点DID实证流程笔记(Aggregate Effects from Public Works: Evidence from India)

文章、数据及stata代码来源: 链接:https://pan.baidu.com/s/1nBvlYGXkV7ednEx93ge1ZQ 提取码:vs5q本文进行的平行趋势检验、异质性检验均与大部分中文文献的处理方式不同,以及Bacon分解的方法对交叠did的潜在偏误进行诊断的做法也是比较新颖的。另外&a…

林业数字孪生打造实时树木“管家”

数字孪生是物联网、人工智能、虚拟现实、云计算等技术高度融合的综合性集成技术,主要目的是进行数字模型的智能分析、预测,为实体提供决策支持。林业数字孪生应用是智慧林业的具体实现手段,服务于智慧林草的业务需求,从而实现实体…

C语言#include的用法详解(文件包含命令)

#include叫做文件包含命令,用来引入对应的头文件(.h文件)。#include 也是C语言预处理命令的一种。 #include 的处理过程很简单,就是将头文件的内容插入到该命令所在的位置,从而把头文件和当前源文件连接成一个源文件&…

AI-多模态-2021:FILIP【一种基于交互的细粒度图文预训练模型】

前言 FILIP(Fine-grained Interactive Language-Image Pretrain)是一种基于交互的细粒度图文预训练模型,用于解决图文双塔匹配模型中的细粒度匹配问题。本文对该论文进行阅读笔记, 论文:https://arxiv.org/abs/2111.…

【C#基础教程】第二十章、事件

目录 事件 1.事件 2.扩展EventArgs类 事件 1.事件 事件的组成:事件由事件发布者和事件订阅者组成。事件内包含一个私有委托。 当事件发布时,事件发布者会发布消息、而事件订阅者会接受到事件已发生的通知,并作出相应的处理。 其中&#xf…

计算机研究生就业方向之考编

我一直跟学生们说你考计算机的研究生之前一定要想好你想干什么,如果你只是转码,那么你不一定要考研,至少以下几个职位研究生是没有啥优势的: 1,软件测试工程师(培训一下就行) 2,前…

MyBatis的缓存机制

文章目录1 MyBatis的一级缓存1.1 演示1.2 使一级缓存失效的四种情况2 MyBatis的二级缓存2.1 演示2.2 使二级缓存失效的情况:2.3 二级缓存的相关配置(了解即可)3 MyBatis缓存查询的顺序1 MyBatis的一级缓存 一级缓存是SqlSession级别的&#…

【vue加载16秒优化到2秒】Vue3加载慢的性能优化,打包后页面静态资源chunk-vendors文件过大加载慢响应慢

【写在前面】基于自己之前做vue3项目的时候发现了一个致命的问题,项目部署好了,但是加载起来反应慢半天,控制台一看才发现,有个chunk-vendors的js文件加载了十几秒,这无非是页面体验差到爆炸。今天就针对打包后静态资源…

【密码学基础】混淆电路(Garbled Circuit)

1 概念 混淆电路是一种密码学协议,以实现安全多方计算(MPC)。场景是当多个通信方需要共同输入数据,然后通过同一个函数计算出一个结果,但是,各个通信方都不允许其他人知道自己的输入是什么。混淆电路就能很…

MySQL错误码大全

MySQL的所有错误码可以从MySQL官网查看。 1.MySQL地址 首页网址 2.文档地址 以此点击如下标志。 MySQL8.0文档地址 3.错误码地址 服务码官网地址 点进去之后,可以看到: 分为三大类: Server Error Message ReferenceClient Error Mes…

Java大型建筑建设综合工程项目管理系统源码带原生APP源码 工程管理系统源码 Java手机端工程项目管理系统源码带客户端操作手册

客户端下载: 1、安卓手机端 先下载“PP助手”应用商店,再在PP助手中搜索“star工程”下载安装。 2、苹果手机端 苹果手机APPstore中搜索“star工程”下载安装。 建设综合工程项目管理系统,让公司业务管理实现可记录,可追踪&#…

数字图像处理(入门篇)七 图像数据预处理之灰度变化

目录 一 常用操作 1 反转 2 对比度增强 3 对比度压缩 4 伽马矫正 二 实践 1 反转、对比度增强和对比度压缩 (1) 代码 (2) 结果图 2 伽马矫正 (1) 代码 (2) 结果图 (3) 结论 (4) np.power函数 图像灰度变换:将原始图像中的每一个点的灰度值&#xff0c…

尚医通 (三十三) -------- 阿里云OSS、用户认证与就诊人

目录一、阿里云 oss1. 开通 对象存储 OSS 服务2. 使用 SDK 文档3. 文件服务实现搭建 service-oss 模块测试SDK封装 service 接口封装 controller 接口二、用户认证1. 需求分析2. api 接口3. 前端4. 预约挂号页面调整三、就诊人管理1. 需求分析2. api 接口3. 前端四、平台用户管…

最新阿里云服务器和GPU服务器长期优惠活动价格表

阿里云服务器优惠活动是长期的,在12月优惠活动大促期间云服务器价格会有所调整,主机教程网现在给大家分享最新的阿里云服务器优惠活动价格表。现在阿里云双十一优惠活动刚刚结束,随之而来的就是双12优惠活动,关于阿里云服务器、对…

camunda入门教程及实现原理

1、camunda简介 1、介绍: Camunda是一种工作流引擎。 Camunda BPM 是一个轻量级、开源灵活的工作流框架,是由Java开发的一个纯Java库。它的核心是一个在Java虚拟机内部运行的原生BPMN 2.0流程引擎,因此它可以嵌入到任何Java应用程序或运行时…

Linux 添加环境变量的两种方法 exprot 临时法 vi /etc/profile永久法

编写一个shell脚本之后,怎么可在任意目录运行改脚本呢?为什么别人写的脚本可以直接运行,自己写的脚本就会出现 -bash: XXXXX.sh: command not found 这样的错误呢? 1、举例验证,先新建一个HelloWorld的shell脚本&…

Alibaba 工具型技术系列,实战技术针对于项目中常用的 Excel 操作指南

ExcelIgnoreUnannotated 默认不加 ExcelProperty 的注解的都会参与读写,加了不会参与 通用参数 ReadWorkbook,ReadSheet 都会有的参数,如果为空,默认使用上级。 converter 转换器,默认加载了很多转换器。也可以自定义。 rea…