MySQL性能优化

297 阅读18分钟

image.png

1. 配置优化

服务端连接数不够导致应用程序获取不到连接错误:Mysql: error1040: Too many connections 的错误。 两个方面解决问题:①从服务的角度,②客户端角度。

1.1 服务端

配置优化

1.1.1 服务端增加可用连接数

修改max_connections的大小

show variables like 'max_connections';

image.png

1.1.2 释放不活动的连接

交互式和非交互式的客户端的默认超时时间都是28800秒。8小时,我们可以把这个值调小。

--及时释放不活动的连接,注意不要释放连接池还在使用的连接
show global variables like 'wait_timeout'; 

image.png

1.2 客户端

1.2.1 使用专用连接池工具

(Druid、Hikari、DBCP、C3P0)维护一定数量大小的连接池,其他客户端排队等待获取连接。Druid默认最大连接池大小是8.Hikari的默认最大连接池大小是10.

1.2.2 连接池默认大小设置技巧

机器核数乘以2加1.(4核的机器,连接池维护9个连接就够)。

1.2.3 CPU执行超过核数大小的任务原理

时间片,上下文切换,频繁上下文切换会造成性能开销。

1.2.4 有默认参数没有标准参数

不管是数据库配置还是安装数据库服务的操作系统的配置,对于配置进行优化,最终的目的都是为了更好地发挥硬件本身的性能,包括CPU、内存、磁盘、网络。在不同的操作系统和MySQL的参数配置是不同的,没有标准的配置

1.2.5 默认配置满足大部分需求修改且看DBA

MySQL和InnoDB的配置参数各种开关和数值配置,比如:buffer_pool_size、默认页大小、并发线程数等,都提供了默认值。可以满足大部分情况的需求,除非有特殊需求,在清楚参数含义的情况下再去修改它。修改配置的工作一般由专业的DBA完成。

1.2.6 主从复制的优化

2. 架构优化

2.1 缓存

用第三方缓存服务,例如Redis。

image.png

2.2 主从复制

如果单台数据库服务满足不了访问需求,那我们可以做数据库的集群方案。

2.2.1 级联复制

集群的话必然会面临一个问题,就是不同的节点之间数据一致性的问题。如果同时读写多台数据库节点,怎么让所有的节点数据保持一致?这个时候我们需要用到复制技术(replication),被复制的节点称为 master,复制的节点称为 slave。slave 本身也可以作为其他节点的数据来源,这个叫做级联复制

image.png

2.2.2 主从复制实现原理

主从复制是怎么实现的呢?更新语句会记录binlog,它是一种该逻辑日志。 从服务器获取主服务器的binlon文件,然后解析里面的SQL语句,在从服务器上面执行一遍,保持主从数据一致。 涉及三个线程:

  1. 连接到master获取binlog,解析binlog写入中继日志,这个线程叫做I/O线程。
  2. Master节点上有一个log dump线程,是用来发送binlog给slave的。
  3. 从库SQL线程,读取relay log,把数据写入数据库。

image.png

2.2.2.1 读写分离

做了主从复制的方案之后,我们只把数据库写请求路由到master节点,读的请求路由到slave节点。这种方案就做读写分离。(路由实现,动态数据源选择(Spring抽象类、客户端工具,代理层)、服务端特殊版本数据库,自动选择路由。

image.png

读写分离可以一定程度低减轻数据库服务器的访问压力,但是需要特别注意主从数据一致性的问题。如果我们在 master 写入了,马上到 slave 查询,而这个时候 slave 的数据还没有同步过来,怎么办?所以,基于主从复制的原理,我们需要弄明白,主从复制到底慢在哪里?

2.2.3 主从复制复制技术

2.2.3.1 单线程

master可以支持SQL语句并行执行,配置最大连接数就是最多同时多少个SQL并行执行。 slave的sql智能单线程排队执行,在主库并发量很大的情况下,同步数据肯定会出现延迟。 原因: 主库执行增删改语句,从库的执行顺序不能颠倒。

insert into user_comments (10000009,'nice');
update user_comments set content ='very good' where id =10000009;
delete from user_comments where id =10000009;

2.2.3.2 异步复制

在主从复制中,MySQL默认是异步复制。对于主节点来说,写入binlog,事务结束,返回客户端。对于slave而言,接收binlog就完成,master不关心slave的数据库有没有写入成功。

优点:事务执行时间短,master性能高。
缺点:读之前数据出现未同步,或同步失败的情况,而客户端得到的是成功的响应。

image.png

2.2.3.3 全同步复制

从库写完数据,主库才返回给客户端,叫做全同步复制

优点:保证读之前数据已同步。
缺点:事务执行时间变长,到导致master节点性能下降。

2.2.3.4 半同步复制

主库执行完客户端提交的事务后,等待至少一个从库接收到binlog并写到relay log中才返回给客户端。master不会等待很长的时间,但是返回给客户端的时候,数据就即将写入成功了,因为它只剩最后一步:就是读取relay log,写入从库。

image.png 相对于异步复制,半同步复制提高了数据的安全性,同时也造成了一定程度的延迟,它需要等待一个 slave 写入中继日志,这里多了一个网络交互的过程,所以,半同步复制最好在低延时的网络中使用。这个是从主库和从库连接的角度,来保证 slave 数据的写入。

优点:提高了数据的安全性。
缺点:造成一定程度的数据同步延迟。

半同步复制,必须安装一个插件,这是谷歌一位工程师的贡献。这个插件在mysql的插件目录下默认提供:cd /usr/lib64/mysql/plugin/.

-- 主库执行
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
set global rpl_semi_sync_master_enabled=1;
show variables like '%semi_sync%';
-- 从库执行
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
set global rpl_semi_sync_slave_enabled=1;
show global variables like '%semi%';

2.2.3.5 多库并行复制

image.png

2.2.3.6 异步复制之GTID复制

把在主库上并行执行的事务,分为一个组,并且给他们编号,这一个组的事务在从库上面也可以并行执行。这个编号叫做GTID(Global Transaction Identifiers),这种主从复制的方式叫做GTID复制

image.png

如果我们要使用GTID复制,我们可以通过修改配置参数打开它,默认是关闭的:

show global variables like 'gtid_mode';

image.png

无论是优化mater和slave的连接方式,还是让从库可以并行执行SQL,都是从数据库的层面解决主从复制延迟的问题。除了数据库本身的层面之外,在应用层面,我们也有一些减少主从同步延迟的方法。

问题:做了主从复制之后,如果单个master节点或者单张表存储的数据过大的时候,比如一张表有上亿的数据,单表的查询性能还是会下降,我们要进一步对单台数据库节点的数据分型拆分,这个就是分库分表。

2.3 分库分表

2.3.1 垂直分库

减少并发压力。水平分表,解决存储瓶颈。垂直分库的做法,把一个数据库按照业务拆分成不同的数据库。

image.png

2.3.2 水平分库分表

水平分库分表的做法,把单张表的数据按照一定的规则分布到多个数据库。

image.png

image.png

2.4 高可用方案

2.4.1 主从复制

传统的HAProxy + keepalived方案,基于主从复制。

2.4.2 NDB Cluster

基于NDB集群存储引擎的MySQL Cluster

dev.mysql.com/doc/mysql-c…

2.4.3 Galera

galeracluster.com/

多主同步复制的集群方案

image.png

2.4.4 MHA/MMM

tech.meituan.com/2017/06/29/…

MMM(Master-Master replication manager for MySQL),一种多主的高可用架构,是一个日本人开发的,像美团这样的公司早期也有大量使用 MMM。MHA(MySQL Master High Available)。MMM 和 MHA 都是对外提供一个虚拟 IP,并且监控主节点和从节点,当主节点发生故障的时候,需要把一个从节点提升为主节点,并且把从节点里面比主节点缺少的数据补上,把 VIP 指向新的主节点。 image.png 优化器优化

2.4.5 MGR

dev.mysql.com/doc/refman/… dev.mysql.com/doc/refman/…

MySQL 5.7.17 版本推出的 InnoDB Cluster,也叫 MySQL Group Replicatioin(MGR),这个套件里面包括了 mysql shell 和 mysql-route。

image.png

2.4.6 总结

高可用 HA 方案需要解决的问题都是当一个 master 节点宕机的时候,如何提升一个数据最新的 slave 成为 master。如果同时运行多个 master,又必须要解决 master 之间数据复制,以及对于客户端来说连接路由的问题。不同的方案,实施难度不一样,运维管理的成本也不一样。以上是架构层面的优化,可以用缓存,主从,分库分表。

3. 服务端优化分析

优化器就是对 SQL 语句进行分析,生成执行计划。

问题:在我们做项目的时候,有时会收到 DBA 的邮件,里面列出了我们项目上几个耗时比较长的查询语句,让我们去优化,这些语句是从哪里来的呢?我们的服务层每天执行了这么多 SQL 语句,它怎么知道哪些 SQL 语句比较慢呢?

3.1 慢查询日志 slow query log

3.1.1 打开慢查询日志开关

开启慢查询日志是有代价的(跟 bin log、optimizer-trace 一样),默认是关闭的:

image.png

还有一个参数,控制执行超过多长时间的 SQL 才记录到慢日志,默认是 10 秒。

image.png

set @@global.slow_query_log=1; -- 1 开启,0 关闭,重启后失效 
set @@global.long_query_time=3; -- mysql 默认的慢查询时间是 10 秒,另开一个窗口后才会查到最新值
show variables like '%long_query%';
show variables like '%slow_query%';

3.1.2 配置文件修改

或者修改配置文件 my.cnf。以下配置定义了慢查询日志的开关、慢查询的时间、日志文件的存放路径。

slow_query_log = ON
long_query_time=2
slow_query_log_file =/var/lib/mysql/localhost-slow.log

3.1.3 模拟慢查询

模拟慢查询:

select sleep(10);

查询 user_innodb 表的 500 万数据(检查是不是没有索引):

SELECT * FROM user_innodb where phone = '136';

3.1.4 慢查询日志分析

3.1.4.1 日志内容

show global status like 'slow_queries'; -- 查看有多少慢查询
show variables like '%slow_query%'; -- 获取慢日志目录

linux目录cat /var/lib/mysql/localhost-slow.log windows目录C:\ProgramData\MySQL\MySQL Server 5.7\Data\

上面的慢查询骚操作,记录在案:执行的用户、执行时间、获取锁时间,扫描的行 image.png

3.1.4.2 mysqldumpslow

MySQL 提供了 mysqldumpslow 的工具,在 MySQL 的 bin 目录下

mysqldumpslow --help

例如:Linux查询用时最多的 20 条慢 SQL:

mysqldumpslow -s t -t 20 -g 'select' /var/lib/mysql/localhost-slow.log

image.png

Count 代表这个 SQL 执行了多少次;
Time 代表执行的时间,括号里面是累计时间;
Lock 表示锁定的时间,括号是累计;
Rows 表示返回的记录数,括号是累计。

3.2 show profile

SHOW PROFILE 是谷歌高级架构师 Jeremy Cole 贡献给 MySQL 社区的,可以查看SQL 语句执行的时候使用的资源,比如 CPU、IO 的消耗情况。在 SQL 中输入 help profile 可以得到详细的帮助信息。

3.2.1 查看是否开启

select @@profiling;
set @@profiling=1;

3.2.2 查看profile统计

3.2.2.1 show profiles

show profiles;

image.png

3.2.2.2 show profile

--查看最后一个 SQL 的执行详细信息,从中找出耗时较多的环节(没有 s)。
show profile;

image.png

3.2.2.3 show profile for query + ID

--根据 ID 查看执行详细信息,在后面带上 for query + ID。
show profile for query 36;

image.png

3.3 其他系统命令

3.3.1 show processlist 运行线程

show full processlis;

image.png

含义
Id线程的唯一标志,可以根据它kill线程
User启动这个线程的用户,普通用户只能看到自己的线程
Host哪个 IP 端口发起的连接
db操作的数据库
Command线程的命令 dev.mysql.com/doc/refman/…
State线程状态,比如查询可能有 copying to tmp table,Sorting result,Sending data dev.mysql.com/doc/refman/…
InfoSQL 语句的前 100 个字符,如果要查看完整的 SQL 语句,用 SHOW FULL PROCESSLIST

3.3.2 show engine 存储引擎运行信息

dev.mysql.com/doc/refman/…

show engine 用来显示存储引擎的当前运行信息,包括事务持有的表锁、行锁信息;事务的锁等待情况;线程信号量等待;文件 IO 请求;buffer pool 统计信息。例如:

--文件很长有锁信息,buffer pool信息
show engine innodb status;

image.png

如果需要将监控信息输出到错误信息 error log 中(15 秒钟一次),可以开启输出。

show variables like 'innodb_status_output%';
-- 开启输出:
SET GLOBAL innodb_status_output=ON;
SET GLOBAL innodb_status_output_locks=ON;

3.3.3 show status 服务器运行状态

dev.mysql.com/doc/refman/…

SHOW STATUS 用于查看 MySQL 服务器运行状态(重启后会清空),有 session和 global 两种作用域,格式:参数-值。可以用 like 带通配符过滤。

--服务端状态值
show global status;

image.png

--服务器启动以后发起了多是次select查询
show global status like 'com_select';

image.png

4. SQL语句优化(EXPLAIN)

5.6.3以后的版本也可以分析insert、update、delete image.png

ColumnJson Name含义
idselect_id选择标识符
select_typeNone选择类型
tabletable_name用于输出行的表
partitionspartitions匹配的分区
typeaccess_type连接类型
possible_keyspossible_keys可能选择的索引
keykey实际选择的索引
key_lenkey所选键的长度
refref与索引比较的列
rowsrows估计要检查的行数
filteredfiltered按表条件过滤的行百分比
ExtraNone其他信息

4.1 id序号

4.1.1 id不同从大到小

image.png

4.1.2 id相同从上往下

image.png

4.1.3 id即有相同也有不同

如果 ID 有相同也有不同,就是 ID 不同的先大后小,ID 相同的从上往下。

4.1.4 小表驱动大表

先查中间结果小的表,先过滤以后中间结果小的表,这样就可以在临时表里面存储较少的数据。

在JOIN查询中经常用到的 inner join、left join、right join :

  1. 当使用left join时,左表是驱动表,右表是被驱动表 ;
  2. 当使用right join时,右表时驱动表,左表是被驱动表 ;
  3. 当使用inner join时,mysql会选择数据量比较小的表作为驱动表,大表作为被驱动表 ;

例如:现有两个表A与B ,表A有200条数据,表B有20万条数据 ;

  • 如果小的循环在外层,对于表连接来说就只连接200次 ;

  • 如果大的循环在外层,则需要进行20万次表连接,从而浪费资源,增加消耗 ;

  • 小表驱动大表 > A驱动表,B被驱动表

 for(200条){
   for(20万条){
     ...
   }
 }

12345
  • 大表驱动小表 > B驱动表,A被驱动表
 for(20万){
   for(200条){
    ...
   }
 }

4.2 select_type 查询类型

这里并没有列举全部(其它:DEPENDENT UNION、DEPENDENT SUBQUERY、MATERIALIZED、UNCACHEABLE SUBQUERY、UNCACHEABLE UNION)

4.2.1 simple

没有子查询、关联查询,是最普通的查询。

EXPLAIN SELECT * FROM teacher;

image.png

4.2.2 primary、subquery

  • PRIMARY:子查询 SQL 语句中的主查询,也就是最外面的那层查询。
  • SUBQUERY:子查询中所有的内层查询都是 SUBQUERY 类型的。
-- 查询 mysql 课程的老师手机号
EXPLAIN SELECT tc.phone FROM teacher_contact tc WHERE tcid = (
SELECT tcid FROM teacher t WHERE t.tid = (
SELECT c.tid FROM course c WHERE c.cname = 'mysql' ));

image.png

4.2.3 derived

DERIVED:衍生查询,表示在得到最终查询结果之前会用到临时表。例如:

-- 查询 ID 为 1 或 2 的老师教授的课程
EXPLAIN SELECT cr.cname FROM (
SELECT * FROM course WHERE tid = 1 UNION SELECT * FROM course WHERE tid = 2 ) cr;

image.png

对于关联查询,先执行右边的 table(UNION),再执行左边的 table,类型是 DERIVED。

4.2.4 union

UNION:用到了 UNION 查询。同上例。

4.2.5 union result

UNION RESULT:主要是显示哪些表之间存在 UNION 查询。<union2,3>代表 id=2 和 id=3 的查询存在 UNION。同上例。

4.3 type 连接类型

dev.mysql.com/doc/refman/…

所有的连接类型中,上面的最好,越往下越差。

在常用的链接类型中:system > const > eq_ref > ref > range > index > all

这 里 并 没 有 列 举 全 部 ( 其 他 : fulltext 、 ref_or_null 、 index_merger 、unique_subquery、index_subquery)。

以上访问类型除了 all,都能用到索引。

4.3.1 const

主键索引或者唯一索引,只能查到一条数据的 SQL。

DROP TABLE IF EXISTS single_data;
CREATE TABLE single_data(
id int(3) PRIMARY KEY,
content varchar(20)
);
insert into single_data values(1,'a');
EXPLAIN SELECT * FROM single_data a where id = 1;

image.png

4.3.2 system

system 是 const 的一种特例,只有一行满足条件。例如:只有一条数据的系统表。

EXPLAIN SELECT * FROM mysql.proxies_priv;

image.png

4.3.3 eq_ref

  • eq_ref 是除 const 之外最好的访问类型。
  • 通常出现在多表的 join 查询,表示对于前表的每一个结果,,都只能匹配到后表的一行结果。一般是唯一性索引的查询(UNIQUE 或 PRIMARY KEY)。

先删除 teacher 表中多余的数据,teacher_contact 有 3 条数据,teacher 表有 3条数据。

DELETE FROM teacher where tid in (4,5,6);
commit;
-- 备份
INSERT INTO `teacher` VALUES (4, 'james', 4);
INSERT INTO `teacher` VALUES (5, 'tom', 5);
INSERT INTO `teacher` VALUES (6, 'seven', 6);
commit;

为 teacher_contact 表的 tcid(第一个字段)创建主键索引

-- ALTER TABLE teacher_contact DROP PRIMARY KEY;
ALTER TABLE teacher_contact ADD PRIMARY KEY(tcid);

为 teacher 表的 tcid(第三个字段)创建普通索引

-- ALTER TABLE teacher DROP INDEX idx_tcid;
ALTER TABLE teacher ADD INDEX idx_tcid (tcid);

执行以下 SQL 语句:

explain select t.tcid from teacher t,teacher_contact tc where t.tcid = tc.tcid;

此时的执行计划(teacher_contact 表是 eq_ref):

image.png

小结:以上三种 system,const,eq_ref,都是可遇而不可求的,基本上很难优化到这个状态。

4.3.4 ref

查询用到了非唯一性索引,或者关联操作只使用了索引的最左前缀。例如:使用 tcid 上的普通索引查询:

explain SELECT * FROM teacher where tcid = 3;

image.png

4.3.5 range

索引范围扫描。如果 where 后面是 between and 或 <或 > 或 >= 或 <=或 in 这些,type 类型就为 range。 不走索引一定是全表扫描(ALL),所以先加上普通索引ALTER TABLE teacher ADD INDEX idx_tid (tid);

EXPLAIN SELECT * FROM teacher t WHERE t.tid <3;
-- 或
EXPLAIN SELECT * FROM teacher t WHERE tid BETWEEN 1 AND 2;

image.png

IN 查询也是 range(字段有主键索引)

EXPLAIN SELECT * FROM teacher_contact t WHERE tcid in (1,2,3);

image.png

4.3.6 index

Full Index Scan,查询全部索引中的数据(比不走索引要快)。

image.png

4.3.7 all

Full Table Scan,如果没有索引或者没有用到索引,type 就是 ALL。代表全表扫描。

4.3.8 NULL

不用访问表或者索引就能得到结果,例如:EXPLAIN select 1 from dual where 1=1;

image.png

4.3.9 小结

一般来说,需要保证查询至少达到range级别,最好能达到ref。ALL(全表扫描)和index(查询全部索引)都是需要优化的。

4.4 possible_key、key

可能用到的索引和实际用到的索引。如果是NULL就代表没有用到索引。possible_key可以有一个或者多个,可能用到索引不代表一定用到索引。 反过来,possible_key为空,key可能有值吗?

表上创建联合索引:

ALTER TABLE user_innodb DROP INDEX comidx_name_phone;
ALTER TABLE user_innodb add INDEX comidx_name_phone (name,phone);

image.png

结论:是由可能的(这里是覆盖索引的情况) 如果通过分析发现没有用到索引,就要检查 SQL 或者创建索引。

4.5 key_len

索引的长度(使用的字节数)。跟索引字段的类型、长度有关。

4.6 rows

MySQL认为扫描多少行才能返回请求的数据,是一个预估值。一般来说行数越少越好。

4.7 filtered

这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,它是一个百分比。

4.8 ref

使用哪个列或者常数和索引一起从表中筛选数据。

4.9 Extra

执行计划给出的额外的信息说明。

4.9.1 using index

用到了覆盖索引,不需要回表。

image.png

4.9.2 using where

使用了where过滤,表示存储引擎返回的记录并不是所有的都满足查询条件,需要在server层进行过滤(跟是否使用索引没有关系)。

image.png

4.9.3 using index condition(索引条件下推)

dev.mysql.com/doc/refman/…

4.9.4 using filesort

不能使用索引来排序,用到了额外的排序(跟磁盘或文件没有关系)。需要优化。(复合索引的前提)

复合索引的前提会发生:

ALTER TABLE user_innodb DROP INDEX comidx_name_phone;
ALTER TABLE user_innodb add INDEX comidx_name_phone (name,phone);

order by id 引起 image.png

4.9.5 using temporary

用到了临时表。例如(以下不是全部的情况)

4.9.5.1 distinct 非索引列

image.png

4.9.5.2 group by 非索引列

image.png

4.9.5.3 使用 join 的时候,group 任意列

image.png

image.png

4.10 SQL 与索引优化

当我们的 SQL 语句比较复杂,有多个关联和子查询的时候,就要分析 SQL 语句有没有改写的方法。举个简单的例子,一模一样的数据:

-- 大偏移量的 limit
explain select * from user_innodb limit 900000,10;
-- 改成先过滤 ID,再 limit
explain SELECT * FROM user_innodb WHERE id >= 900000 LIMIT 10;

image.png

image.png

对于具体的 SQL 语句的优化,MySQL 官网也提供了很多建议,这个是我们在分析具体的 SQL 语句的时候需要注意的,也是大家在以后的工作里面要去慢慢地积累的(这里我们就不一一地分析了)。 dev.mysql.com/doc/refman/…

5. 存储引擎

存储引擎的选择

为不同的业务表选择不同的存储引擎,例如:查询插入操作多的业务表,用MyISAM。临时数据用Memeroy。常规的并发大,更新多的表用InnoDB。

5.1 分区或者分表

分区不推荐。 交易历史表:在年底为下一年度建立12个分区,每个月一个分区。 渠道交易表:分成当日表;当月表;历史表,历史表再做分区。

5.2 字段定义

原则:使用可以正确存储数据的最小数据类型。 为每一列选择合适的字段类型:

INT 有 8 种类型,不同的类型的最大存储范围是不一样的。性别?用 TINYINT,因为 ENUM 也是整型存储。

image.png

5.3 字符类型

变长情况下,varchar 更节省空间,但是对于 varchar 字段,需要一个字节来记录长度。固定长度的用 char,不要用 varchar。

5.4 非空

非空字段尽量定义成 NOT NULL,提供默认值,或者使用特殊值、空串代替 null。NULL 类型的存储、优化、使用都会存在问题。

5.5 不要用外键、触发器、视图

降低了可读性;影响数据库性能,应该把把计算的事情交给程序,数据库专心做存储;数据的完整性应该在程序中检查。

5.6 大文件存储

不要用数据库存储图片(比如 base64 编码)或者大文件;把文件放在 NAS 上,数据库只需要存储 URI(相对路径),在应用中配置 NAS 服务器地址。

5.7 表拆分

将不常用的字段拆分出去,避免列数过多和数据量过大。比如在业务系统中,要记录所有接收和发送的消息,这个消息是 XML 格式的,用blob 或者 text 存储,用来追踪和判断重复,可以建立一张表专门用来存储报文。

6. 总结:优化体系

image.png

除了对于代码、SQL 语句、表定义、架构、配置优化之外,业务层面的优化也不能忽视。举几个例子:

1)在某一年的双十一,为什么会做一个充值到余额宝和余额有奖金的活动(充 300送 50)?

因为使用余额或者余额宝付款是记录本地或者内部数据库,而使用银行卡付款,需要调用接口,操作内部数据库肯定更快。

2)在去年的双十一,为什么在凌晨禁止查询今天之外的账单?

这是一种降级措施,用来保证当前最核心的业务。

3)最近几年的双十一,为什么提前一个多星期就已经有双十一当天的价格了?

预售分流。

在应用层面同样有很多其他的方案来优化,达到尽量减轻数据库的压力的目的,比如限流,或者引入 MQ 削峰,等等等等。

为什么同样用 MySQL,有的公司可以扛住百万千万级别的并发,而有的公司几百个并发都扛不住,关键在于怎么用。所以,用数据库慢,不代表数据库本身慢,有的时候还要往上层去优化。 当然,如果关系型数据库解决不了的问题,我们可能需要用到搜索引擎或者大数据的方案了,并不是所有的数据都要放到关系型数据库存储。