MySQL自带工具类 profiles
,但是只在老一点的版本里用。
set profiling=1; #设置为1以后可以查看sql执行的详细情况
show profiles; # 显示sql id,执行时间,sql 语句
show profile; # 可以查看sql语句在执行的每个过程中消耗的时间
show profile for query 2; # 多条语句的时候,可以通过 sql 的 ID 选择对应的语句,比如 2
是一个数据库,里面有多张表
默认开启,在配置文件中修改
事件的记录不会持久化,保存在内存中,server 断开以后自动删除
show databases;
use performance_schema;
show tables;
-- 打开等待事件的采集器配置项开关,enabled 监控是否开启,timed 计时是否开启
update setup_instruments set enabled = 'yes',timed = 'yes' where name like 'wait%';
-- 打开等待事件的保存表配置开关
update setup_consumers set enabled = 'yes' where name like '%wait%';
配置完成后可以查看当前 server 在干什么,通过下表得知
select from events_waits_current\G -- \G 表示将表格转换为键值对形式
查看当前链接
show processlist;
-- 将 IP 地址转为整数存储
select INET_ATON('182.168.85.111'); -- 3232257391
-- 将整数转回 IP 地址
select INET_NTOA('3232257391'); -- 192.168.85.111
null
,可为 null 的列使得索引,索引统计和值比较都更加复杂。SQL 中 null ≠ null
TINYINT 、SMALLINT、MEDIUMINT、INT、BIGINT 分别使用8、16、24、32、64位存储空间,尽量使用满足需求的最小数据类型
MySQL 把每个 BLOB 和 TEXT 值当作一个独立的对象处理,两者都是为了存储很大的数据而设计的字符串类型,分别采取二进制和字符的方式存储。
datetime
timestamp
date
有时候可以使用枚举类型代替常用字符串类型,MySQL存储枚举类型会非常紧凑,会根据列表值的数据压缩到一个或者两个字节中,MySQL在内部会将每个值在列表中的位置保存为整数,并且在表的 .frm
文件中保存 “数字——字符串” 映射关系的查找表
create table enum_test(
e enum('fish','apple','dog') not null
);
insert into entm_test(e) values ('fish'),('dog'),('apple');
select * from enum_test; -- fish apple dog
select e+0 from enum_test; -- 1 3 2
人们经常使用varchar(15)类存储IP地址,然而,他的本质是32位无符号整数,不是字符串,可以使用 INET_ATON
和 INET_NTOA
函数在这两种表示之间进行转换
select inet_aton('1.1.1.1');
select inet_ntoa(16843009);
优点:
缺点:
优点:
缺点:
MySQL 的utf-8 只能存储两个字符的中文,三个字符中文容易乱码
utf-8 mb4
当我们的表中存在类似于 TEXT 或者是很大的 VARCHAR 类型的大字段的时候,如果我们大部分访问这张表的时候都不需要这个字段,我们就该义无反顾的将其拆分到另外的独立表中,以减少常用数据所占用的存储空间,这样做的一个明显好处就是每个数据块中可以存储的数据条数大大增加,即减少物理 IO 次数,也能大大提高内存的缓存命中率
ID
select_type
分辨查询类型。是普通查询、联合查询还是子查询
--sample:简单的查询,不包含子查询和union
explain select * from emp;
--primary:查询中若包含任何复杂的子查询,最外层查询则被标记为Primary
explain select staname,ename supname from (select ename staname,mgr from emp) t join emp on t.mgr=emp.empno ;
--union:若第二个select出现在union之后,则被标记为union
explain select * from emp where deptno = 10 union select * from emp where sal >2000;
--dependent union:跟union类似,此处的depentent表示union或union all联合而成的结果会受外部表影响
explain select * from emp e where e.empno in ( select empno from emp where deptno = 10 union select empno from emp where sal >2000)
--union result:从union表获取结果的select
explain select * from emp where deptno = 10 union select * from emp where sal >2000;
--subquery:在select或者where列表中包含子查询
explain select * from emp where sal > (select avg(sal) from emp) ;
--dependent subquery:subquery的子查询要受到外部表查询的影响
explain select * from emp e where e.deptno in (select distinct deptno from dept);
--DERIVED: from子句中出现的子查询,也叫做派生类,
explain select staname,ename supname from (select ename staname,mgr from emp) t join emp on t.mgr=emp.empno ;
--UNCACHEABLE SUBQUERY:表示使用子查询的结果不能被缓存
explain select * from emp where empno = (select empno from emp where deptno=@@sort_buffer_size);
--uncacheable union:表示union的查询结果不能被缓存:sql语句未验证
table
对应行正在访问哪一个表,表名或者别名,可能是临时表或者union合并结果集
type
type显示的是访问类型,访问类型表示我是以何种方式去访问我们的数据,最容易想的是全表扫描,直接暴力的遍历一张表去寻找需要的数据,效率非常低下,访问的类型有很多,效率从最好到最坏依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
一般情况下,得保证查询至少达到range级别,最好能达到ref
--all:全表扫描,一般情况下出现这样的sql语句而且数据量比较大的话那么就需要进行优化。
explain select * from emp;
--index:全索引扫描这个比all的效率要好,主要有两种情况,一种是当前的查询时覆盖索引,即我们需要的数据在索引中就可以索取,或者是使用了索引进行排序,这样就避免数据的重排序
explain select empno from emp;
--range:表示利用索引查询的时候限制了范围,在指定范围内进行查询,这样避免了index的全索引扫描,适用的操作符: =, <>, >, >=, <, <=, IS NULL, BETWEEN, LIKE, or IN()
explain select * from emp where empno between 7000 and 7500;
--index_subquery:利用索引来关联子查询,不再扫描全表
explain select * from emp where emp.job in (select job from t_job);
--unique_subquery:该连接类型类似与index_subquery,使用的是唯一索引
explain select * from emp e where e.deptno in (select distinct deptno from dept);
--index_merge:在查询过程中需要多个索引组合使用,没有模拟出来
--ref_or_null:对于某个字段即需要关联条件,也需要null值的情况下,查询优化器会选择这种访问方式
explain select * from emp e where e.mgr is null or e.mgr=7369;
--ref:使用了非唯一性索引进行数据的查找
create index idx_3 on emp(deptno);
explain select * from emp e,dept d where e.deptno =d.deptno;
--eq_ref :使用唯一性索引进行数据查找
explain select * from emp,emp2 where emp.empno = emp2.empno;
--const:这个表至多有一个匹配行,
explain select * from emp where empno = 7369;
--system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现
possible_keys
显示可能应用在这张表中的索引,一个或多个,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用
explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;
key
实际使用的索引,如果为null,则没有使用索引,查询中若使用了覆盖索引,则该索引和查询的select字段重叠。
explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;
key_len
表示索引中使用的字节数,可以通过key_len计算查询中使用的索引长度,在不损失精度的情况下长度越短越好。
explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;
ref
显示索引的哪一列被使用了,如果可能的话,是一个常数
explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;
rows
根据表的统计信息及索引使用情况,大致估算出找出所需记录需要读取的行数,此参数很重要,直接反应的sql找了多少数据,在完成目的的情况下越少越好
explain select * from emp;
extra
包含额外的信息。
--using filesort:说明mysql无法利用索引进行排序,只能利用排序算法进行排序,会消耗额外的位置
explain select * from emp order by sal;
--using temporary:建立临时表来保存中间结果,查询完成之后把临时表删除
explain select ename,count(*) from emp where deptno = 10 group by ename;
--using index:这个表示当前的查询时覆盖索引的,直接从索引中读取数据,而不用访问数据表。如果同时出现using where 表名索引被用来执行索引键值的查找,如果没有,表面索引被用来读取数据,而不是真的查找
explain select deptno,count(*) from emp group by deptno limit 10;
--using where:使用where进行条件过滤
explain select * from t_user where id = 1;
--using join buffer:使用连接缓存,情况没有模拟出来
--impossible where:where语句的结果总是false
explain select * from emp where empno = 7469;
索引底层是 B+树
order by 是全表排序,效率比较慢,用索引以后就自动排序了
1)主键索引
2)唯一索引
3)普通索引
4)全文索引
5)组合索引
数据库会自动创建索引,索引是给唯一键创建的,不是给主键
给一个普通列创建索引
在 varchar char text 等类型中创建全文索引
将两个列组合在一起创作索引
例如给 name 列创建索引,最后的节点里面放的是主键,按照 name 进行查询的时候,第一次根据 name 列的 B+ 树进行查找,在 name 列叶子节点里找到主键,在到主键的 B+树里找到整行的记录,这个过程就叫回表
或者按sql语句来理解,就是 select * from 表名 where id in (111,222,333);
例如 select id from 表名 where name = '....'
,这个语句在执行的时候,定位到name的叶子节点,而这个节点里存着 主键 ID,最终要查的也是 ID,就不需要在通过 主键ID在主键的叶子节点里查找数据,这就是索引覆盖。也就是省去了回表的过程。
假如用 name 和 age 两个列创建索引,name在前age在后,select * from test where name=? and age=?
这样可以走索引,但 where age=? and name=?
这样就不能走索引。先有最左边(name),才能有右边(age)。
假如只查 name ,不查 age,同样也可以走索引,也就是只要最左边有了,右边有没有都行。
只有组合索引才有最左匹配的概念
假如有一个需求,需要返回 name 和 age,单独 name,单独 age,创建索引的时候就可以创建成 name age
和 age
两个索引。因为索引存储也需要占用空间,显然 age 占用的空间更小。
谓词下推:
以select t1.name,t2.name from t1 join t2 on t1.id = t2.id
为例,有两种筛选方式
第一种:将 t1 和 t2 先组合,然后筛选
第二种:先将 name 和 id 取出来,然后筛选。
第二种方式就是谓词下推
索引下推:前提是在组合索引中
有一个 name,age 组合索引,查找的时候筛选条件 where name = ? and age = ?
老版本的时候,先把所有 name 符合的值从存储引擎中提取出来,然后在 server 层再筛选 age 。
高版本直接在存储引擎中将 name 和 age 符合的筛选出来,不在 server 层进行处理,这就是索引下推
哈希表:Memory
B+ 树:InnoDB Myisam
哈希索引的限制:
例:
当需要存储大量的URL,并且根据URL进行搜索查找,如果使用B+树,存储的内容就会很大 select id from url where url=''
也可以利用将 url 使用 CRC32 做哈希 select id from url where url = '' and url crc=CRC32('')
此查询性能较高的原因是使用体积很小的索引来完成查找。
CRC32 :循环冗余校验,可以把指定字符串变成整数值
当包含多个列做为索引,需要注意的是正确的顺序依赖于该索引的查询,同时需要考虑如何更好的满足排序和分组的需求
不是单独的索引类型,而是一种数据存储方式,指的是数据行跟相邻的键值紧凑的存储在一起
explain select .......
查完以后,Extra 是 Using inde 即为覆盖索引
查询的时候尽量带上主键查询
创建前缀索引: alter table 表名 add key(列名(数量));
有时候需要索引很长的字符串,这会让索引变的大且慢,通常情况下可以使用某个列开始的部分字符串,这样大大的节约索引空间,从而提高索引效率,但这会降低索引的选择性,索引的选择性是指不重复的索引值和数据表记录总数的比值,范围从1/#T到1之间。索引的选择性越高则查询效率越高,因为选择性更高的索引可以让mysql在查找的时候过滤掉更多的行。
一般情况下某个列前缀的选择性也是足够高的,足以满足查询的性能,但是对应BLOB,TEXT,VARCHAR类型的列,必须要使用前缀索引,因为mysql不允许索引这些列的完整长度,使用该方法的诀窍在于要选择足够长的前缀以保证较高的选择性,通过又不能太长。
create table user(id int,name varchar(10),phone varchar(11));
alter table user add index idx_1(phone);
cont(distince(列名))/congt(*)
来计算left join左联接()/返回包括左表中的所有记录和右表中联结字段相等的记录
right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
inner join(等值连接) 只返回两个表中联结字段相等的行
select from t1 left join t2 on t1.id = t2.id and t1.name = '...';
and 是在表连接前过滤A表或B表里面哪些记录符合条件,同时会兼顾是 left join 还是 right join 。即假如是左连接的话,如果左边表的某条记录不符合连接条件,那么它不进行连接,但是仍然留在结果集中(此时右边部分的连接结果为NULL)。on 条件是在生成临时表时使用的条件,它不管 on 中的条件是否为真,都会返回左边表中的记录
如果明确知道只有一条结果返回,limit 1 能够提高效率
limit 1 指针不会多做一次判断
.在返回结果的时候,即使只有1个返回结果,还会往下进行判断,看还有没有值
监控所有索引
show status like 'Handler_rtead%'
Handler_read_first
:读取索引第一个条目的次数Handler_read_key
:通过 index 获取数据的次数Handler_read_last
:读取索引最后一个条目的次数Handler_read_next
:通过索引读取下一条数据的次数Handler_read_prev
:通过索引读取上一条数据的次数Handler_read_rnd
:从固定位置读取数据的次数Handler_read_rnd_next
: 从数据节点读取下一条数据的次数在 5.x 几的版本中,有查询缓存,如果查询缓存是打开的,那么 MySQL 会优先检查这个查询时候命中查询缓存中的数据,如果查询恰好命中缓存,那么会在返回结果之前检查用户权限,如果没问题,就跳过所有阶段,直接从缓存中拿到结果并返回给客户端
MySQL查询完缓存后会经过以下几个步骤:
MySQL通过关键字将SQL语句进行解析,并生成一颗解析树,MySQL解析器将使用MySQL语法规则验证和解析查询,例如验证使用了错误的关键字或者顺序是否正确等等。预处理器会进一步检查解析树是否合法,例如表名和列名是否存在,是否有歧义,还会验证权限等等
select count(*) from film_actor;
show status like 'last_query_cost' -- 最后一次查询所消耗的成本
可以看到这条查询语句大概需要做 1104 个数据页才能找到对应的数据,这是经过一系列的统计信息计算来的
很多情况下 MySQL 会执行错误的执行计划,原因如下:
统计信息不准确
InnoDB 因为其 mvcc 的架构,并不能维护一个数据表的行数的精确统计信息
执行计划的成本估算不等同于实际执行的成本
有时候某个执行计划虽然需要读取更多的界面,但是他的成本却更小,因为如果这些页面都是顺序读或者这些页面都已经在内存中的话,那么它的访问成本将很小,MySQL层面并不知道哪些页面在内存中,哪些在磁盘,所以查询之际执行过程中到底需要多少次IO是无法得知的
MySQL 的最优可能跟你想的不一样
MySQL的优化是基于成本的优化,但是有可能不是最快的优化
MySQL 不考虑其他并发执行的查询
MySQL 不会考虑不受其控制的操作成本
执行存储过程或者用户自定义函数的成本
优化器的优化策略
优化器的优化类型
重新定义关联表的顺序
数据表的关联并不总是按照在查询中指定的顺序进行,决定关联顺序是优化器很重要的功能
将外连接转化为内连接,内连接的效率要高于外连接
使用等价交换规则,MySQL 可以使用一些等价变化来简化并规划表达式
优化 count() , min() , max()
索引和列是否可以为空通常可以帮助 MySQL 优化这类表达式。例如:要找到某一列的最小值,只需要查询索引的最左端的记录即可,不需要全文扫描比较
预估并转化为常数表达式,当 MySQL 检测到一个表达式可以转化为常数的时候,就会一直把该表达式做为常数进行处理
where id = 1
索引覆盖扫描,当索引中的列包含所有查询中需要使用的列的时候,可以使用覆盖索引
子查询优化
mysql 在某些情况下可以将子查询转换一种效率更高的形式,从而减少多个查询多次对数据进行访问。例如将经常查询的数据放入到缓存中。
等值传播
a inner join b on a.id = b.id where a.id > 500 and b.id > 500
==> a inner join b on a.id = b.id where a.id > 500
关联查询
排序优化
两次传输排序
第一次数据读取是将需要排序的字段读取出来,然后进行排序。第二次是将排好序的结果按照需要去读取数据行。这种方式效率比较低,原因是第二次读取数据的时候因为已经排好序,需要去读取所有记录而此时更多的是随机IO,读取数据成本会比较高
两次传输的优势,在排序的时候存储尽可能少的数据,让排序的时候存储尽可能少的数据,让排序缓冲区可以尽可能多的容纳行数来进行排序操作
单次传输排序
先读取查询所需要的所有列,然后再根据给定列进行排序,最后直接返回排序结果,此方式只需要一次顺序IO读取所有的数据,而无需任何的随机IO,问题在于查询的列特别多的时候,会占用大量的存储空间,无法存储大量的数据
当需要排序的列的总大小加上 order by 的列大小超过 max_length_for_sort_data 定义的字节,MySQL 会选择双次排序,反之使用单次排序,当然,用户可以设置此参数的值来选择排序方式
select count(*) / count(id) / count(1)
执行效率完全一样
count() 是特殊类型的函数,有两种不同的作用,一种是某个列值的数量,也可以统计行数
总有人认为 myisam 的 count 函数比较快,这是有前提条件的,只有没有任何 where 条件的 count(*) 才是比较快的
使用近似值
在某些场景中,不需要完全精确的值,可以参考使用近似值来代替,比如可以使用 explain 来获取近似的值。
其实在很多OLAP 的应用中,需要计算某一个列值的基数,有一个计算近似值的方法叫 hyperloglog
更复杂的优化
一般情况下,count() 需要扫描大量的行才能获得精确的数据,其实很难优化,在实际操作的时候可以考虑使用索引覆盖扫描,或者增加汇总表,或者增加外部缓存系统
确保 on 或者 using 子句中的列上有索引,在创建索引的时候就要考虑到关联的顺序
确保任何的 group by 和 order by 中的表达式只涉及到一个表中的列,这样 MySQL 才有可能使用索引来优化这个过程
尽可能使用关联查询代替,子查询会有临时表,影响效率
优化此类查询的最简单办法就是尽可能的使用覆盖索引,而不是查询所有列
select * from 表名 limit 10000,5
==> select * from a join (select id from 表名 limit 10000,5) b on a,id = b.id;
-- 查看执行计划的扫描行数进行验证
select id,name from 表名 order by title limit 50,5 -- rows 大概 1000
explain select a.id,a.name from a inner join (select id from a order by title limit 50,5) as lim using(id); -- rows 大概 111
在很多场景下,MySQL 使用相同的方式来优化 group by 和 distinct 的查询,使用索引是最有效的方式,当时有很多的情况下无法使用索引,可以使用临时表或者文件排序来分组
union ,并集,不包含重复部分
union all ,并集,包含重复部分,也就是两个结果集交叉的部分,即重复算两次交叉部分
行转列可以用到 union
除非确实需要服务器消除重复的行,否则一定要使用 union all ,因此没有 all 关键字,MySQL 会在查询的时候给临时表加上 distinct 的关键字,这个操作代价很高
set @one := 1
set @min_actor :=(select min(id) from actor)
set @last_week := current_date - interval 1 week
确定取值顺序结果:
第一个,先执行where后的语句,所以两条
第二个:
第三个,先执行where后括号里的语句,所以一 条
分区表由多个相关的底层表实现,这个底层表也是由句柄对象标识,我们可以直接访问各个分区。存储引擎管理分区的各个底层表和管理普通表一样(所有的底层表都必须使用相同的存储引擎),分区表的索引知识在各个底层表上各自加上一个完全相同的索引。从存储引擎的角度来看,底层表和普通表没有任何不同,存储引擎也无须知道这是一个普通表还是一个分区表的一部分。
分区表的操作按照以下的操作逻辑进行:
select查询
当查询一个分区表的时候,分区层先打开并锁住所有的底层表,优化器先判断是否可以过滤部分分区,然后再调用对应的存储引擎接口访问各个分区的数据
insert操作
当写入一条记录的时候,分区层先打开并锁住所有的底层表,然后确定哪个分区接受这条记录,再将记录写入对应底层表
delete操作
当删除一条记录时,分区层先打开并锁住所有的底层表,然后确定数据对应的分区,最后对相应底层表进行删除操作
update操作
当更新一条记录时,分区层先打开并锁住所有的底层表,mysql先确定需要更新的记录再哪个分区,然后取出数据并更新,再判断更新后的数据应该再哪个分区,最后对底层表进行写入操作,并对源数据所在的底层表进行删除操作
有些操作时支持过滤的,例如,当删除一条记录时,MySQL需要先找到这条记录,如果where条件恰好和分区表达式匹配,就可以将所有不包含这条记录的分区都过滤掉,这对update同样有效。如果是insert操作,则本身就是只命中一个分区,其他分区都会被过滤掉。mysql先确定这条记录属于哪个分区,再将记录写入对应得曾分区表,无须对任何其他分区进行操作
虽然每个操作都会“先打开并锁住所有的底层表”,但这并不是说分区表在处理过程中是锁住全表的,如果存储引擎能够自己实现行级锁,例如innodb,则会在分区层释放对应表锁。
官网地址:https://dev.mysql.com/doc/refman/8.0/en/partitioning-types.html
根据列值在给定范围内将行分配给分区
在此分区方案中,对应于在商店 1 到 5 工作的员工的所有行都存储在分区中,对应于在商店 6 到 10 工作的员工的所有行都存储在分区中,依此类推。每个分区按从低到高的顺序定义。
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT NOT NULL,
store_id INT NOT NULL
)
PARTITION BY RANGE (store_id) (
PARTITION p0 VALUES LESS THAN (6),
PARTITION p1 VALUES LESS THAN (11),
PARTITION p2 VALUES LESS THAN (16),
PARTITION p3 VALUES LESS THAN (21)
);
也可以使用 UNIX_TIMESTAMP() 函数根据 TIMESTAMP 列的值按 对表进行分区,如以下示例所示:RANGE
CREATE TABLE quarterly_report_status (
report_id INT NOT NULL,
report_status VARCHAR(20) NOT NULL,
report_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
PARTITION BY RANGE ( UNIX_TIMESTAMP(report_updated) ) (
PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-01-01 00:00:00') ),
PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-04-01 00:00:00') ),
PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-07-01 00:00:00') ),
PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-10-01 00:00:00') ),
PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-01-01 00:00:00') ),
PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-04-01 00:00:00') ),
PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-07-01 00:00:00') ),
PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-10-01 00:00:00') ),
PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2010-01-01 00:00:00') ),
PARTITION p9 VALUES LESS THAN (MAXVALUE)
);
类似于按 range 分区,区别在于 list 分区时基于列值匹配一个离散值集合中的某个值来进行选择(等值分区)
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT,
store_id INT
)
PARTITION BY LIST(store_id) (
PARTITION pNorth VALUES IN (3,5,6,9,17),
PARTITION pEast VALUES IN (1,2,10,11,19,20),
PARTITION pWest VALUES IN (4,12,13,14,18),
PARTITION pCentral VALUES IN (7,8,15,16)
);
MySQL 从 5.5 开始支持 column 分区,可以认为 i 是 range 和 list 的升级版,在 5.5 之后,可以使用 column 分区代替 range 和 list,但是 column 分区只接受普通列不接受表达式
基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算,这个函数可以包含MySQL中有效的、产生非负整数值的任何表达式
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT,
store_id INT
)
PARTITION BY HASH(store_id)
PARTITIONS 4;
类似于hash 分区,区别在于 key 分区只支持一列或多列,且 MySQL 服务器提供器自身的哈希函数,必须有一列或多列包含整数值
CREATE TABLE k1 (
id INT NOT NULL,
name VARCHAR(20),
UNIQUE KEY (id)
)
PARTITION BY KEY()
PARTITIONS 2;
在分区的基础上,再进行分区后存储
CREATE TABLE ts (id INT, purchased DATE)
PARTITION BY RANGE( YEAR(purchased) )
SUBPARTITION BY HASH( TO_DAYS(purchased) ) (
PARTITION p0 VALUES LESS THAN (1990) (
SUBPARTITION s0,
SUBPARTITION s1
),
PARTITION p1 VALUES LESS THAN (2000) (
SUBPARTITION s2,
SUBPARTITION s3
),
PARTITION p2 VALUES LESS THAN MAXVALUE (
SUBPARTITION s4,
SUBPARTITION s5
)
);
如果需要从非常大的表中查询某一段时间的记录,而这张表中包含很多历史数据,数据按照时间排序,因数据量巨大,肯定不能每次都进行全表扫描,考虑到索引在空间和维护上的消耗,即使使用索引还会产生大量的随机IO,但当数据量超大的时候,索引也就无法起作用了,此时可以考虑使用分区来进行解决
使用简单的分区方式村表,不要任何索引,根据分区规则大致定位需要的数据为止,通过使用where条件将需要的数据限制在少数分区中,这种策略适用于以正常的方式访问大量数据
如果数据有明显的热点,而且除了这部分数据,其他数据很少被访问到,那么可以将这部分热点数据单独放在一个分区中,让这个分区的数据能够有机会都缓存在内存中,这样查询就可以只访问一个很小的分区表,能够使用索引,也能够有效的使用缓存
max_connections
MySQL 的最大连接数,如果数据库的并发连接请求比较大,应该调高该值
默认连接数是根据电脑配置定的
设置默认连接数:set global max_connections=1024
查看默认连接数:show variables like '%max_connection%'
max_user_connections
限制每个用户的连接个数,默认是0,表示不限制
如果一个用户连接成百上千次,肯定有异常,可以通过它来限制
back_log
MySQL 能够暂存的连接数量,当 MySQL 的线程在一个很短时间内得到非常多的连接请求时,就会起作用,如果MySQL 的连接数量达到 max_connections 时,新的请求会被存储在堆栈中,以等待某一个连接释放资源,如果等待连接的数量超过 back_log ,则不再接受连接资源
默认80,不宜过大 ,在业务中过大造成等待时间过长,还不如返回拒绝连接或者连接过多
wait_timeout
MySQL 在关闭一个非交互的连接之前需要等待的时长
使用 JDBC 请求数据就是非交互式连接
interactive_timeout
关闭一个交互的连接之前需要等待的时长
MySQL 的黑窗口就是交互连接
log_bin : 多用于主从复制,默认不开启。 使用 log-bin=master-bin 开启
索引缓存区的大小(只对 myisam 表起作用)
8 版本被淘汰
query_cache_size
查询缓存的大小,未来版本被删除
query_cache_limit
超出此大小的查询将不被缓存
query_cache_mine_res_unit
缓存块最小大小
query_cache_type
缓存类型,决定缓存什么样的查询
0 表示禁用
1 表示将缓存所有结果,除非 sql 语句中使用 sql_no_cache 禁用查询缓存
2 表示只缓存 select 语句中通过 sql_cache 指定需要缓存的查询
sort_buffer_size
每个需要排序的线程分派该大小的缓冲区
max_allowed_packet=32M
限制 server 接受的数据包大小
join_buffer_size=2M
表示关联缓存的大小
thread_cache_size