1、什么是索引?
索引是帮助mysql高效获取数据的排好序的数据结构。
1.1索引的特点
1 索引一个排序的数据结构可以加速数据库的检索速度。2索引降低了数据库Insert、Update、Delete等维护任务的难度。3 MySQL索引只能创建在表上,不能创建在视图上。查询处理器执行SQL语句,一个表上,一次只能使用一个索引
1.2 索引的优点
1 提高数据检索的效率,降低数据库的IO成本创建唯一性的索引,保证数据库表中每一行数据的唯一性。2加速表和表之间的连接。3在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。
1.3 索引的缺点
创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度。
1.4 那么为什么InnoDB的主键最好要搞成有序的?
InnoDB中主键索引是聚集索引,所有数据都存在主键索引所在的聚集索引的B+Tree结构的叶子节点中。如果每次插入的主键是大小随机的话,每次数据进来找到的叶子节点的位置是随机的,这样的话,有些叶子节点所在页本来就排满了,结果又来了一条数据,就势必要引起页分裂,所以导致性能下降;但是如果主键是有序的话,每次进行都找到当前叶子前面的位置,一个一个叶子按顺序排满一个页再排一个页,就不会又页分裂的问题了。所以自增主键对于InnoDB这种使用B+Tree索引的存储引擎来说,性能更好。
2、为什么索引不用二叉树或者红黑树?
如果使用二叉树,索引自增的话二叉树就会一直向右递增,这样的话就变成了链表,并没有减少查找数据的次数。
如果使用红黑树,红黑树其实就是一个二叉平衡树。当树的一侧高于另一侧太多时,会自动旋转平衡。但是如果当索引太多时,树的高度太高,查询速度慢。
3、B树和B+树的区别
B+树是B树的变体,是一种查询性能更好的B树。B+树是一种平衡查找树 。在B+树中,所有记录节点都是按键值的大小顺序存放在同一层的叶节点中,各叶结点指针进行连接。
1、B树的中间节点保存节点和数据,B+树的中间节点不保存数据,数据保存在叶子节点中;
所以磁盘页能容纳更多的节点元素,更"矮胖";
2、B树的叶子节点没有使用链表相连。B+树的叶子节点通过指针连接,从左到右顺序排列;
3、B+树的非叶子节点与叶子节点冗余,B+树的叶子节点存放了用户插入的所有元素;
4、对于范围查找到说,B树要从头到尾查找,而B+树只需要在一定的范围内的叶子节点中查 找就可以;
1)B+树的磁盘读写代价更低
B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了;
2)B+树查询效率更加稳定
由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当;
3)B+树便于范围查询(最重要的原因,范围查找是数据库的常态)
B树在提高了IO性能的同时并没有解决元素遍历的我效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作或者说效率太低。
补充:B树的范围查找用的是中序遍历,而B+树用的是在链表上遍历。
B+树:
4、myslam和innodb的区别
myslam是非聚集索引:.frm(存储表的结构)、myd(存储表中的数据)、myi(存储索引)
innodb是聚集索引:.frm(存储表的结构)、idb(存储主键索引和数据) mysql默认使用的是innodb
innodb中也只有主键索引的叶子节点存储的数据,辅助索引存储的是主键索引,使用辅助索引查询时需要回表查询,先查询道主键索引再利用主键索引去查询数据。
1、 InnoDB支持事务,MyISAM不支持;
2、 InnoDB支持外键,而MyISAM不支持;
3、InnoDB支持表、行(默认)级锁,而MyISAM支持表级锁;
4、Innodb不支持全文索引,而MyISAM支持全文索引,在涉及全文索引领域的查询效率上 MyISAM速度更快高;
InnoDB:
MyISAM:
innodb和myslam底层都是b+树的结构,只不过innodb是聚集索引,主键索引的叶子节点上存储的是数据,二级索引(非主键的索引都是二级索引)的叶子节点存储的是主键索引。而myslam是非聚集索引,主键索引下存储对是数据在磁盘上存储的位置。
innodb使用二级索引来查询都需要进行回表查询。
5、如何避免innodb中非主键索引的回表查询
回表查询就是在数据查询过程中MySQL内部需要两次查询。既先定位查询数据所在表的主键值,在根据主键定位行记录。
这里需要使用联合索引(覆盖索引),可以将需要查询的字段都设置成索引,将需要当做第一个查询条件的放联合索引的最前面(最左前缀原则),这样就可以走联合索引进行查询,查询出来叶子节点包含主键索引 ,联合索引中的值就是需要查询的字段,所以就不用再走回表查询了。
覆盖索引:
如果执行一个查询语句不经过两次B+Tree查询直接得到要查询的值,这个时候就不需要回表,也就是说在这个查询中,索引"覆盖了"查询 ,这个称为覆盖索引。 由于覆盖索引减少B+Tree是搜索次数,提高查询性能,所以使用覆盖索引是一个常用的索引手段。使用覆盖索引最常见的方法是创建联合索引,将需要查询的字段都放在联合索引上。
6、什么是联合索引?
联合索引:对多个字段同时建立的索引(有顺序,ABC,ACB是完全不同的两种联合索引。
优化:在联合索引中将选择性最高的列放在索引最前面。
例如:创建一个联合索引(a,b,c)能够使用联合索引的情况
①全匹配
select * from t where a=xxx and b=xxx and c=xxx
②最左前缀匹配
对于单个的a列,也可以用到(a,b,c)联合索引
select * from t where a=xxx and b=xxx
索引失效:
select * from t where b=xxx select * from t where c=xxx
如果使用:select * from t where b=xxx and a=xxx 索引不会失效**,数据库底层执行sql的时候会自动调换a和b的顺序。**
**最左前缀原则总结:**使用联合索引作为查询条件时必须要带上联合索引最左边的索引字段才会生效
假设有三个字段(col1, col2, col3),MySQL可以支持(col1), (col1, col2), 和(col1, col2, col3)的联合索引。必须要有col1,才会走索引
比较有争议的(col1, col3) 是否支持联合索引,官方给的文档中是支持的,我们试验也是支持。
where子句几个搜索条件顺序调换不影响查询结果 ,因为Mysql中有查询优化器,会自动优化查询顺序。
where子句,若遇到范围查询(> < between, like)或未在总结1中创建的索引对时,就会停止匹配(遇到的范围查询还是参与索引)。
7、什么是索引下推(ICP)?
是Mysql5.6之后的新特性
例如:联合索引为(name,age)
select * from user where name like "张%" and age=10;
没有索引下推的情况:查询出张猛,张三,张涛的主键,然后根据主键回表3次查询到完整的数据,然后取出age值看是否是10,如果不是则剔除,最终找到指定的数据。这样的问题就是回表次数会较多。
索引下推:当使用like "张%"查询出张猛、张三、张涛时,会使用二级索引age=10来过滤条件,然后再进行回表查询,这样可以减少回表次数.
8、索引失效场景,索引优化
1、联合索引一定要遵循最左前缀原则,否则会索引失效;
2、like语句中"xxx%"可以使用索引,但是如果是"%xx"就会失效;
3、当查询条件存在类型转换时,索引可能会失效。例如: tx_no在数据库是varchar类型,但是我们传入的参数却使用了int或者bigint其他类型;这个时候由于tx-no是varchar是字符型要和数值进行比较,所以mysql底层会使用函数将所有的tx-no转换为数值,所以就会全表扫描,不走索引。
4、在查询到条件索引列上进行函数运算会失效;
5、使用in索引不会 失效,但是使用not in 索引就会失效;
6、如果mysql觉得全表扫描更快时(数据少)不会走索引;
7、范围查询后面的索引全失效,比如联合索引a,b,c.。查询条件为where a=x and b>n and c=x ,那么此时只有a,b会走索引,后面的c不会,如果a>x那么都不会走索引(也可能会走索引,possible_keys字段为联合索引名字),会进行全表扫描。查询条件中有OR,即使有部分条件带索引也会失效。
8.使用explain分析sql语句是,如果possible key字段有值(为可能使用的索引名称),可以强制使用索引 select * from 表名 force index(索引名称) where a=xx 。使用explain分析看扫描的rows行数是否比不走索引少。如果扫描的行数少,就强制使用该索引。
9.小表驱动大表:要以小表作为主表
例如: A是小表,B是大表
使用left join 时,则应该这样写select * from A a left join B b on a.code=b.code
A表是驱动表,B表是被驱动表
测试:A表140多条数据,B表20万左右的数据量
select * from A a left join B b on a.code=b.code
执行时间:7.5s
select * from B b left join A a on a.code=b.code
执行时间:19s
结论:小表驱动大表优于大表驱动小表
10.连接查询代替子查询,子查询会执行两次sql,一次是外部查询,一次是嵌套子查询
11.批量插入不要一条一条插入,可以使用批量插入。一次性插入500条。如果批量插入的条数过多,会导致一次性发送到数据库的 SQL 语句非常大,增加了网络传输的负担,从而导致延迟。MySQL 在执行批量插入时通常会在一个事务内提交数据。过多的插入会导致事务变得过大,从而增加内存使用和数据库的负载,甚至可能导致锁的竞争,死锁。
12.使用limit查询大表时,limit翻页会越往后越慢。比如limit100000,10 从第100000条开始往后查询10条,会先从1找到第100000,效率比较慢。可以使用 where id >=100000 limit 10。这样会走索引。
9、哪些情况需要建立索引(原则)
为搜索、排序和分组的列建立索引
1、对查询频次较高,且数据量比较大的表建立索引;
2、频繁作为where查询条件的字段添加index普通索引;
3、经常要用于排序(order by),分组(group by)的列,因为索引已经排好序了;
4、频繁更新的字段不适合建立索引,因为每次更新不仅仅是更新了表记录而且还会更新索引;
10、select * from a,b和select * from a inner join b有什么区别?
单纯的select * from a,b是笛卡尔乘积。但是如果对两个表进行关联:select * from a,b where a.id = b.id 意思就变了,此时就等价于:select * from a inner join b on a.id = b.id。即就是内连接。
11、为什么不使用uuid作为主键索引?
因为uuid是无序的,MySQL写入数据时,会把数据存放到索引页中。使用UUID作为主键,新 行的主键值不一定比之前的主键值大,所以innoDb无法做到总是把新行插入到索引的最后,而 需要为新行寻找合适的位置来分配新的空间,这个过程会导致:
1、写入的目标页可能从缓存上移除了,或者还没有加载到缓存上,innodb写入之前需要先从 磁盘找到目标页,会产生大量的随机IO;
2、因为写入是乱序的,innoDb 要做频繁的分页操作,以便为行产生新的空间,页分裂导致 移动大量的数据,一次插入最少需要修改三个页以上;
3、频繁的页分裂,页会变得稀疏并被不规则的填充,最终会导致数据会有碎片;
4、随机值(uuid)载入到聚簇索引,有时候会需要做一次OPTIMEIZE TABLE来重建表并优 化页的填充,这将又需要一定的时间消耗。
使用自增主键则可以避免上述问题:
1、自增主键值是顺序的,所以Innodb把每一条记录都存储在一条记录的后面。当达到页面的 最大填充因子时候(innodb默认的最大填充因子是页大小的15/16,会留出1/16的空间留作 以后的修改),下一条记录就会写入新的页中;
2、数据按照顺序方式加载,主键页就会近乎于顺序的记录填满,提升了页面的最大填充率, 不会有页的浪费;
3、新插入的行一定会在原有的最大数据行下一行,mysql定位和寻址很快,不会为计算新行 的位置而做出额外的消耗;
4、减少了页分裂和碎片的产生
结论:使用innodb应该尽可能的按主键的自增顺序插入,并且尽可能使用单调的增加的聚簇 键的值来插入新行。
12、mysql索引如何做优化?
1、定位慢查询: 需要在数据库中开启慢查询配置,这样执行超过**慢查询配置时间(默认10s)**的语句为慢查询语句。
如何开启慢查询?
2、定位到慢查询语句,使用EXPLAIN 查询该语句索引是否生效。
EXPLAIN使用方式:EXPLAIN +SQL语句
[MySQL高级](一) EXPLAIN用法和结果分析_王洪玉的博客-CSDN博客_mysql explain
优化时,EXPLAIN起码要满足tpye=range级别
最好到最差依次是:system > const > eq_ref > ref > range > index > all
sql优化案例:
select t.* ,s.name as s_name ,s.sn as s_sn from base_schedule_log t left join base_schedule_config s on s.id = t.schedule_id WHERE ( t.type in ( '04' ) ) order by t.ctime desc LIMIT 0,10
base_schedule_log表里有700w条数据,base_schedule_config表里只要69条数据。这条sql执行时间需要将近4s。
分析后:第一张表走的是全表扫描
优化:给type字段和ctime字段添加索引,也可以不给ctime字段添加索引,因为这里的主键id是自增的,所以可以直接order by t.id desc和ctime是一样的。达到ref级别。
走了索引比不走索引快了1个数量级。,只需要0.3秒
13.mysql常用命令
进入数据库:mysql -u用户名 -p密码
使用数据库: use 数据库名
查看当前数据库增删改查的使用频次:
SHOW GLOBAL STATUS LIKE 'Com_______';
导入百万级sql脚本导数据库:
load data local infile '/www/sql/tb_sku1.sql' into table `tb_sku` fields terminated by ',' lines terminated by '\n';
将/www/sql/ 目录下的tb_sku.sql的数据导入到表tb_sku中
DDL语句(数据定义语言,用来定义数据库对象(数据库,表,字段)
数据库操作:
查询所有数据库:show databases 查询当前数据库:select database();
创建数据库:create database [ if not exists ] 数据库名 [ default charset 字符集 ] [ collate 排序规则 ] ;
删除数据库:drop database [ if exists ] 数据库名 ; 切换数据库: use 数据库名 ;
表操作:
查询当前数据库中所有的表:show tables; 查看指定表结构:desc 表名;
查看指定表的建表语句:show create table 表名 ;
创建表 :
CREATE TABLE 表名(
字段1 字段1类型 [ COMMENT 字段1注释 ],
字段2 字段2类型 [COMMENT 字段2注释 ],
字段3 字段3类型 [COMMENT 字段3注释 ],
......
字段n 字段n类型 [COMMENT 字段n注释 ]
) [ COMMENT 表注释 ] ;
添加表字段:ALTER TABLE 表名 ADD 字段名 类型 (长度) [ COMMENT 注释 ] [ 约束 ];
修改字段类型:ALTER TABLE 表名 MODIFY 字段名 新数据类型 (长度)
修改字段名称和字段类型:ALTER TABLE 表名 CHANGE 旧字段名 新字段名 类型 (长度) [ COMMENT 注释 ] [ 约束 ]
删除字段:ALTER TABLE 表名 DROP 字段名;
修改表名:ALTER TABLE 表名 RENAME TO 新表名
删除表: DROP TABLE [ IF EXISTS ] 表名
DML数据操作语句(增删改语句)
给指定字段添加数:INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...)
给全部字段添加数据:INSERT INTO 表名 VALUES (值1, 值2, ...);
批量添加数据:INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...) ;
修改数据:UPDATE 表名 SET 字段名1 = 值1 , 字段名2 = 值2 , .... [ WHERE 条件 ] ;
删除数据:DELETE FROM 表名 [ WHERE 条件 ]
DQL数据查询语言:
查询多个字段:SELECT 字段1, 字段2, 字段3 ... FROM 表名 ;
去除重复字段:SELECT DISTINCT 字段列表 FROM 表名;
DCL英文全称是Data Control Language(数据控制语言),用来管理数据库用户、控制数据库的访问权限。
用户管理:
查询用户:select * from mysql.user;
创建用户:CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
修改用户密码:CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
删除用户:DROP USER '用户名'@'主机名' ;
例如:
创建rk用户,密码为123456,可以任意主机访问:create user 'rk'@'%' identified by '123456';
创建luo用户,密码为123456,只允许本机访问:create user 'luo'@'localhost' identified by '123456';
删除luo用户:drop user 'luo'@'localhost'
权限控制:
查看权限:SHOW GRANTS FOR '用户名'@'主机名' ;
授予权限:GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';
撤销权限:REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';
mysql常用的权限种类:
ALL, ALL PRIVILEGES 所有权限
SELECT 查询数据
INSERT 插入数据
UPDATE 修改数据
DELETE 删除数据
ALTER 修改表
DROP 删除数据库/表/视图
CREATE 创建数据库/表
例如:
授予rk用户project数据库所有权限:grant all on project.* to 'rk'@'%';
授予luo用户所有库所有权限:grant all on *.* to 'luo'@'localhost';
查看事务隔离级别:SELECT @@TRANSACTION_ISOLATION
设置事务隔离级别:SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED |
READ COMMITTED | REPEATABLE READ | SERIALIZABLE }
14、SQL书写顺序和执行顺序
15、SQL优化
(1).插入数据
a、如果是批量插入数据 ,不要多次insert。使用insert into 表名 values(数据1),(数据2)......
b、主键顺序插入性能高于乱序插入(乱序插入可能会造成页分裂)
c、如果是大批量插入数据(比如:几百万行),使用insert插入性能较低,此时可以使用MySql数据库提供的load指令进行插入。100w条数据17s就可以完成。
InnoDB的逻辑结构
在InnoDB引擎中,数据行是记录在逻辑结构 page 页中的,而每一个页的大小是固定的,默认16K。 那也就意味着, 一个页中所存储的行也是有限的,如果插入的数据行row在该页存储不小,将会存储 到下一个页中,页与页之间会通过指针连接。
B+树的一个节点就是一页(既16K),InnoDB的指针占用6个字节的空 间,主键即使为bigint,占用字节数为8。
高度为2: n * 8 + (n + 1) * 6 = 16*1024 , 算出n约为 1170 1171* 16 = 18736 也就是说,如果树的高度为2,则可以存储 18000 多条记录。
高度为3: 1171 * 1171 * 16 = 21939856 也就是说,如果树的高度为3,则可以存储 2200w 左右的记录。
(2).update优化
update 时where后面的条件最好为索引,如果是索引就是行锁 ,如果是非索引列,会升级为表锁。InnoDB的行锁是针对索引加的锁,不是针对记录加的锁 ,并且该索引不能失效,否则会从行锁 升级为表锁 。
(3).索引设计原则
a.满足业务需求的情况下,尽量降低主键的长度。
b.插入数据时,尽量选择顺序插入,选择使用 AUTO_INCREMENT自增主键。
c.尽量不要使用UUID做主键或者是其他自然主键,如身份证号。
d.业务操作时,避免对主键的修改。
(4).order by优化
MySQL的排序,有两种方式:
Using filesort : 通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。
Using index : 通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要 额外排序,操作效率高。
a. 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则。
b. 尽量使用覆盖索引。
c. 多字段排序, 一个升序一个降序,此时需要注意联合索引在创建时的规则 ASC/DESC。
d. 如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小 sort_buffer_size(默认256k)。
(5).group优化
a. 在分组操作时,可以通过索引来提高效率。
b. 分组操作时,索引的使用也是满足最左前缀法则的。
(6).limit优化
在数据量比较大时,如果进行limit分页查询,在查询时,越往后,分页查询效率越低。如图:
因为,当在进行分页查询时,如果执行 limit 2000000,10 ,此时需要MySQL排序前2000010 记 录,仅仅返回 2000000 - 2000010 的记录,其他记录丢弃,查询排序的代价非常大 。
两次查询唯一不同的就是*变成了id,但是查询出来的数据不一样。因为第一条sql没走索引,走的全走扫描,第二条sql走了索引,所以结果不一样。所以,如果走了索引,必须通过order by对索引进行排序。
**优化思路:**一般分页查询时,通过创建 覆盖索引 能够比较好地提高性能,可以通过覆盖索引加子查 询形式进行优化。
explain select * from tb_sku t , (select id from tb_sku order by id limit 2000000,10) a where t.id = a.id;
(7).count优化
按照效率排序的话,count(字段) < count(主键 id) < count(1) ≈ count(*),所以尽 量使用 count(*)。
16、视图
视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视 图的查询中使用的表,并且是在使用视图时动态生成的。通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作 就落在创建这条SQL查询语句上。
17、存储过程
存储过程是事先经过编译并存储在数据库中的一段 SQL 语句的集合,调用存储过程可以简化应用开发 人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。 存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。
封装,复用 -----------------------> 可以把某一业务SQL封装在存储过程中,需要用到 的时候直接调用即可。
可以接收参数,也可以返回数据 --------> 再存储过程中,可以传递参数,也可以接收返回值。
减少网络交互,效率提升 -------------> 如果涉及到多条SQL,每执行一次都是一次网络传 输。 而如果封装在存储过程中,我们只需要网络交互一次可能就可以了。
游标:
游标(CURSOR)是用来存储查询结果集的数据类型 , 在存储过程和函数中可以使用游标对结果集进 行循环的处理。游标的使用包括游标的声明、OPEN、FETCH 和 CLOSE。
存储函数:
存储函数是有返回值的存储过程,存储函数的参数只能是IN类型的。使用存储函数能够实现的,都可以使用存储过程来实现,所以一般很少使用存储函数。
18、触发器
触发器是与表有关的数据库对象,指在insert/update/delete之前(BEFORE)或之后(AFTER),触 发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性 , 日志记录 , 数据校验等操作 。
使用别名OLD和NEW来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还 只支持行级触发,不支持语句级触发。
19、VARCHAR(50) 和 VARCHAR(500) 的区别
很多人可能会好奇,VARCHAR 不是按需分配的么?在满足业务的需求情况下,设置 VARCHAR(50) 和 VARCHAR(500) 有什么区别呢?最后的空间占用还不是以实际写入的字符串为主,从这个角度来看,确实没错。但考虑以下两点:
1. 索引有最大长度的限制
对于行格式为 REDUNDANT 或 COMPACT 的InnoDB表,索引的最大长度被限制为767字节。所以,在MySQL 5.6 中,在创建索引时,我们通常会碰到"Index column size too large. The maximum column size is 767 bytes"错误:
mysql> create table t(c1 varchar(200)) charset=utf8mb4; Query OK, 0 rows affected (0.02 sec) mysql> alter table t add index(c1); ERROR 1709 (HY000): Index column size too large. The maximum column size is 767 bytes.
要解决这个问题,可选的方案有:
-
减少字段的长度,确保字段的长度 * 字符集的最大字节数不超过767。
-
设置前缀索引,确保前缀索引的长度 * 字符集的最大字节数不超过767。