面试 MySQL
Kiml Lv5
  • 前言
    ❗表示必掌握,❔表示基本不会问

  • 更新

1
24-06-19 初始记录

引擎

❗MySQL 有哪些常用的存储引擎?它们的区别是什么?怎么选择?

常见的存储引擎有:InnoDB(最常用),MyISAM(次常用),MEMORY(最次)。

InnoDB:是最常用的存储引擎,采用 B+Tree 索引,最适用于需要增删改的表,支持事务,支持并发控制(MVCC),InnoDB 是现在 MySQL 默认的存储引擎。分库分表、读写分离、主备切换(技术方案很成熟)

MyISAM:是 5.5 版本之前默认的存储引擎,采用 B-Tree 索引,访问速度快,但不支持事务和外键,所以只适用于查询需求的表。

  • 基于 MyISAM 的报表系统,hadoop 前一天把报表数据查询然后保存到 MySQL,后面只要查询。MySQL 的单表建议控制数据量在几百万,一般是 500w。后面这种报表数据量会很大,sql 也很复杂,不适合用 MySQL。
    MEMORY:基于内存存储,采用的是哈希索引,速度快,但存储量受内存大小限制,而且安全性低,一但断电,内存就会清空,一般不使用这种存储引擎。

为什么 MyISAM 访问查询速度快?

MyISAM 相比于 InnoDB,不需要添加隐藏值来实现 MVCC 保证高并发,也不需要事务锁,MyISAM 通过舍弃并发控制和事务保证来提升查询速度。

此外 MyISAM 是非聚集索引,且叶节点始终是地址直接指向数据文件,而 InnoDB 可能出现回表的情况,所以相对来说,MyISAM 的访问速度比 InnoDB 快。

❗MyISAM 的底层数据结构是什么?

MyIsam 则是非聚集型索引,底层是 BTree 数据结构,myisam 存储会有三个文件,一个是表文件,一个是索引文件,另外一个是数据文件 ,无论是否是主键索引,索引文件最终都是以地址指向数据文件中的表数据。

❗InnoDB 的数据结构又是什么?

要求必须要有主键,默认内置根据主键建立一个索引,叫聚簇索引。如果对于某个非主键的字段创建索引,最后那个叶子节点的值就是主键的值。可以用主键的值到聚簇索引里根据主键值再次找到数据。

InnoDB 的数据“类型”是 BTree,底层数据“结构”是 B+Tree,B+Tree 是 BTree 数据结构的变种,是绝对平衡树,所有叶节点在同一高度。

B+Tree 是一个多路平衡二叉树,也就是说它不只有左右两个分支,而是可以一个节点下有很多分支。

B+Tree 除叶子节点外,都只存储 key 值,所有的数据都存储在叶子节点上,且所有的叶子节点间都连有指针,使所有数据成为有序序列,可以用来做全表顺序描扫或范围查询

B+Tree 的数据检索规则为左闭合区间,这样可以实现向右添加。

为什么使用 B+Tree 而不使用普通的 Tree?

普通的 tree 最多只能有 2 路,而 B+Tree 则是多路数据结构,而且在数据量庞大的情况下,Tree 可能高度会很高,增加 IO 次数,降低了性能,而 B+Tree,一页大小为 16KB,单纯用来存索引的话,假设索引为 int 类型,即 4 个字节,不考虑子节点引用的情况下,每个节点可以存 2000 个关键字,即 2000 路,能够搜索的关键字个数远远的大于普通的二叉树(有只两路)。

为什么使用 B+Tree 而不使用 B-Tree?

数据库在查询数据时,以页的方式将磁盘数据加载到数据建库内存,默认每页数据大小是 16kb,为了提升查询效率,需要减少磁盘的 IO 次数。

B-Tree 数据结构,每个节点中直接保存数据,这样的话 16kb 的页包含的节点数量就会比较少,所以在查询时,需要加载更多的页。

B+Tree 数据结构,只有叶子节点包含数据,其他非叶子节点只包含索引列的值,16kb 的页能包含的节点数量就大增,相对于 B-Tree 而言,能够更少的加载页。

为什么不用 Hash 或者红黑数?

业务查询一般可能不是查询一条,而是查询多条。

hash 索引查询单条确实比较快,但是他是无序的,查询多条或者排序的话性能就比较低了,并且在内存资源紧张的情况下,树索引可以分批装入内存进行计算。

红黑树因为大数据存储下,树的高度很高,每个节点都有数,这样可能会导致多次 IO,查询效率比较低,红黑树并不适合庞大数据搜索。

而 B+ 树可以一次性装入更多的叶子节点到内存,并且树的高度可以控制到很低,叶子节点存储数据并且形成链表可以避免跨层查询,这种“矮胖”的数据结构更适合于庞大数据搜索。

B+Tree 叶子节点中的数据怎么有序?

索引是表数据之外的一种数据结构,数据结构即是存储方式,也就是在表数据之外还会生成一张专门搜索而用的表,新加入的数据会找到对应子节点所在的范围,按照升序从小到大排列。

所以 InnoDB 的批量插入效率是较低的,如果需要批量插入,就需要进行一定的数据库优化。

怎么优化数据库来提升数据批量插入的效率呢?
  1. 尽量保持数据有序。减少数据插入时对索引的维护成本。

  2. 一次插入多条数据 (不宜过多)。减少日志,降低日志刷新磁盘的频率。减少 sql 解析次数。

  3. 尽量保证主键足够小,且表上没有多余的索引。

  4. 如果允许,考虑临时关闭二进制日志。

  5. 确保 innodb_buffer_pool_size,innodb_log_buffer_size,max_allowed_packet 足够大。注:

  • innodb_buffer_pool_size:InnoDB 最重要的参数,缓存 innodb 表的素引、数据和插入数据的缓冲。

  • innodb_log_buffer_size:日志缓冲区,大量事务时可以将默认 3M 设为 16M。

  • max_allowed_packet:网络包大小,避免出现较大的网络包错误。

为什么 InnoDB 能支持事务特性?

redo log 重做日志用来保证事务的持久性:当 commit 时,必须先将事务的所有日志写到重做日志文件进行持久化,直到 commit 结束日志才算完成。

undo log 回滚日志保证事务的原子性:事务完成前,操作并没有真正执行,而记录在日志中,undo log 会记录之前事务对应的行数据,回滚时,会根据日志进行反向操作,对中间记录的每一步操作进行逻辑删除,从而保证原子性。

undo log + redo log 保证事务的一致性:操作过程中由 redo log 保证持久化,一但过程中出错,就由 undo log 回滚。

锁(共享、排他)用来保证事务的隔离性:事务的隔离性的实现原理就是锁,InnoDB 主要有 2 种锁,行级锁跟意向锁。(具体见 MySQL 锁回答)

什么是 InnoDB 回表?

即第一次 B+tree 的叶节点上没能直接获取数据,还需要通过叶节点上的数据做为新的索引在另一张表上进行第二次 B+tree 的扫描,直到获取最终想要的数据。

那有没有好的解决办法?

覆盖索引(就是联合索引)。解决回表问题,只需要在一棵索引树上就能获取 SQL 所需的所有数据,无需回表,速度更快。

覆盖索引的实现方法是,将需要查询的数据与主键共同设为索引,即联合索引,索引扫描遵循最左匹配原则,通过一次扫描 B+tree 即可查询到相应的结果,实现覆盖索引。

所以创建索引的时候,尽可能创建“覆盖索引”,减少回表操作,提升搜索性能。

❗什么是最左匹配原则?

联合索引进扫 B+tree 扫描时,会以最左边的索引条件起点优先,任何连续的索引都能匹配的上。比如有一个联合索引为(name, price, address),那么它的匹配索引有三个,按照优先级分别是:name、name+price、name+price+address(注:必须是从最左开始,且连续)

  1. 全列匹配,可以使用

  2. 最左前缀匹配。最左边,一列、两列、三列匹配都行

  3. 最左匹配原则,但是中间某个值没匹配。会匹配的部分走索引,然后后面的值根据过滤出来的值,再匹配。(线上常用,效果还好

  4. 没有最左前缀匹配。比如直接从 price 开始匹配,就不会走索引

  5. 前缀匹配。like 操作只有 XX% 才会进行匹配

  6. 范围列表查询。只有范围字段可以查询。

  7. 包含函数。使用函数的字段不会用上索引。

那是不是覆盖索引越多越好?

不是。在 B+Tree 数据类型在保证树的平衡的过程中,每次关键字的变化,都会导致结构发生很大的变化,这个过程是特别浪费时间的,所以创建索引一定要创建合适的索引,而不是把所有的字段都创建索引,创建冗余索引只会在对数据进行新增,删除,修改时增加性能消耗。

百万级别或以上的数据如何删除?

索引文件是单独存在的文件,对存储表的操作都会连带到索引文件上,为了提高删除速度,可以优先删除索引(百万级约 3 分钟),然后优先删除无用数据,最后对需要保留的数据重新创建索引。这样就算需要回滚,也比全部回滚速度更快。

事务

❗你能说说事务的几个特性是啥?有哪几种隔离级别?

——ACID(只有 InnoDB 才支持事务)。

  1. 原子性(undo log 回滚保证事务的原子性):指同一对的事务操作,要么全成功,要么全失败,操作失败不能对数据库有影响。

  2. 一致性(undo log+redo log 保证事务一致性):事务操作之后,数据库内的数据总量保持一致。(能量守恒)

  3. 隔离性(锁保证事务隔离性):相同的表,不同事务之间不能互相干扰。

  4. 持久性(redo log 重做日志用来保证事务持久性):事务一旦被提交,就需要在数据永久化存储,即便故障也不会丢失提交事务的操作。

4 种。

  1. 读未提交(RU:read uncommitted):读到了别的事务没有提交的数据。可能存在【脏读 + 不可重复读 + 幻读】的问题。

  2. 读已提交(RC:read committed):可能存在【不可重复读 + 幻读】问题。

  3. 可重复读(RR:repeatable read):可能存在【幻读】问题。【MySQL 默认】

  4. 串行化(serializable):无以上问题,但效率低,一般在分布式事务的情况下用该级别。

❗什么是脏读?幻读?不可重复读?

脏读 (Drity Read):是指在一个事务处理过程中读取了另一个未提交的事务中的数据 , 导致两次查询结果不一致。

可重复读 (Non-repeatable read):事务开启后关闭前,多次读取同一条记录,结果却不能保证一致,所以叫不可重复读。主要问题不在同一个数据库的问题,而在不同的服务器,不同数据库时会出现的问题,因为两台电脑之间要保证数据相同,是需要时间进行复制的,从表在复制主表的过程中,很可能因为修改数据过快而导致复制到错误数据。

幻读 (Phantom Read):select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入。或不存在执行 delete 删除,却发现删除成功。

❗事务的隔离级别是怎么解决以上三种问题的?

解决脏读:修改时加排他锁(写锁),直到事务提交后才释放,读取时加共享锁(读锁),其他事务只能读取,不能再有更新操作。防止脏读。

解决不可重复读:innodb 引擎采用了 MVCC(多版本并发控制)来解决不可重复读问题。mvcc 是利用在每条数据后面加了隐藏的两列(创建版本号和删除版本号)。当执行查询的时,当前查询版本号>= 创建版本号 并且 >删除版本号,MVCC 可以在大多数情况下代替行级锁,使用 MVCC,能降低其系统开销。

解决幻读:采用 next-key 锁解决幻读问题,next-key 锁包含两部分:记录锁(行锁)+ 间隙锁,就是在索引和索引之间上面加锁。

❗InnoDB 是怎么做到并发控制的?MVCC

事务 id,在 mysql 内部是全局唯一递增的。
当一个事务内查询的时候,mysql 只会查询创建事务 id <= 当前事务 id 的数据且当前事务 id < 删除事务 id。

通过多版本并发控制(MVCC)实现。

指的是一种提高并发的技术。最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提高了 InnoDB 的并发度。

在内部实现中,InnoDB 通过 undo log 保存每条数据的多个版本,并且能够找回数据历史版本提供给用户读,每个事务读到的数据版本可能是不一样的。在同一个事务中,用户只能看到该事务创建快照之前已经提交的修改和该事务本身做的修改。

MVCC 在 Read Committed 和 Repeatable Read 两个隔离级别下工作。

MySQL 的 InnoDB 存储引擎默认事务隔离级别是 RR(可重复读),是通过 " 行级锁 +MVCC" 一起实现的,正常读的时候不加锁,写的时候加锁。而 MVCC 的实现依赖:隐藏字段、Read View、Undo log。

隐藏字段是 InnoDb 存储引擎在每行数据的后面自动加上的字段,包括事务 id 字段(最重要,每次新增或修改会修改 id 号,删除的话是逻辑删除,当做更新处理,会多一个更新记录)和回滚指针字段(指向 undo log 的上一次事务对应的数据)。

ReadView 就是快照,主要是用来做可见性判断的,保证其它事务对该事务不可见,里面包括该事务的最大事务 ID,最小事务 ID 以及未提交的事务 ID 等。

Undo log 中存储的是老版本数据,用于回滚。如当一个事务需要读取记录行时,如果当前记录行不可见,可以顺着 undo log 链找到满足其可见性条件的记录行版本。

MySQL 数据库锁的实现原理

在关系型数据库中,可以按照锁的粒度把数据库锁分为行级锁 (INNODB 引擎)、表级锁 (MYISAM 引擎) 和页级锁 (BDB 引擎 )。

MyISAM 和 InnoDB 存储引擎使用的锁:

  • MyISAM 采用表级锁。(一个例子:hadoop 处理数据出了问题,上午 10 点还在插入大量数据,导致锁表。页面报错 504

  • InnoDB 支持行级锁和表级锁,默认为行级锁。(innoDB 在执行增删改时会自动给行加排他锁

行级锁:行级锁是 MySQL 中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁(读锁)排他锁(写锁)

共享锁和排他锁
共享锁: 又叫做读锁。 当用户要进行数据的读取时,对数据加上共享锁。共享锁可以同时加上多个。

排他锁: 又叫做写锁。 当用户要进行数据的写入时,对数据加上排他锁。排他锁只可以加一个,他和其他的排他锁,共享锁都相斥。

悲观锁和乐观锁
数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。

悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。在查询完数据的时候就把事务锁起来,直到提交事务。实现方式:使用数据库中的锁机制。

乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。在修改数据的时候把事务锁起来,通过 version 的方式来进行锁定。实现方式:乐观锁一般会使用版本号机制或 CAS 算法实现

两种锁的使用场景(❗线上少用悲观锁)

从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。

但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行 retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

❗如果死锁了咋办?

死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。

表级锁不会产生死锁,行级锁和页级锁会产生死锁。

常见的解决死锁的方法:一般是看死锁的相关 log,然后解决出问题的 sql

  1. 如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。

  2. 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;

  3. 对于非常容易产生死锁的业务部分,可以尝试使用升级锁的颗粒度,通过表级锁定来减少死锁产生的概率;

如果业务处理不好可以用分布式事务锁或者使用乐观锁。

数据库优化

优化数据库

  1. 提升硬盘配置,换 SSD 固态硬盘,硬件直接决定了磁盘读写的快慢。

  2. 设计索引的时候,尽量保证主键足够小。

  3. 对数据库结构优化,索引可以适当冗余,可以减少多表联查。

  4. 尽量保证数据的有序性,提升批量插入的性能。

  5. 分库分表。

  6. 模糊查询和海量数据查询使用 ES:倒排索引,近实时查询。

  7. 数据迁移使用 XXL-Job:只保留当天数据,庞大的历史数据放到历史表。

  8. SQL 语句的选用:比如固定长度用 char 更快,比如用 not exist(子查询仍然可以用索引查找是否存在) 替代 not in(索引失效,因为需要遍历判断)性能更好。

❗SQL 优化

  1. 通过慢 SQL 日志查询

  • 需要手动开启慢日志开关:

1
2
3
4
5
6
-- 查看慢日志是否开启
show variables like 'slow_query_log';
-- 开启MySQL慢日志查询开关
set global slow_query_log = 1;
-- 设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
set global long_query_time = 2;
  • 查看慢日志记录的 sql

1
2
show global status like 'slow_queries'; -- 查看有多少慢查询 
show variables like '%slow_query%'; -- 获取慢日志目录
  1. 使用explain命令来查看语句的执行计划,它可以显示数据库引擎对于 SQL 语句的执行的详细情况,包含是否使用索引,使用什么索引,使用的索引的相关信息等,具体的判断参数如下:

id select_type table type possible_keys key key_len ref rows Extra

id :表示查询中各个子查询的执行顺序,id 值越大,优先级越高,越先被执行。

Select_type:每个子查询的查询类型。

  • SIMPLE:不包含任何子查询或 union(全链接)等查询。

  • PRIMARY:包含子查询的最外层查询显示为 PRIMARY。

  • SUBQUERY:在 select 或 where 字句中包含的查询。

  • DERIVED:from 字句中包含的查询。

  • UNION:出现在 union 后的查询语句中。

  • UNION RESULT:从 UNION 中获取结果集。

table:显示具体对应的表。

type(很重要):查看有没有走索引,及访问类型。

  • ALL 扫描全表数据

  • index 遍历索引

  • range 索引范围查找

  • index_subquery 在子查询中使用 ref

  • unique_subquery 在子查询中使用 eq_ref

  • ref_or_null 对 Null 进行索引的优化的 ref

  • fulltext 使用全文索引

  • ref 使用非唯一索引查找数据

  • eq_ref 在 join 查询中使用 PRIMARY KEY or UNIQUE NOT NULL 索引关联。

possible_keys :可能使用的索引,注意不一定会使用。查询涉及到的字段上若存在索引,则该索引将被列出来。当该列为 NULL 时就要考虑当前的 SQL 是否需要优化了。

key :显示 MySQL 在查询中实际使用的索引,若没有使用索引,显示为 NULL。

  • TIPS:查询中若使用了覆盖索引 (覆盖索引:索引的数据覆盖了需要查询的所有数据),则该索引仅出现在 key 列表中。

key_length:索引长度

ref:表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

rows:返回估算的结果集数目,并不是一个准确的值。

场景题

如果在实际工作中发现 MySQL 数据库 CPU 飙升到 500%,该怎么处理?

  1. 先用操作系统命令 top 命令查看情况;

  2. 如果是 mysql 造成的,就用 show processlist 语句看一下 MySQL 实例的连接情况,其中 state 列可以显示使用当前连接的 SQL 语句的状态,time 显示这个状态持续的时间,info 显示正在执行的语句;

  3. 找到可能有问题的 SQL 语句后,再通过 explain 执行计划看看它的索引情况。

  4. 针对具体情况做相应的优化。

❗在一个千万级的数据库查寻中,如何提高查询效率?分别说出在数据库设计、SQL 语句、java 等层面的解决方案。(一共三个方面 数据库设计方面,Sql 语句方面,java 方面)

  1. 数据库设计方面:

    1. 对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
    2. 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num is null 可以在 num 上设置默认值 0,确保表中 num 列没有 null 值,然后这样查询: select id from t where num=0
    3. 并不是所有索引对查询都有效,SQL 是根据表中数据来进行查询优化的,当索引列有大量数据重复时,查询可能不会去利用索引,如一表中有字段 sex,male、female 几乎各一半,那么即使在 sex 上建了索引也对查询效率起不了作用。
    4. 索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过 6 个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
    5. 应尽可能的避免更新索引数据列,因为索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新索引数据列,那么需要考虑是否应将该索引建为索引。
    6. 尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。 这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
    7. 尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
    8. 尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
    9. 避免频繁创建和删除临时表,以减少系统表资源的消耗。
    10. 临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
    11. 在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log,以提高速度;如果数据量不大,为了缓和系统表的资源,应先 create table,然后 insert。
    12. 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
  2. SQL 语句方面:

    1. 应尽量避免在 where 子句中使用 !=<> 操作符,否则将引擎放弃使用索引而进行全表扫描。
    2. 应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,select id from t where num=10 or num=20 可以这样查询: select id from t where num=10 union all select id from t where num=20
    3. in 和 not in 也要慎用,否则会导致全表扫描,如: select id from t where num in(1,2,3) 对于连续的数值,能用 between 就不要用 in 了: select id from t where num between 1 and 3
    4. 下面的查询也将导致全表扫描: select id from t where name like ‘%abc%’
    5. 如果在 where 子句中使用参数,也会导致全表扫描。因为 SQL 只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描: select id from t where num=@num。可以改为强制查询使用索引: select id from t with(index(索引名)) where num=@num
    6. 应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。select id from t where num/2=100 应改为: select id from t where num=100*2
    7. 应尽量避免在 where 子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如: select id from t where substring(name,1,3)=’abc’–name 以 abc 开头的 id select id from t where datediff(day,createdate,’2005-11-30′)=0–‘2005-11-30’ 生成的 id 应改为: select id from t where name like ‘abc%’ select id from t where createdate>=’2005-11-30′ and createdate<’2005-12-1′
    8. 不要在 where 子句中的 = 左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
    9. 很多时候用 exists 代替 in 是一个好的选择: select num from a where num in(select num from b)。 用下面的语句替换: select num from a where exists(select 1 from b where num=a.num)
    10. 任何地方都不要使用 select * from t ,用具体的字段列表代替 *,不要返回用不到的任何字段。
    11. 尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过 1 万行,那么就应该考虑改写。
    12. 尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
    13. 尽量避免大事务操作,提高系统并发能力。
  3. java 方面:

    1. 尽可能的少造对象。
    2. 合理摆正系统设计的位置。大量数据操作,和少量数据操作一定是分开的。大量的数据操作,肯定不是 ORM 框架搞定的。
    3. 使用 JDBC 链接数据库操作数据
    4. 控制好内存,让数据流起来,而不是全部读到内存再处理,而是边读取边处理;
    5. 合理利用内存,有的数据要缓存
 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
访客数 访问量