
长...
最大长度:char是255,varchar是65535,单位是字符(而不是字节)。
尾随空格:char会将尾随空格去掉,而varchar不会。
如果char字段有唯一索引,
a
和a_
会提示唯一索引冲突。
存储空间占用:varchar会占用额外的1~2字节来存储字符串长度。如果最大长度超过255,就需要2字节,否则1字节。(变长字段长度列表)
CHAR(N)
字段,如果实际存储数据小于N
字节,会填充空格到N
个字节。在InnoDB存储引擎下、DYNAMIC行格式下。
CHAR(N)
字段,如果实际存储数据小于N
字节,会填充空格到N
个字节。BINARY / VARBINARY : 存放二进制字符串
BLOB / TEXT : 存放较大的数据,分别采用二进制和字符方式存储
ENUM:枚举
为了提升查询性能,数据从磁盘读取出来后,缓存到内存当中,后续查询缓存命中,提高了数据库的读写性能。
为此,Innodb存储引擎设计了缓冲池。默认缓冲池大小为128MB。可以通过调整 innodb_buffer_pool_size
参数来设置 Buffer Pool 的大小,一般建议设置成可用物理内存的 60%~80%。
有了缓冲池之后:
Buffer Pool同样按照页来划分,在 MySQL 启动的时候,InnoDB 会为 Buffer Pool 申请一片连续的内存空间,然后按照默认的16KB的大小划分出一个个的页, Buffer Pool 中的页就叫做缓存页。
缓存池是连续的内存空间,当MySQL运行一段时间后,这篇空间既有使用的也有空闲的,为了更快遍历到空闲的缓存页,构建了一个Free链表。
Free链表中的包括了一个头节点,来包含链表信息,空闲缓存页的控制块作为链表的节点,每个控制块又包含对应缓存页的地址。所以相当于Free链表节点对应一个个空闲的缓存页。
每次修改数据,修改Buffer Pool 中数据所在的页,然后将其页设置为脏页,最后由后台线程将脏页写入到磁盘。
为了快速知道哪些是脏页,而有了Flush链表,记录哪些是脏页,同上面的Free链表结构相似。
由于缓冲池的大小有限,希望尽可能留下频繁访问的数据,去除很少访问的数据,从而提高命中率。
最简单的实现:LRU(Least recently used)算法。
该算法的思路是,链表头部的节点是最近使用的,而链表末尾的节点是最久没被使用的。那么,当空间不够了,就淘汰最久没被使用的节点,从而腾出空间。
简单的 LRU 算法的实现思路是这样的:
最简单的LRU没有被采用,考虑到以下两个问题:
靠近被访问的数据的数据,在未来大概率会被访问,所以MySQL在加载数据时会提前把它相邻的数据一起加入过来,减少磁盘IO。
当时这些预加载的数据可能未被访问,从而造成预读失效。
当使用LRU算法时,就可能会造成加预读页放在前排位置,缓冲池空间不够时淘汰了末尾的页,而这些预读页如果一直不被访问,会占用前排位置,并且淘汰了可能访问的页。
MySQL将LRU算法划分前半部分的young区域,后半部分的old区域,一般按照63:37划分。
划分这两个区域后,预读的页就只需要加入到 old 区域的头部,当页被真正访问的时候,才将页插入 young 区域的头部。如果预读的页一直没有被访问,就会从 old 区域移除,这样就不会影响 young 区域中的热点数据。
优先去除old区域的页。
某一个 SQL 语句扫描了大量的数据时,在 Buffer Pool 空间比较有限的情况下,可能会将 Buffer Pool 里的所有页都替换出去,导致大量热数据被淘汰了,等这些热数据又被再次访问的时候,由于缓存未命中,就会产生大量的磁盘 IO,MySQL 性能就会急剧下降,这个过程被称为 Buffer Pool 污染。
发生全表扫描时,很容易出现这样的问题,热点数据被替换掉。因此想到提高数据进入young区域的门槛,通过判断是不是频繁访问,而不是短暂访问一次。
MySQL 是这样做的,进入到 young 区域条件增加了一个停留在 old 区域的时间判断。
- 如果后续的访问时间与第一次访问的时间在某个时间间隔内,那么该缓存页就不会被从 old 区域移动到 young 区域的头部;
- 如果后续的访问时间与第一次访问的时间不在某个时间间隔内,那么该缓存页移动到 young 区域的头部;
同时满足被访问,并且在old区域停留在超过1s两个条件,才会被插入到young区域。
另外,MySQL 针对 young 区域其实做了一个优化,为了防止 young 区域节点频繁移动到头部。young 区域前面 1/4 被访问不会移动到链表头部,只有后面的 3/4被访问了才会。
为了提高性能,会在一定时机统一批量刷入磁盘。并且InnoDB具有Write Ahead Log策略,先写日志再写入磁盘,通过日志可以进行崩溃恢复,防止MySQL宕机未来得及写入磁盘。
当满足以下情况会触发脏页的刷新:
对比下,普通索引重复查找确认,性能差距不大。毕竟是按页读写的,查到k=5,所在的数据也就在内存当中了,重复查找就在这个内存页中指针寻找和计算。
查询时,不在缓冲池,磁盘IO读入。
当需要更新一个数据页时,如果数据页在内存中就直接更新(标记为脏页),而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InnoDB 会将这些更新操作缓存在 change buffer 中,这样就不需要从磁盘中读入这个数据页了。
在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行 change buffer 中与这个页有关的操作。
change buffer在内存中和磁盘中都有拷贝,是持久化数据。
通过先修改change buffer,减少磁盘IO提高了岁都,同时避免数据写入内存,提高内存利用率。
change buffer使用的是buffer pool内存,因此不能无限量增加。
对于唯一索引来说,需要先判断现在表中是否存在k=4(保证唯一性),必须将数据页读入内存才能判断 ,读入后不如直接读内存中更新了。因此change buffer应用在普通索引上,因为普通索引不用考虑唯一性,直接记录。
当更新记录的目标页不在内存中,唯一索引需要先加载到内存当中,这个磁盘IO消耗是巨大的。而普通索引直接使用change buffer,大大降低了成本。
在merge更新之前,change buffer记录越多,说明节省的成本越大,收益大。
因此change buffer适合写多读少的场景,比如账单类、日志类的系统。
如果一个业务更新模式写入之后马上查询,那么就去看内存中有没有缓存,没有的话需要加载到内存中,立即触发merge操作,这样操作反而增加了merge的成本。
综上考虑,查询过程性能差距不大,在更新时由于change buffer存在,应该尽可能选择普通索引。
如果所有的更新后面,都马上伴随着对这个记录的查询,那么你应该关闭 change buffer。而在其他情况下,change buffer 都能提升更新性能。