ChatGPT3.5使用体验( 八 )


如何优化呢?MySQL 中使用了 rowid 排序来解决这种场景 。
rowid 排序原理的大致思路就是,不会将 SQL 语句中后面的所有字段都放入到中,而是只会将需要被排序的字段和主键 id 放入到中,对应到本文的例子中就是:将 name 字段和主键 id 字段放入到中 。
那么 MySQL 判断单行长度的标准是什么呢?
通过 data 字段,MySQL 中专门用于控制排序的行数据长度的字段,如果超过了这个长度,那么就会使用 rowid 排序算法了 。
来看下 rowid 排序的过程
1、首先 MySQL 会为应用进程分配一块内存大小为的内存,然后确定放入的字段,假定这时候字段city,name,age的长度之和已超过了 data 的限制,这时候就需要用到 rowid 排序了,这时候放入到中的字段只有要排序的列 name 字段和主键 id;
2、查询首先使用索引 city 来确定查询的数据,然后查询到的数据都会通过查询到的主键 id 进行一次回表操作(第一次回表),查询到的name,id字段放入到中;
3、所有的数据都放入到排序内存之后,会根据排序字段对中的数据进行排序;
4、遍历排序结果,取前 1000 行,并按照 id 的值回到原表中取出city、name和age三个字段返回给客户端(第二次回表),排序结束 。
可以看到相比于相比全字段排序而言,rowid 排序的多了一次回表的查询操作 。
总结下来就是
如果 MySQL 实在是担心排序内存太小,会影响排序效率,才会采用 rowid 排序算法,这样排序过程中一次可以排序更多行,但是需要再回到原表去取数据 。
如果 MySQL 认为内存足够大,会优先选择全字段排序,把需要的字段都放到中,这样排序后就会直接从内存里面返回查询结果了,不用再回到原表去取数据 。
这也就体现了 MySQL 的一个设计思想:如果内存够,就要多利用内存,尽量减少磁盘访问 。
对于表来说,rowid 排序会要求回表多造成磁盘读,因此不会被优先选择 。
MySQL 之所以需要生成临时表,并且在临时表上做排序操作,其原因是原来的数据都是无序的 。如何优化,在排序的字段上建索引 。因为索引是有序的,给排序字段建立索引,就能用到索引的有序性来排序了 。
联合索引
针对上面排序的两种方式,想要拥有更好的查询性能,我们可以考虑加联合索引,例如上面的栗子,我们可以考虑给city,name建立联合索引,因为索引是有序的,这样既能满足查询的优化需求,也能满足排序的需求 。
这是最优解吗?
因为查询的 age 字段不在索引内,所以查询排序之后还是需要进行回表操作的 。
这个查询的最优解就是建立city,name,age字段的联合索引也叫覆盖索引,这样通过索引就能返回所有查询的值了 。
主键选择自增还是使用 UUID
主键是用自增还是 UUID 呢?
主键索引最好是自增的 。
创建主键索引默认为聚簇索引,数据被存放在了 B+Tree 的叶子节点上 。也就是说,同一个叶子节点内的各个数据是按主键顺序存放的,因此,每当有一条新的数据插入时,数据库会根据主键将其插入到对应的叶子节点中 。
1、如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页;
2、如果使用非自增主键(如uuid),由于每次插入主键的值近似于随机,因此每次新纪录都要被插到索引页的随机某个位置,此时 MySQL 为了将新记录插到合适位置而移动数据,甚至目标页面可能已经被回写到磁盘上而从缓存中清掉,此时又要从磁盘上读回来,这增加了很多开销,同时频繁的移动、分页操作造成索引碎片,得到了不够紧凑的索引结构,后续不得不通过 TABLE来重建表并优化填充页面 。