1. 数据可以永久保存 !
2. 使用SQL语句,查询方便效率高 !
3. 管理数据方便 !
结构化查询语言 (Structured Query Language) 简称 SQL ,是一种数据库查询语言 。
用于存取数据、查询、更新和管理数据库系统 。
MySQL 是一个关系型数据库管理系统,属于Oracle旗下产品。MySQL是最流行的关系型数据库管理系统之一,在WEB应用方面,MySQL 是最好的关系型数据库管理系统应用软件之一。在 Java 企业级开发中非常常用,因为 MySQL 是开源免费的,并且方便扩展 。
第一范式:每个列都不可以再拆分
第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分
第三范式:在第二范式的基础上,非主键列完全依赖于主键,而不依赖于其他非主键 。
在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由。比如性能。事实上我们经常会为了性能而妥协数据库的设计 。
MySQL 服务器通过权限表来控制用户对数据库的访问,权限表存放在 MySQL 数据库里,由 mysql_install_db 脚本初始化。这些权限表分别 user,db,table_priv,columns_priv 和 host 。
下面分别介绍一下这些表的结构和内容:
user 权限表:记录允许连接到服务器的用户帐号信息,里面的权限是全局级的 。
db 权限表:记录各个帐号在各个数据库上的操作权限 。
table_priv 权限表:记录数据表级的操作权限 。
columns_priv 权限表:记录数据列级的操作权限 。
host 权限表:配合db权限表对给定主机上数据库级操作权限作更细致的控制 ,这个权限表不受GRANT和 REVOKE语句的影响 。
有三种格式,statement,row和mixed。
1. statement模式下,每一条会修改数据的sql都会记录在binlog中。不需要记录每一行的变化,减少 了binlog日志量,节约了IO,提高性能。由于sql的执行是有上下文的,因此在保存的时候需要保存 相关的信息,同时还有一些使用了函数之类的语句无法被记录复制。
2. row级别下,不记录sql语句上下文相关信息,仅保存哪条记录被修改。记录单元为每一行的改动, 基本是可以全部记下来但是由于很多操作,会导致大量行的改动(比如altertable),因此这种模式的 文件保存的信息太多,日志量太大。
3. mixed,一种折中的方案,普通操作使用statement记录,当无法使用statement的时候使用row。 此外,新版的MySQL中对row级别也做了一些优化,当表结构发生变化的时候,会记录语句而不是逐行 记录。
MySQL数据类型 分类 类型名称 说明 整数类型 tinyint 很小的整数(8位二进制) smallint 小的整数(16位二进制) int(integer) 普通大小的整数(32位二进制) 小数类型 float 单精度浮点数 double 双精度浮点数 decimal(m,d) 压缩严格的定点数 日期类型 year YYYY
1998
time HH:MM:SS
20:58:14
date YYYY -MM -DD
1998 -09 -14
datetime YYYY -MM -DD HH:MM:SS
1998 -09 -14 20:58:20
timestamp YYYY-MM-DD HH:MM:SS
1998 -09 -14 20:57:54
文本、二进制 类型 CHAR(S) S 为 0~65535 之 间的整数 VARCHAR(S) S 为 0~65535 之 间的整数 TINYBLOB 允许长度0~255字节 BLOB 允许长度0~65535字节 MEDIUMBLOB 允许长度0~167772150字节 LONGBLOB 允许长度0~4294967295字节 TINYTEXT 允许长度0~255字节 TEXT 允许长度0~65535字节 MEDIUMTEXT 允许长度0~167772150字节 LONGTEXT 允许长度0~4294967295字节 VARBINARY(M) 允许长度0~M个字节的变长字节字符串 BINARY(M) 允许长度0~M个字节的定长字节字符
整数类型,包括TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT,分别表示1字节、2字节、3字 节、4字节、8字节整数。任何整数类型都可以加上UNSIGNED属性,表示数据是无符号的,即非负整 数。
长度:整数类型可以被指定长度,例如:INT(11)表示长度为11的INT类型。长度在大多数场景是没有意 义的,它不会限制值的合法范围,只会影响显示字符的个数,而且需要和UNSIGNEDZEROFILL属性配合 使用才有意义。
例子,假定类型设定为INT(5),属性为UNSIGNEDZEROFILL,如果用户插入的数据为12的话,那么数据库实际存储数据为00012。
实数类型,包括FLOAT、DOUBLE、DECIMAL。DECIMAL可以用于存储比BIGINT还大的整型,能存储 精确的小数。而FLOAT和DOUBLE是有取值范围的,并支持使用标准的浮点进行近似计算。 计算时FLOAT和DOUBLE相比DECIMAL效率更高一些,DECIMAL你可以理解成是用字符串进行处理。
字符串类型,包括VARCHAR、CHAR、TEXT、BLOBVARCHAR用于存储可变长字符串,它比定长类型更 节省空间。
VARCHAR使用额外1或2个字节存储字符串长度。列长度小于255字节时,使用1字节表示,否则使用2字 节表示。
VARCHAR存储的内容超出设置的长度时,内容会被截断。
CHAR是定长的,根据定义的字符串长度分配足够的空间。
CHAR会根据需要使用空格进行填充方便比较。
CHAR适合存储很短的字符串,或者所有值都接近同一个长度。
CHAR存储的内容超出设置的长度时,内容同样会被截断。
使用策略: 对于经常变更的数据来说,CHAR比VARCHAR更好,因为CHAR不容易产生碎片。 对于非常短的列,CHAR比VARCHAR在存储空间上更有效率。 使用时要注意只分配需要的空间,更长的列排序时会消耗更多内存。尽量避免使用TEXT/BLOB类型,查 询时会使用临时表,导致严重的性能开销。
枚举类型(ENUM),把不重复的数据存储为一个预定义的集合。有时可以使用ENUM代替常用的字符 串类型。ENUM存储非常紧凑,会把列表值压缩到一个或两个字节。ENUM在内部存储时,其实存的是 整数。尽量避免使用数字作为ENUM枚举的常量,因为容易混乱。排序是按照内部存储的整数 日期和时间类型,尽量使用timestamp,空间效率高于datetime,用整数保存时间戳通常不方便处理。 如果需要存储微妙,可以使用bigint存储。 看到这里,这道真题是不是就比较容易回答了。
日期和时间类型,尽量使用timestamp,空间效率高于datetime,用整数保存时间戳通常不方便处理。 如果需要存储微妙,可以使用bigint存储。
存储引擎Storageengine:MySQL中的数据、索引以及其他对象是如何存储的,是一套文件系统的实现。
常用的存储引擎有以下:
1. Innodb引擎:Innodb引擎提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。
2. MyIASM引擎(原本Mysql的默认引擎):不提供事务的支持,也不支持行级锁和外键。
3. MEMORY引擎:所有的数据都在内存中,数据的处理速度快,但是安全性不高。
MyISAM | InnoDB | |
---|---|---|
存储结构 | 张表被存放在三个文件:frm-表格定义、MYD(MYData)-数据 文件、MYI(MYIndex)-索引文件 | 所有的表都保存在同一个数据文件中 (也可能是多个文件,或者是独立的表 空间文件),InnoDB表的大小只受限 于操作系统文件的大小,一般为2GB |
存储空间 | MyISAM可被压缩,存储空间较 小 | InnoDB的表需要更多的内存和存储, 它会在主内存中建立其专用的缓冲池用 于高速缓冲数据和索引 |
可移植性、备份 及恢复 | 由于MyISAM的数据是以文件的 形式存储,所以在跨平台的数据 转移中会很方便。在备份和恢复 时可单独针对某个表进行操作 | 免费的方案可以是拷贝数据文件、备份 binlog,或者用mysqldump,在数据 量达到几十G的时候就相对痛苦了 |
文件格式 | 数据和索引是分别存储的,数 据.MYD,索引.MYI | 数据和索引是集中存储的,.ibd |
记录存储顺序 | 按记录插入顺序保存 | 按主键大小有序插入 |
外键 | 不支持 | 支持 |
事务 | 不支持 | 支持 |
锁支持(锁是避 免资源争用的一 个机制,MySQL 锁对用户几乎是 透明的) | 表级锁定 | 行级锁定、表级锁定,锁定力度小并发 能力高 |
SELECT | MyISAM 更优 | |
INSERT、 UPDATE、 DELETE | InnoDB 更优 | |
select count(*) | myisam更快,因为 MyISAM 内部维护了一个计数器,可以直接调取。 | |
索引的实现方式 | B+树索引,myisam是堆表 | B+树索引,Innodb是索引组织表 |
哈希索引 | 不支持 | 支持 |
全文索引 | 支持 | 不支持 |
1. InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。
2. InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效。
3. MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。
4. InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会 非常高效。
插入缓冲(insertbuffer)
二次写 (doublewrite)
自适应哈希索引 (ahi)
预读 (readahead)
如果没有特别的需求,使用默认的Innodb即可。
MyISAM:以读写插入为主的应用程序,比如博客系统、新闻门户网站。
Innodb:更新(删除)操作频率也高,或者要保证数据的完整性;并发量高, 支持事务和外键。比如OA自动化办公系统。
索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新 数据库表中数据。索引的实现通常使用B树及其变种 B+树。 更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。
上图中,根据 id 查询记录,因为id字段仅建立了主键索引,所以此 SQL 执行可选的索引只有主键索引,如果有多个,最终会选一个较优的作为检索的依据 。
1__ 增加一个没有建立索引的字段
2__ alter table student add sex char(1) ;
3__ 按 sex 检索时可选的索引为null
4__ explain select * from student where sex= '男' ;
!!!注意提示:
可以尝试在一个字段未建立索引时,根据该字段查询的效率,然后对该字段建立索引 (alter table 表名 add index(字段名) ),同样的SQL执行的效率,你会发现查询效率会有明显的提升(数据量越大越明显)。
order by
当我们使用 order by 将查询结果按照某个字段排序时,如果该字段没有建立索引,那么执行计划会将查询出所有数据使用外部排序(将数据从硬盘分批读取到内存使用内部排序, 最后合并排序结果),这个操作是很影响性能的,因为需要将查询涉及到的所有数据从磁盘中读到内存 (如果单条数据过大或者数据量过多都会降低效率),更别提读到内存之后的排序了。 但是如果我们对该字段建立索引 [^^^ 注意提示] ,那么由于索引本身是有序的,因此直接按照索引的顺序和映射关系逐条取出数据即可。而且如果是分页的,那么只用取出索引表某个范围内的索引对应的数据,而不用像上述那取出所有数据进行排序再返回某个范围内的数据。(从磁盘取数据是最影响性能的)
join
对 join 语句匹配关系(on)涉及的字段建立索引能够提高效率
索引覆盖
如果要查询的字段都建立过索引,那么引擎会直接在索引表中查询而不会访问原始数据(否则只要有一个字段没有建立索引就会做全表扫描),这叫索引覆盖。 因此我们需要尽可能的在 select 后只写必要的查询字段,以增加索引覆盖的几率。 这里值得注意的是不要想着为每个字段建立索引,因为优先使用索引的优势就在于其体积小 。
主键索引 : 数据列不允许重复,不允许为NULL, 一个表只能有一个主键 。
唯一索引 : 数据列不允许重复,允许为NULL值, 一个表允许多个列创建唯一索引 。
创建唯一索引的方式:
ALTER TABLE [ 表名 ] ADD UNIQUE [ 表列名 ] ;
创建唯一组合索引的方式:
ALTER TABLE [ 表名 ] ADD UNIQUE [ 列1,列2 ] ;
普通索引 : 基本的索引类型,没有唯一性的限制,允许为NULL值 。
创建普通索引的方式:
ALTER TABLE [ 表名 ] ADD INDEX [ 索引名 ] [ 表列名 ] ;
创建普通组合索引的方式:
ALTER TABLE [ 表名 ] ADD INDEX [ 索引名 ] [ 列1,列2,列3 ] ;
全文索引:是目前搜索引擎使用的一种关键技术 。
创建全文索引的方式
ALTER TABLE [ 表名 ] ADD FULLTEXT [ 表列名 ] ;
1. 索引的基本原理
索引用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表。 索引的原理很简单,就是把无序的数据变成有序的查询 。
1. 把创建索引列的内容进行排序 。
2. 把排序结果生成倒排表 。
3. 在倒排表内容上拼上数据地址链 。
4. 在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据 。
2. 索引算法有哪些 ?
索引算法有 BTree算法 和 Hash算法
BTree 算法:
BTree 是常用的 mysql 数据库索引算法,也是 mysql 默认的算法。因为它不仅可以被用在=、>、>=、<、<=、between 这些比较操作符上,而且还可以用于 like 操作符,它的查询条件是一个不以通配符开头的 常量,例如:
1. 查询条件不以通配符开头的常量:
SELECT * FROM STUDENT WHERE NAME LIKE '娜%'
2.查询条件以通配符开头,或者没有使用常量,则不会使用索引:
SELECT * FROM STUDENT WHERE NAME LIKE '贺%'
如下图所示!!!
Hash算法 :
Hash索引只能用于对等比较,例如 =、<=>(相当于=)操作符。由于是一次定位数据,不像BTree索引需要从根节点到枝节点才能访问到页节点这样多次IO访问,所以检索效率远高于BTree索引 。
3. 创建索引的原则 [ 重点 ]
1)左前缀匹配原则,组合索引非常重要的原则,mysql 会一直向右匹配直到遇到范围查询(>、<、Like、Between)就停止匹配,比如 a=1 and b=2 and c>3 and d=4 如果建立(a,b,c,d) 顺序的索引,d 是用不到 索引的,如果建立 (a,b,d,c) 的索引则都可以用到,a、b、d 的顺序可以任意调整 。= 和 in 可以乱序,比如a=1 and b=2 and c=3建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
2)较频繁作为查询条件的字段才去创建索引
3)更新频繁字段不适合创建索引
4)若是不能有效区分数据的列不适合做索引列(如性别,男女未知,多也就三种,区分度实在太低)
5)尽量的扩展索引,不要新建索引。比如表中已经有 a 的索引,现在要加 (a,b) 的索引,那么只需要修改原来的索引即可
6)定义有外键的数据列一定要建立索引
7)对于那些查询中很少涉及的列,重复值比较多的列不要建立索引
8)对于定义为 text、image、bit 的数据类型的列不要建立索引
4. 创建索引的方式
第一种方式:在执行 CREATE TABLE 时创建索引 :
CREATE TABLE USER_INDEX(
id INT PRIMARY KEY, # 主键索引
first_name VARCHAR(16),
last_name VARCHAR(16),
id_card VARCHAR(16),
information TEXT,
KEY NAME (first_name,last_name), # 普通组合索引
FULLTEXT KEY (information), # 全文索引
UNIQUE KEY (id_card)); # 唯一索引第二种方式:使用 ALTER TABLE 命令去增加索引 :
ALTER TABLE [ table_name ] ADD UNIQUE [ column_list ] ;
ALTER TABLE 用来创建普通索引、UNIQUE——> 唯一索引 、——> PRIMARY KEY 主键索引。 其中 table_name 是要增加索引的表名,column_list 指出对哪些列进行索引,多列时各列之间用逗号分隔。 索引名 index_name 可自己命名,缺省时,MySQL 将根据第一个索引列赋一个名称。另外, ALTER TABLE 允许在单个语句中更改多个表,因此可以在同时创建多个索引 。
第三种方式:使用 CREATE INDEX 命令创建 :
CREATE INDEX [ index_name ] ON table_name [ column_list ] ;
CREATE INDEX 可对表增加 普通索引 或 UNIQUE索引 。( 注意:不能创建PRIMARY KEY 索引 !!!)
5. 删除索引的方式
根据索引名删除 普通索引、唯一索引、全文索引 :ALTER TABLE [ 表名 ] DROP KEY [ 索引名 ]
删除主键索引 ALTER TABLE [ 表名 ] DROP PRIMARY KEY(因为主键只有一个)。
注意:如果主键自增长,那么不能直接执行此操作(自增长依赖于主键索引)
上图说明,需要取消自增长再行删除:
alter table user_index
MODIFY id int, #修改列属性
drop PRIMARY KEY注意:但通常不会删除主键,因为设计主键一定与业务逻辑无关。
6. 创建索引时需要注意什么 ?
1. 非空字段:应该指定为 NOT NULL ,除非想存储 NULL 。在 mysql 中,含有空值很难进行查询优化,因为它们使得索引的统计信息以及比较运算更加复杂 。应该用 0、一个特殊的值或者一个空串代替空值
2. 取值离散大的字段:[ 变量各个取值之间的差异程度 ] 的列放到联合索引的前面,可以通过count() 函数查看字段的差异值,返回值越大说明字段的唯一值越多字段的离散程度高
3. 索引字段越小越好:数据库的数据存储以页为单位、一页存储的数据越多则一次IO操作获取的数据越大 效率越高
7. 使用查询索引一定能提高查询的性能吗 ?
通常,通过索引查询数据比全表扫描要快、但是我们也必须注意到它的代价。
索引 需要空间来存储,也需要定期维护,每当有记录在表中增减或索引列被修改时,索引本身也会被修改。这意味着每条记录的INSERT,DELETE,UPDATE 将为此多付出4、5次的磁盘I/O。因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。使用索引查询不一定能提高查询性能。
索引范围查询 ( INDEX RANGESCAN ) 适用于两种情况:
1)基于一个范围的检索,一般查询返回结果集小于表中记录数的30%
2)基于非唯一性索引的检索
关于索引:由于索引需要额外的维护成本,因为索引文件是单独存在的文件, 所以当我们对数据的增、改、删,都会产生额外的对索引文件的操作,这些操作需要消耗额外的IO,会降低增、改、删的执行效率。所以、在我们删除数据库百万级别数据的时候,查询MySQL官方手册得知删除数据的速度和创建的索引数量是成正比的
具体操作:
1. 先删除索引( 此时大概耗时三分多钟 )
2. 然后删除其中无用数据( 此过程需要不到两分钟 )
3. 删除完成后重新创建索引 ( 此时数据量较少 ) 创建索引也非常快,约十分钟左右。
4. 对比之前的直接删除绝对是要快速很多,更别说万一删除中断、一切删除都将会回滚
前缀索引语法:alter table student add index (email(7)),使用字段值的前7个字符建立索引,默认是使用字段的全部内容建立索引。
前提:前缀的标识度高。比如密码就适合建立前缀索引,因为密码几乎各不相同。
实操的难度:在于前缀截取的长度。 我们可以利用 select count(*)/count(distinct left(email,prefixLen)) from student; 通过从调整 prefixLen 的值( 从1自增 )查看不同前缀长度的一个平均匹配度,接近1时就可以了(表示一个密码的前 prefixLen 个字符几乎能确定唯一一条记录 )
MySQL 可以使用多个字段同时建立一个索引,叫做联合索引。
在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引。
具体原因 : MySQL使用索引时需要索引有序,假设现在建立了 " name,age,school "的联合索引,那么索引的排序为: 先按照 name排序,如果 name 相同,则按照 age 排序,如果 age 的值也相等,则按照 school 进行排 序。 当进行查询时,此时索引仅仅按照name 严格有序,因此必须首先使用 name 字段进行等值查询,之后对 于匹配到的列而言,其按照 age 字段严格有序,此时可以使用 age 字段用做索引查找,以此类推。因此在 建立联合索引的时候应该注意索引列的顺序,一般情况下,将查询 需求频繁或者字段选择性高 的列放在前面。此外可以根据特例的查询或者表结构进行单独的调整 。
事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库 从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。
事务最经典也经常被拿出来说例子就是转账了。 假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小 红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。
关系型数据库需要遵循 ACID 规则,具体内容如下 :
原子性:一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么
全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作 。一致性:根据定义,一致性是指事务执行前后,数据处于一种合法的状态,这种状态是语义上的而不是语法上的 。[
那什么是合法的数据状态呢 ?
这个状态是满足预定的约束就叫做合法的状态,再通俗一点,这状态是由你自己来定义的。满足这个状态,数据就是一致的,不满足这个状态,数据就是不一致的!如果无法保证一致性会怎么样 ?
例一:A账户有200元,转账300元出去,此时A账户余额为-100元。你自然就发现了此时数据是不一致的,为什么呢?因为你定义了一个状态,余额这列必须大于0。
例二:A账户200元,转账50元给B账户,A账户的钱扣了,但是B账户因为各种意外,余额并没有增加。你也知道此时数据是不一致的,为什么呢?因为你定义了一个状态,要求A+B的余额必须不变。]隔离性:并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的 。[ 未提交事务之前, 甲向乙转了两次帐,执行了第一次转账操作后,第二次转账未开始,此时另外一个事务查看甲账户余额操作开始执行,则其看到支票帐户的余额并没有任何变化 ]
持久性:一个事务被提交之后,它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响 。
怎么保持数据一致性 ?
1. 从数据库层面,数据库通过原子性、隔离性、持久性来保持一致性。ACID四大特性之中C(一致性)是目的、A(原子性)、C(隔离性)、D(持久性)是手段,是为了保持数据一致性、数据库提供的手段。数据库必须实现AID三大特性,才有可能实现一致性。例如原子性无法保证,显然一致性也无法保证。但是,如果你在事务里故意写出违反约束的代码,一致性还是无法保证的。
例如,你在转账的例子中,你的代码里故意不给乙账户加钱,那一致性还是无法保证。因此,还必须从应用层角度考虑。
2. 从应用层面,通过代码判断数据库数据是否有效,然后决定回滚还是提交数据!
脏读:(Drity Read) 脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
脏读示例:
张三的工资为 5000 、事务 A 中把他的工资改为 8000、但事务 A 尚未提交 。
与此同时,事务 B 正在读取张三的工资,读取到张三的工资为 8000 。
随后,事务 A 发生异常,而回滚了事务。张三的工资又回滚为 5000 。
最后,事务 B 读取到的张三工资为 8000 的数据即为脏数据,事务 B 做了一次脏读 。
不可重复读:(Non-repeatable read) 在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务使原有的数据更新了 。
不可重复读示例:
在事务 A 中,读取到张三的工资为 5000,操作没有完成,事务还没提交。
与此同时,事务 B 把张三的工资改为 8000,并提交了事务。
随后,在事务 A 中,再次读取张三的工资,此时工资变为 8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。
幻读:(Phantom Read) 在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数 据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据 是它先前所没有的 。
目前工资为 5000 的员工有 10 人,事务 A 读取所有工资为 5000 的人数为 10 人 。
此时,事务 B 插入一条工资也为 5000 的记录 。
这是,事务 A 再次读取工资为 5000 的员工,记录为 11 人。此时产生了幻读 。