导致数据库慢查询的12个常见原因,以及对应的解决方法。

图片

前言

日常开发中,我们经常会遇到数据库慢查询。那么导致数据慢查询都有哪些常见的原因?今天就跟大家聊聊导致数据库慢查询的12个常见原因,以及对应的解决方法。

图片


1. SQL没加索引


很多时候,我们的慢查询,都是因为没有加索引。如果没有加索引的话,会导致全表扫描的。因此,应考虑在where的条件列,建立索引,尽量避免全表扫描。

反例:

select * from user_info where name ='捡田螺的小男孩公众号' ;

图片

正例:

//添加索引
alter table user_info add index idx_name (name);

图片


2. SQL 索引不生效


有时候我们明明加了索引了,但是索引却不生效。在哪些场景,索引会不失效呢?主要有以下十大经典场景:

图片

2.1 隐式的类型转换,索引失效

我们创建一个用户user表

CREATE TABLE user (
  id int(11) NOT NULL AUTO_INCREMENT,
  userId varchar(32) NOT NULL,
  age varchar(16) NOT NULL,
  name varchar(255) NOT NULL,
  PRIMARY KEY (id),
  KEY idx_userid (userId) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

userId字段为字串类型,是B+树的普通索引,如果查询条件传了一个数字过去,会导致索引失效。如下:

图片

如果给数字加上’',也就是说,传的是一个字符串呢,当然是走索引,如下图:

图片

为什么第一条语句未加单引号就不走索引了呢?这是因为不加单引号时,是字符串跟数字的比较,它们类型不匹配,MySQL会做隐式的类型转换,把它们转换为浮点数再做比较。隐式的类型转换,索引会失效

2.2 查询条件包含or,可能导致索引失效

我们还是用这个表结构:

CREATE TABLE user (
  id int(11) NOT NULL AUTO_INCREMENT,
  userId varchar(32) NOT NULL,
  age varchar(16) NOT NULL,
  name varchar(255) NOT NULL,
  PRIMARY KEY (id),
  KEY idx_userid (userId) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

其中userId加了索引,但是age没有加索引的。我们使用了or,以下SQL是不走索引的,如下:

图片

对于or+没有索引的age这种情况,假设它走了userId的索引,但是走到age查询条件时,它还得全表扫描,也就是需要三步过程:全表扫描+索引扫描+合并

如果它一开始就走全表扫描,直接一遍扫描就完事Mysql优化器出于效率与成本考虑,遇到or条件,让索引失效,看起来也合情合理嘛。

注意:如果or条件的列都加了索引,索引可能会走也可能不走,大家可以自己试一试哈。但是平时大家使用的时候,还是要注意一下这个or,学会用explain分析。遇到不走索引的时候,考虑拆开两条SQL

2.3. like通配符可能导致索引失效。

并不是用了like通配符,索引一定会失效,而是like查询是以%开头,才会导致索引失效

like查询以%开头,索引失效

explain select * from user where userId like '%123';

图片

把%放后面,发现索引还是正常走的,如下:

explain select * from user where userId like '123%';

图片

既然like查询以%开头,会导致索引失效。我们如何优化呢?

  • 使用覆盖索引
  • 把%放后面

2.4 查询条件不满足联合索引的最左匹配原则

MySQl建立联合索引时,会遵循最左前缀匹配的原则,即最左优先。如果你建立一个(a,b,c)的联合索引,相当于建立了(a)、(a,b)、(a,b,c)三个索引。

假设有以下表结构:

CREATE TABLE user (
  id int(11) NOT NULL AUTO_INCREMENT,
  user_id varchar(32) NOT NULL,
  age varchar(16) NOT NULL,
  name varchar(255) NOT NULL,
  PRIMARY KEY (id),
  KEY idx_userid_name (user_id,name) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

有一个联合索引idx_userid_name,我们执行这个SQL,查询条件是name,索引是无效:

explain select * from user where name ='捡田螺的小男孩';

因为查询条件列name不是联合索引idx_userid_name中的第一个列,索引不生效

图片

在联合索引中,查询条件满足最左匹配原则时,索引才正常生效。
图片

2.5 在索引列上使用mysql的内置函数

表结构:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` varchar(32) NOT NULL,
  `login_time` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_userId` (`userId`) USING BTREE,
  KEY `idx_login_time` (`login_Time`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

虽然login_time加了索引,但是因为使用了mysql的内置函数Date_ADD(),索引直接GG,如图:

图片

一般这种情况怎么优化呢?可以把内置函数的逻辑转移到右边,如下:

explain  select * from user where login_time = DATE_ADD('2022-05-22 00:00:00',INTERVAL 1 DAY);

图片

2.6 对索引进行列运算(如,+、-、*、/),索引不生效

表结构:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` varchar(32) NOT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_age` (`age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

虽然age加了索引,但是因为它进行运算,索引直接迷路了。。。如图:

图片

所以不可以对索引列进行运算,可以在代码处理好,再传参进去。

2.7 索引字段上使用(!= 或者 < >),索引可能失效

表结构:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` int(11) NOT NULL,
  `age` int(11) DEFAULT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_age` (`age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

虽然age加了索引,但是使用了!= 或者< >,not in这些时,索引如同虚设。如下:

图片

图片

其实这个也是跟mySQL优化器有关,如果优化器觉得即使走了索引,还是需要扫描很多很多行的哈,它觉得不划算,不如直接不走索引。平时我们用!= 或者< >,not in的时候,留点心眼哈。

2.8 索引字段上使用is null, is not null,索引可能失效

表结构:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `card` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) USING BTREE,
  KEY `idx_card` (`card`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

单个name字段加上索引,并查询name为非空的语句,其实会走索引的,如下:

图片

单个card字段加上索引,并查询name为非空的语句,其实会走索引的,如下:在这里插入图片描述

但是它两用or连接起来,索引就失效了,如下:

图片

很多时候,也是因为数据量问题,导致了MySQL优化器放弃走索引。同时,平时我们用explain分析SQL的时候,如果type=range,要注意一下哈,因为这个可能因为数据量问题,导致索引没无效。

2.9 左右连接,关联的字段编码格式不一样

新建两个表,一个user,一个user_job

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

CREATE TABLE `user_job` (
  `id` int(11) NOT NULL,
  `userId` int(11) NOT NULL,
  `job` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

user表的name字段编码是utf8mb4,而user_job表的name字段编码为utf8。

图片
图片

执行左外连接查询,user_job表还是走全表扫描,如下:

图片

如果把它们的name字段改为编码一致,相同的SQL,还是会走索引。

图片

所以大家在做表关联时,注意一下关联字段的编码问题哈

2.10 优化器选错了索引

MySQL 中一张表是可以支持多个索引的。你写SQL语句的时候,没有主动指定使用哪个索引的话,用哪个索引是由MySQL来确定的。

我们日常开发中,不断地删除历史数据和新增数据的场景,有可能会导致MySQL选错索引。那么有哪些解决方案呢?

  • 使用force index 强行选择某个索引
  • 修改你的SQl,引导它使用我们期望的索引
  • 优化你的业务逻辑
  • 优化你的索引,新建一个更合适的索引,或者删除误用的索引。

- limit深分页问题


limit深分页问题,会导致慢查询,应该大家都司空见惯了吧。

3.1 limit深分页为什么会变慢

limit深分页为什么会导致SQL变慢呢?假设我们有表结构如下:

CREATE TABLE account (
  id int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
  name varchar(255) DEFAULT NULL COMMENT '账户名',
  balance int(11) DEFAULT NULL COMMENT '余额',
  create_time datetime NOT NULL COMMENT '创建时间',
  update_time datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (id),
  KEY idx_name (name),
  KEY idx_create_time (create_time) //索引
) ENGINE=InnoDB AUTO_INCREMENT=1570068 DEFAULT CHARSET=utf8 ROW_FORMAT=REDUNDANT COMMENT='账户表';

你知道以下SQL,执行过程是怎样的嘛?

select id,name,balance from account where create_time> '2020-09-19' limit 100000,10;

这个SQL的执行流程:

  • 通过普通二级索引树idx_create_time,过滤create_time条件,找到满足条件的主键id
  • 通过主键id,回到id主键索引树,找到满足记录的行,然后取出需要展示的列(回表过程)
  • 扫描满足条件的100010行,然后扔掉前100000行,返回。

图片

limit深分页,导致SQL变慢原因有两个:

  • limit语句会先扫描offset+n行,然后再丢弃掉前offset行,返回后n行数据。也就是说limit 100000,10,就会扫描100010行,而limit 0,10,只扫描10行。

  • limit 100000,10 扫描更多的行数,也意味着回表更多的次数。*

3.2 如何优化深分页问题

我们可以通过减少回表次数来优化。一般有标签记录法和延迟关联法。

标签记录法(流式分页,不能跨页

就是标记一下上次查询到哪一条了,下次再来查的时候,从该条开始往下扫描。就好像看书一样,上次看到哪里了,你就折叠一下或者夹个书签,下次来看的时候,直接就翻到啦。

假设上一次记录到100000,则SQL可以修改为:

select id,name,balance FROM account where id > 100000 limit 10;

这样的话,后面无论翻多少页,性能都会不错的,因为命中了id索引。但是这种方式有局限性:需要一种类似连续自增的字段。

延迟关联法

延迟关联法,就是把条件转移到主键索引树,然后减少回表。如下:

select  acct1.id,acct1.name,acct1.balance FROM account acct1 INNER JOIN (SELECT a.id FROM account a WHERE a.create_time > '2020-09-19' limit 100000, 10) AS acct2 on acct1.id= acct2.id;

覆盖索引 + 主键索引,减少回表

优化思路就是,先通过idx_create_time二级索引树查询到满足条件的主键ID,再与原表通过主键ID内连接,这样后面直接走了主键索引了,同时也减少了回表


4. 单表数据量太大


4.1 单表数据量太大为什么会变慢?

一个表的数据量达到好几千万或者上亿时,加索引的效果没那么明显啦。性能之所以会变差,是因为维护索引的B+树结构层级变得更高了,查询一条数据时,需要经历的磁盘IO变多,因此查询性能变慢。

4.2 一棵B+树可以存多少数据量

大家是否还记得,一个B+树大概可以存放多少数据量呢?

InnoDB存储引擎最小储存单元是页,一页大小就是16k

B+树叶子存的是数据,内部节点存的是键值+指针。索引组织表通过非叶子节点的二分查找法以及指针确定数据在哪个页中,进而再去数据页中找到需要的数据;

图片

假设B+树的高度为2的话,即有一个根结点和若干个叶子结点。这棵B+树的存放总记录数为=根结点指针数*单个叶子节点记录行数。

  • 如果一行记录的数据大小为1k,那么单个叶子节点可以存的记录数 =16k/1k =16.
  • 非叶子节点内存放多少指针呢?我们假设主键ID为bigint类型,长度为8字节(面试官问你int类型,一个int就是32位,4字节),而指针大小在InnoDB源码中设置为6字节,所以就是8+6=14字节,16k/14B
    =16*1024B/14B = 1170

因此,一棵高度为2的B+树,能存放1170 * 16=18720条这样的数据记录。同理一棵高度为3的B+树,能存放1170 *1170 *16 =21902400,也就是说,可以存放两千万左右的记录。B+树高度一般为1-3层,已经满足千万级别的数据存储。

如果B+树想存储更多的数据,那树结构层级就会更高,查询一条数据时,需要经历的磁盘IO变多,因此查询性能变慢。

4.3 如何解决单表数据量太大,查询变慢的问题

一般超过千万级别,我们可以考虑分库分表了。

分库分表可能导致的问题:

  • 事务问题
  • 跨库问题
  • 排序问题
  • 分页问题
  • 分布式ID

因此,大家在评估是否分库分表前,先考虑下,是否可以把部分历史数据归档先,如果可以的话,先不要急着分库分表。如果真的要分库分表,综合考虑和评估方案。比如可以考虑垂直、水平分库分表。水平分库分表策略的话,range范围hash取模range+hash取模混合等等。


5. join 或者子查询过多


一般来说,不建议使用子查询,可以把子查询改成join来优化。而数据库有个规范约定就是:尽量不要有超过3个以上的表连接。为什么要这么建议呢? 我们来聊聊,join哪些方面可能导致慢查询吧。

MySQL中,join的执行算法,分别是:Index Nested-Loop Join和Block Nested-Loop Join。

  • Index Nested-Loop Join:这个join算法,跟我们写程序时的嵌套查询类似,并且可以用上被驱动表的索引。
  • Block Nested-Loop
    Join:这种join算法,被驱动表上没有可用的索引,它会先把驱动表的数据读入线程内存join_buffer中,再扫描被驱动表,把被驱动表的每一行取出来,跟join_buffer中的数据做对比,满足join条件的,作为结果集的一部分返回。

join过多的问题:

一方面,过多的表连接,会大大增加SQL复杂度。另外一方面,如果可以使用被驱动表的索引那还好,并且使用小表来做驱动表,查询效率更佳。如果被驱动表没有可用的索引,join是在join_buffer内存做的,如果匹配的数据量比较小或者join_buffer设置的比较大,速度也不会太慢。但是,如果join的数据量比较大时,mysql会采用在硬盘上创建临时表的方式进行多张表的关联匹配,这种显然效率就极低,本来磁盘的 IO 就不快,还要关联。

一般情况下,如果业务需要的话,关联2~3个表是可以接受的,但是关联的字段需要加索引哈。如果需要关联更多的表,建议从代码层面进行拆分,在业务层先查询一张表的数据,然后以关联字段作为条件查询关联表形成map,然后在业务层进行数据的拼装。


6. in元素过多


如果使用了in,即使后面的条件加了索引,还是要注意in后面的元素不要过多哈。in元素一般建议不要超过500个,如果超过了,建议分组,每次500一组进行哈。

反例:

select user_id,name from user where user_id in (1,2,3...1000000);

如果我们对in的条件不做任何限制的话,该查询语句一次性可能会查询出非常多的数据,很容易导致接口超时。尤其有时候,我们是用的子查询,in后面的子查询,你都不知道数量有多少那种,更容易采坑(所以我把in元素过多抽出来作为一个小节)。如下这种子查询:

select * from user where user_id in (select author_id from artilce where type = 1);

正例是,分批进行,每批500个

select user_id,name from user where user_id in (1,2,3...500);

如果传参的ids太多,还可以做个参数校验什么的

if (userIds.size() > 500) {
    throw new Exception("单次查询的用户Id不能超过200");
}

7. 数据库在刷脏页


7.1 什么是脏页

内存数据页磁盘数据页内容不一致的时候,我们称这个内存页为“脏页”。内存数据写入到磁盘后内存和磁盘上的数据页的内容就一致了,称为“干净页”。一般有更新SQL才可能会导致脏页,我们回忆一下:一条更新语句是如何执行的

7.2 一条更新语句是如何执行的?

以下的这个更新SQL,如何执行的呢?

update t set c=c+1 where id=666;
  • 对于这条更新SQL,执行器会先找引擎取id=666这一行。如果这行所在的数据页本来就在内存中的话,就直接返回给执行器。如果不在内存,就去磁盘读入内存,再返回。
  • 执行器拿到引擎给的行数据后,给这一行C的值加一,得到新的一行数据,再调用引擎接口写入这行新数据。
  • 引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,但是此时redo log 是处于prepare状态的哈。
  • 执行器生成这个操作的binlog,并把binlog写入磁盘。
  • 执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更新完成。

图片

InnoDB 在处理更新语句的时候,只做了写日志这一个磁盘操作。这个日志叫作redo log(重做日志)。平时更新SQL执行得很快,其实是因为它只是在写内存和redo log日志,等到空闲的时候,才把redo log日志里的数据同步到磁盘中

有些小伙伴可能有疑惑,redo log日志不是在磁盘嘛?那为什么不慢?其实是因为写redo log的过程是顺序写磁盘的。磁盘顺序写会减少寻道等待时间,速度比随机写要快很多的。

7.3 为什么会出现脏页呢?

更新SQL只是在写内存和redo log日志,等到空闲的时候,才把redo log日志里的数据同步到磁盘中。这时内存数据页跟磁盘数据页内容不一致,我们称之为脏页。

7.4 什么时候会刷脏页(flush)?

InnoDB存储引擎的redo log大小是固定,且是环型写入的,如下图(图片来源于MySQL 实战 45 讲):

图片

那什么时候会刷脏页?有几种场景:

  • redo log写满了,要刷脏页。这种情况要尽量避免的。因为出现这种情况时,整个系统就不能再接受更新啦,即所有的更新都必须堵住。
  • 内存不够了,需要新的内存页,就要淘汰一些数据页,这时候会刷脏页

InnoDB 用缓冲池(buffer pool)管理内存,而当要读入的数据页没有在内存的时候,就必须到缓冲池中申请一个数据页。这时候只能把最久不使用的数据页从内存中淘汰掉:如果要淘汰的是一个干净页,就直接释放出来复用;但如果是脏页呢,就必须将脏页先刷到磁盘,变成干净页后才能复用。

  • MySQL 认为系统空闲的时候,也会刷一些脏页

  • MySQL 正常关闭时,会把内存的脏页都 flush 到磁盘上

7.5 为什么刷脏页会导致SQL变慢呢?

  • redo log写满了,要刷脏页,这时候会导致系统所有的更新堵住,写性能都跌为0了,肯定慢呀。一般要杜绝出现这个情况。

  • 一个查询要淘汰的脏页个数太多,一样会导致查询的响应时间明显变长。


- order by 文件排序


order by就一定会导致慢查询吗?不是这样的哈,因为order by平时用得多,并且数据量一上来,还是走文件排序的话,很容易有慢SQL的。听我娓娓道来,order by哪些时候可能会导致慢SQL哈。

8.1 order by 的 Using filesort文件排序

我们平时经常需要用到order by ,主要就是用来给某些字段排序的。比如以下SQL:

select name,age,city from staff where city = '深圳' order by age limit 10;

它表示的意思就是:查询前10个,来自深圳员工的姓名、年龄、城市,并且按照年龄小到大排序。

图片

查看explain执行计划的时候,可以看到Extra这一列,有一个Using filesort,它表示用到文件排序。

8.2 order by文件排序效率为什么较低

order by用到文件排序时,为什么查询效率会相对低呢?

图片

order by的文件排序,分为全字段排序rowid排序。它是拿max_length_for_sort_data结果行数据长度对比,如果结果行数据长度超过max_length_for_sort_data这个值,就会走rowid排序,相反,则走全字段排序。

rowid排序

rowid排序,一般需要回表去找满足条件的数据,所以效率会慢一点。以下这个SQL,使用rowid排序,执行过程是这样:

select name,age,city from staff where city = '深圳' order by age limit 10;
  • MySQL 为对应的线程初始化sort_buffer,放入需要排序的age字段,以及主键id
  • 从索引树idx_city, 找到第一个满足 city='深圳’条件的主键id,也就是图中的id=9;
  • 到主键id索引树拿到id=9的这一行数据, 取age和主键id的值,存到sort_buffer
  • 从索引树idx_city拿到下一个记录的主键id,即图中的id=13;
  • 重复步骤 3、4 直到city的值不等于深圳为止;
  • 前面5步已经查找到了所有city为深圳的数据,在sort_buffer中,将所有数据根据age进行排序;
  • 遍历排序结果,取前10行,并按照id的值回到原表中,取出city、name 和 age三个字段返回给客户端。

图片

全字段排序

同样的SQL,如果是走全字段排序是这样的:

select name,age,city from staff where city = '深圳' order by age limit 10;
  1. MySQL 为对应的线程初始化sort_buffer,放入需要查询的name、age、city字段
  2. 从索引树idx_city, 找到第一个满足 city='深圳’条件的主键 id,也就是图中的id=9;
  3. 到主键id索引树拿到id=9的这一行数据, 取name、age、city三个字段的值,存到sort_buffer;
  4. 从索引树idx_city 拿到下一个记录的主键id,即图中的id=13;
  5. 重复步骤 3、4 直到city的值不等于深圳为止;
  6. 前面5步已经查找到了所有city为深圳的数据,在sort_buffer中,将所有数据根据age进行排序;
  7. 按照排序结果取前10行返回给客户端。

在这里插入图片描述

sort_buffer的大小是由一个参数控制的:sort_buffer_size。

  • 如果要排序的数据小于sort_buffer_size,排序在sort_buffer内存中完成
  • 如果要排序的数据大于sort_buffer_size,则借助磁盘文件来进行排序。

借助磁盘文件排序的话,效率就更慢一点。因为先把数据放入sort_buffer,当快要满时。会排一下序,然后把sort_buffer中的数据,放到临时磁盘文件,等到所有满足条件数据都查完排完,再用归并算法把磁盘的临时排好序的小文件,合并成一个有序的大文件。

8.3 如何优化order by的文件排序

order by使用文件排序,效率会低一点。我们怎么优化呢?

  • 因为数据是无序的,所以就需要排序。如果数据本身是有序的,那就不会再用到文件排序啦。而索引数据本身是有序的,我们通过建立索引来优化order
    by语句
  • 我们还可以通过调整max_length_for_sort_data、sort_buffer_size等参数优化;

- 拿不到锁


有时候,我们查询一条很简单的SQL,但是却等待很长的时间,不见结果返回。一般这种时候就是表被锁住了,或者要查询的某一行或者几行被锁住了。我们只能慢慢等待锁被释放

举一个生活的例子哈,你和别人合租了一间房子,这个房子只有一个卫生间的话。假设某一时刻,你们都想去卫生间,但是对方比你早了一点点。那么此时你只能等对方出来后才能进去。

这时候,我们可以用show processlist命令,看看当前语句处于什么状态哈。


10. delete + in子查询不走索引!


之前见到过一个生产慢SQL问题,当delete遇到in子查询时,即使有索引,也是不走索引的。而对应的select + in子查询,却可以走索引。
MySQL版本是5.7,假设当前有两张表account和old_account,表结构如下:

CREATE TABLE `old_account` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
  `name` varchar(255) DEFAULT NULL COMMENT '账户名',
  `balance` int(11) DEFAULT NULL COMMENT '余额',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1570068 DEFAULT CHARSET=utf8 ROW_FORMAT=REDUNDANT COMMENT='老的账户表';

CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
  `name` varchar(255) DEFAULT NULL COMMENT '账户名',
  `balance` int(11) DEFAULT NULL COMMENT '余额',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1570068 DEFAULT CHARSET=utf8 ROW_FORMAT=REDUNDANT COMMENT='账户表';

执行的SQL如下:

delete from account where name in (select name from old_account);

查看执行计划,发现不走索引:

图片

但是如果把delete换成select,就会走索引。如下:

图片

为什么select + in子查询会走索引,delete + in子查询却不会走索引呢?
我们执行以下SQL看看:

explain select * from account where name in (select name from old_account);

show WARNINGS; //可以查看优化后,最终执行的sql

结果如下:

select `test2`.`account`.`id` AS `id`,`test2`.`account`.`name` AS `name`,`test2`.`account`.`balance` AS `balance`,`test2`.`account`.`create_time` AS `create_time`,`test2`.`account`.`update_time` AS `update_time` from `test2`.`account` 
semi join (`test2`.`old_account`)
where (`test2`.`account`.`name` = `test2`.`old_account`.`name`)

可以发现,实际执行的时候,MySQL对select in子查询做了优化,把子查询改成join的方式,所以可以走索引。但是很遗憾,对于delete in子查询,MySQL却没有对它做这个优化。

11、group by使用临时表

group by一般用于分组统计,它表达的逻辑就是根据一定的规则,进行分组。日常开发中,我们使用得比较频繁。如果不注意,很容易产生慢SQL。

11.1 group by的执行流程

假设有表结构:

CREATE TABLE `staff` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `id_card` varchar(20) NOT NULL COMMENT '身份证号码',
  `name` varchar(64) NOT NULL COMMENT '姓名',
  `age` int(4) NOT NULL COMMENT '年龄',
  `city` varchar(64) NOT NULL COMMENT '城市',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COMMENT='员工表';

我们查看一下这个SQL的执行计划:

explain select city ,count(*) as num from staff group by city;

图片

  • Extra 这个字段的Using temporary表示在执行分组的时候使用了临时表
  • Extra 这个字段的Using filesort表示使用了文件排序

group by是怎么使用到临时表和排序了呢?我们来看下这个SQL的执行流程

select city ,count(*) as num from staff group by city;
  1. 创建内存临时表,表里有两个字段city和num;
  2. 全表扫描staff的记录,依次取出city = 'X’的记录。
    判断临时表中是否有为 city='X’的行,没有就插入一个记录 (X,1);
    如果临时表中有city='X’的行,就将X这一行的num值加 1;
  3. 遍历完成后,再根据字段city做排序,得到结果集返回给客户端。这个流程的执行图如下:

图片

临时表的排序是怎样的呢?

就是把需要排序的字段,放到sort buffer,排完就返回。在这里注意一点哈,排序分全字段排序rowid排序

1. 如果是全字段排序,需要查询返回的字段,都放入sort buffer,根据排序字段排完,直接返回

2. 如果是rowid排序,只是需要排序的字段放入sort buffer,然后多一次回表操作,再返回。

11.2 group by可能会慢在哪里?

group by使用不当,很容易就会产生慢SQL 问题。因为它既用到临时表,又默认用到排序。有时候还可能用到磁盘临时表

1. 如果执行过程中,会发现内存临时表大小到达了上限(控制这个上限的参数就是tmp_table_size),会把内存临时表转成磁盘临时表

2. 如果数据量很大,很可能这个查询需要的磁盘临时表,就会占用大量的磁盘空间。

11.3 如何优化group by呢?

从哪些方向去优化呢?

  • 方向1:既然它默认会排序,我们不给它排是不是就行啦。
  • 方向2:既然临时表是影响group by性能的X因素,我们是不是可以不用临时表?

我们一起来想下,执行group by语句为什么需要临时表呢?group by的语义逻辑,就是统计不同的值出现的个数。如果这个这些值一开始就是有序的,我们是不是直接往下扫描统计就好了,就不用临时表来记录并统计结果啦?

可以有这些优化方案:

  • group by 后面的字段加索引
  • order by null 不用排序
  • 尽量只使用内存临时表
  • 使用SQL_BIG_RESULT

系统硬件或网络资源

  • 如果数据库服务器内存、硬件资源,或者网络资源配置不是很好,就会慢一些哈。这时候可以升级硬件配置。这就好比你的计算机有时候很卡,你可以加个内存条什么的一个道理。
  • 如果数据库压力本身很大,比如高并发场景下,大量请求到数据库来,数据库服务器CPU占用很高或者IO利用率很高,这种情况下所有语句的执行都有可能变慢的哈。这时候你开始排查是不是出什么问题啦。

最后

如果测试环境的数据库一些参数配置,和生产的环境参数配置不一致啊,也容易产生慢SQL哈。之前见过一个慢SQL的生产案例,就是测试环境用了index merge,所以查看explain执行计划时,是可以走索引的,但是到了生产,确实全表扫描,最后排查发现是生产环境配置把index merge关闭了。

有了这份MySQL慢查询优化葵花宝典,你和面试官吹牛皮绰绰有余!

  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
请问在openlayers中如何删除Layer中的marker 3 在层中加了一个标记怎么永久的存到地图上 3 OpenLayers 如何设死地图边界 3 谁写过openlayers解析xml 3 openlayer.popup 3 OpenLayers 怎么做字段标注 4 字段标注不是用OL做的,而是你在SLD中定义用数据表的哪个字段做标签就可以了 4 OpenLayers 中画最短轨迹 4 使用TileCache配合OpenLayers 4 openlayers如何准确测距 9 openlayers简单实用例子 9 关于TileCache函数 13 openlayers 有函数说明的类图 14 go 14 Maker 一多,客户端就死掉 15 OpenLayers 怎么实现 AJAX 16 openlayer 要怎么研究才会应用得比较自如 16 改良OpenLayers的Popup 16 在openlayers的地图中要添加地图搜索功能一般采用什么方法 17 openlayers中如何访问geoserver发布的图片 18 什么是TMS? 18 怎样设置瓦片的大小 18 画一条线,数据量太大,怎么办? 18 怎么在图层上动态画点 20 GeoExt集成google map ,地图偏移 21 用JavaScript为OpenLayers.Marker的加上单击显示详细信息的功能 21 改良OpenLayers的MousePostion 25 使用OpenLayers实现一个在鼠标点击处添加标记的效果 27 openlayers 利用google maps的卫星地图 29 openLayers集成google map ,点标注错位 30 openlayers可以做出谷歌效果的地图吗? 31 用OpenLayers API和类来画点、线、面 32 OGC标准术语介绍WMS、WFS、WCS 35 如何控制地图的放缩 37 openLayers链接WMS的代码探讨 37 openlayers 2.5 矢量层在ie下闪烁的问题 38 openLayers 在地图上添加一个点并保存 39 openLayers 各个参数的意义 42 geoserver能搞出这种风格的图来吗? 43 关于SLD的线切割后的设置 43 GEOSERVE 标注铁路,使用 SLD 44 geoserver 发布.shp 中文乱码问题 怎么解决啊 45 Geoserver怎么连postGreSql 数据库 48 Geoserver连Oracle Spatial 52 GeoServer架构浅谈 53 Geoserver发布地图无法显示问题 57 WebGIS相关的OpenGIS规范 58 geoserver中地图以外的区域以自定义图片填充 62 怎样修改 geoServer 的用户名密码 65 GeoServer中的WMS服务生成的png图片背景色为透明 65 比例尺 65 需不需要一个layer对应一个store 66 如何部署shp 66 用GeoWebCache后Marker错位 66 标签太大导致不能显示? 67 geoserver把两个shapefile格式的图叠 67 GeoServer 能够集成 Google Map? 68 gwc地图切片的缓存路径在web.xml中设置么 68 如何实现地图查询? 68 sld文件怎么用 69 在sld中怎么控制路名的间隔? 69
数据库系统管理制度 1. 总 则 第一条 为加强我司数据库管理,保障数据库正常、有效运行,确保数据库安全,使数据库能更 好地服务于生产,特制定本管理制度。 第二条 公司数据库管理员负责评级数据库的日常维护和运行管理。 第三条 公司IT总监负责对数据库使用者进行权限审批。 2. 数据库系统的修改和维护 第一条 数据库系统的变更,指数据库硬件或参数调整等;涉及到停机时,应征求业务部门对系 统使用,并根据实际情况由部门负责人确定并发布停机时间后,在停机时间范围内完成 相关操作,同时数据库管理人员应该评估风险后,再操作,并有相应的应急处理方案。 第二条 数据库业务数据的变更修改,指开发部门或业务部门关于数据库业务表数据的变更修改 ;应由开发部门提供具体表和字段,或要求开发部门提供脚本,由数据库管理人员执行 操作;并且修改前先进行数据备份。 第三条 数据库系统的监控,应实时了解数据库运行状态,确保数据库可用状态。 第四条 数据库系统的健康检查,应定期对数据库的响应指标巡检。并给出巡检的报告。健康检 查应当定期执行,每周进行一次健康检查。 第五条 数据的备份恢复,备份的主要目的就是为了恢复,所以在一段时期应当数据库进行恢复 演练,以防止意外发生时数据不能恢复而造成损失。恢复演练应尽量避开数据库生产环 境,以免影响生产的运行。恢复演练应当定期执行,以季度为单位,每季度至少演练一 次。 第六条 数据库故障处理,数据库系统中常见的四种故障主要有事务内部的故障、系统故障、介 质故障以及计算机病毒故障,对应于每种故障都有不同的解决方法。。 3. 数据库的安全管理 第一条 数据库管理员负责数据库系统的安全管理,保证安全管理软件的及时升级。 第二条 数据库应每周至少备份一次,数据库管理员应在保证数据安全和保密的情况下,采取适 当方式保存备份文件,保证数据库出现异常时能快速恢复,避免或尽量减少数据丢失。 数据库系统管理制度的实现方法 1. 数据库变更 数据库系统变更 变更管理流程分为:需求调研、需求评审、变更申请、变更方案、变更审批、执行 变更六个步骤: 数据库的调整应针对问题先进行调研评审,再定制变更方案及实施计划回退方案后再 进行实施。 1.需求调研:由数据库管理员根据数据库运行监控过程中发现的问题进行调研,确 定问题解决方案。 2.需求评审:由数据库管理员组织调研,由部门经理确定后,完成对变更需求的分 析评审。 3.变更申请:由数据库管理员根据变更需求,给出初步的方案建议。 4.变更方案:由部门负责人确定变更评审小组成员,评审中修改并确定变更的实施 方案及应急回退方案。 5.变更审批:小型变更由部门负责人审批,大、中型变更由部门审批。 6.执行变更:由数据库管理运维团队执行系统变更的具体实现工作。 业务表数据变更 业务数据变更应由需求部门邮件申请,并由部门负责人进行审批后进行变更。 1.变更申请:由需求部门提出申请。 2.变更审批:由需求部门及运维部门负责人审批。 3.变更执行:确定变更需求后由数据库管理员进行相关操作。 2. 数据库系统的监控,应实时了解数据库运行状态。确保数据库可用状态。 1. 第三方数据库监控软件。 ZABBIX,APM 2. 数据库监控软件。 Oracle Enterprise Manager 3. 手动获取数据库报表 StatsPack、Automatic Workload Repository、Active Session History 3. 数据库系统的健康检查,应定期对数据库的响应指标巡检。 1. 数据库管理员每天定时巡检数据库服务器,并填写记录表(见附录一)。 2. TOP前5执行慢的SQL语句进行优化跟踪,并邮件提交给部门经理,抄送IT总监。 第四条 核心数据的日常备份管理。 1)数据库应每周至少备份一次,数据库管理员应在保证数据安全和保密的情况下 ,采取适当方式保存备份文件,保证数据库出现异常时能快速恢复,避免或尽量 减少数据丢失。 2)为了节省磁盘空间,大型业务库使用增量备份,周日全备,周一到六为增量备 份。 3)协助开发和业务部门做数据变更时,需提前备份好数据,再执行变更步骤。 第五条 数据库故障处理 一、常见的四种故障 (1)事务内部的故障:事务内部故障可分为预期的和非预期的,其中大部分的故障 都是非预期的。预期的事务内部故障是指可以通过事务程序本身发现的事务内部 故障;非预期的事务内部故障是不能由事务程序处理的,如运算溢出故障、并发事 务死锁故障、违反了某些完整性限制而导致的故障等。 (2)系统故障:系统故障也称为软故障,是指数据库在运行过程中,由于硬件故障 、数据库软件及操作系统的漏洞、突然停电灯情况,导致系统停止运转,所有正 在运行的事务以非正常方式终止,需要系统重新启动的一类故障。这类事务不破
数据库设计反范式 1.范式 1.1 1NF-3NF 定义 1NF确保原⼦性(Atomicity)原⼦性的粒度、原⼦性的价值(1范式:单值,并且把值当做单值⽤,建议不要把值 拆开) 2NF检查对键的完全依赖价值在在于控制数据冗余和查询性能 3NF检查属性的独⽴性 规范化的价值合理规范化的模型可应对需求变 更 规范化数据重复降⾄最少 为什么要有三范式? 不会发⽣插⼊(insert)、删除(delete)和更新(update)操作异常。 控制数据冗余和提⾼查询性能 更好的进⾏数据有效性 检查,提⾼存储效率。 范式的满⾜便于数据⼀致性的控制 如何控制冗余? 使⽤三范式 缺点 范式化的表,在查询的时候经常需要很多的关联,因为单独⼀个表内不存在冗余和重复数据。这导致多次的关联,增加查询代价 可能 使⼀些索引策略⽆效。因为范式化将列存放在不同的表中,⽽这些列在⼀个表中本可以属于同⼀个索引。 1.2 反范式(逆范式)打破范式 定义,为什么要有反范式? 等级越⾼的范式设计出来的表越多,可能会增加查询所需时间。当我们的业务所涉及 的表⾮常多,经常会有多表连接,并且我们对表的操作要时间上要尽量的快,这时可以考虑我们使⽤"反范式"。 反范式⽤空间来换取时 间,引⼊受控的数据冗余,当查询时可以减少或者是避免表之间的关联 优点 可以避免关联,因为所有的数据⼏乎都可以在⼀张表上显⽰; 可以设计有效的索引; 缺点 提⾼了对数据冗余的维护,为了保证数据的⼀ 致性(可以⽤触发器来解决这个问题,某个表被修改后触发另⼀个表的更新) 反范式的7种模式,并举例。 ⼀对⼀:合并表 如果双⽅都是完全参与,那么某个表直接可以作为另⼀张表的属性直接合并。若有⼀⽅是 部分参与,把完全参与的并⼊部分参与的会出现空值,将部分参与并⼊完全参与的可以。若双⽅都是部分参与,⼀定会出现空值,这样就很 难确定主键 例⼦:员⼯表(员⼯编号,姓名,住址,薪⽔,科室编号) 亲属表(员⼯编号,亲属姓名,亲属电话),其中亲属表中只登记⼀位亲 属,如果要查询张三的亲属的电话,就要连接两张表,所以将两张表合并以减少连接。 ⼀对多:复制⾮关键字以减少连接 适⽤条件:当两张表连接时,最主要的事务都与某个⾮键值相关 例⼦:两个表(⽤户id,好友id)和(⽤户 id,⽤户昵称,⽤户邮箱,联系电话)符合3NF,如果需查询某个⽤户的好友(昵称)名单,此时需对2个表进⾏连接查询,可以把第⼀个表修改成(⽤户 id,好友id,好友昵称)这样只需要查询第⼀个表就可获取所有好友昵称. ⼀对多:复制关键字以减少连接 复制⼀对多关系中的外键,使需要通过第三张表连接的两张表直接关连在⼀起 在⼀对多的关系中,将另 ⼀实体表的主键复制到多的实体表当外键,减少join表的数量 例⼦:每个部门租了⼀些录像带,每盘录像带是有租赁协议的。这样有三张表:部门(部门编号),租的录像(录像带编号,部门编号),租赁 协议,见下图关系表 \ 多对多:关系⾥⾯复制值以减少连接 引⼊⼀张新表,多对多被拆成两个⼀对多(满⾜3NF)。 假设要查询,演员演了哪些电影,为了避免 三张表连接,就在role表中复制电影名称以减少连接 \ 引⼊重复组 引⼊重复组,重复组⼀般不超过10个 (对于⼀个多值属性,如果多值的数量不多⼩于等于10) 例⼦:customer 可能有多个 address。⼀般是customer⼀张表,address⼀张表,address中存储它对应的customer的ID那么每次付款的时候,需要再查找address 表来得到customer的所有地址。可以在customer后⾯加addr1、addr2字段,但不知道要加多少个,不好。较为常见的做法是,在 customer表中引⼊⼀条addr字段,放最常⽤地址,address表中存储所有地址。 提取表 extract table的表中内容和原表可能都相同,只是组织结构不同,有可能⼀个是为了查询⽽建的表(将经常被查询的数据提前计算 出来存⼊该表,会有⼤量冗余,但是提⾼效率),另⼀个是为了update的,⼀定程度上实现了读写分离。 ⽐如有学⽣表,宿舍表,为了查学 ⽣的住宿费,需要连接两表。此时可以提取出⼀张⽤于查询的表,其中包含了学⽣姓名,住宿费⽤以及其他字段,⽅便查询。 使⽤分区表 可以将表分解成更少的分区。 ⽔平分区:跨多个(较⼩)的表分配记录。这种形式分区是对表的⾏进⾏分区,通过这样的⽅式 不同分组⾥⾯的物理列分割的数据集得以组合,从⽽进⾏个体分割(单分区)或集体分割(1个或多个分区)。所有在表中定义的列在每个数据集 中都能找到,所以表的特性依然得以保持。 例⼦:⼀个包含⼗年发票记录的表可以被分区为⼗个不同的分区,每个分区包含的是其中⼀年 的记录。 垂直分区:跨多个(较⼩)表分布列。 PK复制,以便重建。
数据库系统管理制度 1. 总 则 第一条 为加强我司数据库管理,保障数据库正常、有效运行,确保数据库安全,使数据库能更 好地服务于生产,特制定本管理制度。 第二条 公司数据库管理员负责评级数据库的日常维护和运行管理。 第三条 公司IT总监负责对数据库使用者进行权限审批。 2. 数据库系统的修改和维护 第一条 数据库系统的变更,指数据库硬件或参数调整等;涉及到停机时,应征求业务部门对系 统使用,并根据实际情况由部门负责人确定并发布停机时间后,在停机时间范围内完成 相关操作,同时数据库管理人员应该评估风险后,再操作,并有相应的应急处理方案。 第二条 数据库业务数据的变更修改,指开发部门或业务部门关于数据库业务表数据的变更修改 ;应由开发部门提供具体表和字段,或要求开发部门提供脚本,由数据库管理人员执行 操作;并且修改前先进行数据备份。 第三条 数据库系统的监控,应实时了解数据库运行状态,确保数据库可用状态。 第四条 数据库系统的健康检查,应定期对数据库的响应指标巡检。并给出巡检的报告。健康检 查应当定期执行,每周进行一次健康检查。 第五条 数据的备份恢复,备份的主要目的就是为了恢复,所以在一段时期应当数据库进行恢复 演练,以防止意外发生时数据不能恢复而造成损失。恢复演练应尽量避开数据库生产环 境,以免影响生产的运行。恢复演练应当定期执行,以季度为单位,每季度至少演练一 次。 第六条 数据库故障处理,数据库系统中常见的四种故障主要有事务内部的故障、系统故障、介 质故障以及计算机病毒故障,对应于每种故障都有不同的解决方法。。 3. 数据库的安全管理 第一条 数据库管理员负责数据库系统的安全管理,保证安全管理软件的及时升级。 第二条 数据库应每周至少备份一次,数据库管理员应在保证数据安全和保密的情况下,采取适 当方式保存备份文件,保证数据库出现异常时能快速恢复,避免或尽量减少数据丢失。 数据库系统管理制度的实现方法 1. 数据库变更 数据库系统变更 变更管理流程分为:需求调研、需求评审、变更申请、变更方案、变更审批、执行 变更六个步骤: 数据库的调整应针对问题先进行调研评审,再定制变更方案及实施计划回退方案后再 进行实施。 1.需求调研:由数据库管理员根据数据库运行监控过程中发现的问题进行调研,确 定问题解决方案。 2.需求评审:由数据库管理员组织调研,由部门经理确定后,完成对变更需求的分 析评审。 3.变更申请:由数据库管理员根据变更需求,给出初步的方案建议。 4.变更方案:由部门负责人确定变更评审小组成员,评审中修改并确定变更的实施 方案及应急回退方案。 5.变更审批:小型变更由部门负责人审批,大、中型变更由部门审批。 6.执行变更:由数据库管理运维团队执行系统变更的具体实现工作。 业务表数据变更 业务数据变更应由需求部门邮件申请,并由部门负责人进行审批后进行变更。 1.变更申请:由需求部门提出申请。 2.变更审批:由需求部门及运维部门负责人审批。 3.变更执行:确定变更需求后由数据库管理员进行相关操作。 2. 数据库系统的监控,应实时了解数据库运行状态。确保数据库可用状态。 1. 第三方数据库监控软件。 ZABBIX,APM 2. 数据库监控软件。 Oracle Enterprise Manager 3. 手动获取数据库报表 StatsPack、Automatic Workload Repository、Active Session History 3. 数据库系统的健康检查,应定期对数据库的响应指标巡检。 1. 数据库管理员每天定时巡检数据库服务器,并填写记录表(见附录一)。 2. TOP前5执行慢的SQL语句进行优化跟踪,并邮件提交给部门经理,抄送IT总监。 第四条 核心数据的日常备份管理。 1)数据库应每周至少备份一次,数据库管理员应在保证数据安全和保密的情况下 ,采取适当方式保存备份文件,保证数据库出现异常时能快速恢复,避免或尽量 减少数据丢失。 2)为了节省磁盘空间,大型业务库使用增量备份,周日全备,周一到六为增量备 份。 3)协助开发和业务部门做数据变更时,需提前备份好数据,再执行变更步骤。 第五条 数据库故障处理 一、常见的四种故障 (1)事务内部的故障:事务内部故障可分为预期的和非预期的,其中大部分的故障 都是非预期的。预期的事务内部故障是指可以通过事务程序本身发现的事务内部 故障;非预期的事务内部故障是不能由事务程序处理的,如运算溢出故障、并发事 务死锁故障、违反了某些完整性限制而导致的故障等。 (2)系统故障:系统故障也称为软故障,是指数据库在运行过程中,由于硬件故障 、数据库软件及操作系统的漏洞、突然停电灯情况,导致系统停止运转,所有正 在运行的事务以非正常方式终止,需要系统重新启动的一类故障。这类事务不破
数据库小型MIS开发中,常遇到的问题有以下几个: 1. 数据库性能问题:当数据量较大时,查询和插入数据的速度会变慢,导致系统变得不稳定。 解决方法:可以通过优化数据库结构、使用索引、缓存等方式来提高数据库性能。另外,可以使用一些性能监控工具来定位问题所在,如MySQL的slow query日志、Percona Toolkit等。 2. 数据库安全问题:数据库中存储着大量的敏感数据,如用户信息、密码等,如果不加以保护,可能会引发数据泄露、数据丢失等安全问题。 解决方法:可以通过加密存储、访问控制、备份恢复等方式来保护数据库的安全。同时,还需要定期对数据库进行漏洞扫描、安全审计等操作,及时发现并修复安全漏洞。 3. 数据库设计问题:数据库结构设计不合理,可能会导致数据冗余、数据不一致等问题,影响系统的正常运行。 解决方法:需要在设计数据库结构时,遵循一些设计原则,如避免冗余、保证数据一致性、选择合适的数据类型等。在实际操作过程中,需要不断进行优化和调整,保证数据库的正常运行。 4. 数据库扩展问题:随着业务的扩大,数据库的数据量也会不断增加,需要及时扩展数据库的能力,否则会影响系统的稳定性。 解决方法:可以采用数据分片、读写分离、集群等方式来扩展数据库的能力。同时,还需要考虑数据迁移等问题,保证扩展过程的顺利进行。 以上是常见数据库小型MIS开发中遇到的问题及相对应解决方法

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值