数据库的优化方案

it2025-12-23  7

一、数据库设计优化

1、选取最合适的字段属性

选择最合适的数据类型包括尽量去使用比较小的数据类型 和 使用简单的数据类型 , 字段的定义尽量非空等。 MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。 例如,在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任务了。相同的,如果TINYINT能满足我们的业务需求,那我们没有必要使用INT或者BIGINT。

2、表设计优化

一般情况下表的设计是应该严格遵循三大范式的。 数据量过大 , 表结构过于复杂的情况下 , 就要考虑进行表的垂直或水平拆分.

3、数据库索引

索引是提高数据库性能最常用的方法,它可以大大提高数据库查询的效率,尤其是在查询语句当中包含有MAX(),MIN()和ORDER BY这些函数和语句的时候,性能提高更为明显。 通常情况,索引应建立在那些将用于JOIN连接,WHERE判断和ORDERBY排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引。如用户表中的性别字段就不适合创建索引(因为性别只有男或女两个值),在这样的字段上创建索引不仅不会提高数据库查询的效率,反而有可能降低数据库的性能。 索引并不是越多越好,索引固然可以提高相应的SELECT的效率,但同时也降低了INSERT及UPDATE 的效率,因为INSERT或UPDATE 时有会更新索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。

4、分析表,检查表,优化表

分析表主要是分析表中关键字的分布,使用 ANALYZE 关键字,如ANALYZE TABLE user; Op:表示执行的操作。 Msg_type:信息类型,有status,info,note,warning,error。 Msg_text:显示信息。

检查表主要是检查表中是否存在错误,使用 CHECK关键字,如CHECK TABLE user [option]; option 只对MyISAM有效,共五个参数值: QUICK:不扫描行,不检查错误的连接。 FAST:只检查没有正确关闭的表。 CHANGED:只检查上次检查后被更改的表和没被正确关闭的表。 MEDIUM:扫描行,以验证被删除的连接是有效的,也可以计算各行关键字校验和。 EXTENDED:最全面的的检查,对每行关键字全面查找。

优化表主要是消除删除或更新造成的表空间浪费,使用OPTIMIZE关键字,如OPTIMIZE [LOCAL|NO_WRITE_TO_BINLOG] TABLE user; LOCAL|NO_WRITE_TO_BINLOG都是表示不写入日志.,优化表只对VARCHAR,BLOB和TEXT有效,通过OPTIMIZE TABLE语句可以消除文件碎片,在执行过程中会加上只读锁。

二、硬件优化

1、 硬件三件套

1、配置多核心和频率高的cpu,多核心可以执行多个线程. 2、配置大内存,提高内存,即可提高缓存区容量,因此能减少磁盘I/O时间,从而提高响应速度. 3、配置高速磁盘或合理分布磁盘:高速磁盘提高I/O,分布磁盘能提高并行操作的能力.

2、优化数据库参数

优化数据库参数可以提高资源利用率,从而提高MySQL服务器性能.MySQL服务的配置参数都在my.cnf或my.ini,下面列出性能影响较大的几个参数。 key_buffer_size:索引缓冲区大小 table_cache:能同时打开表的个数 query_cache_size和query_cache_type:前者是查询缓冲区大小,后者是前面参数的开关,0表示不使用缓冲区,1表示使用缓冲区,但可以在查询中使用SQL_NO_CACHE表示不要使用缓冲区,2表示在查询中明确指出使用缓冲区才用缓冲区,即SQL_CACHE. sort_buffer_size:排序缓冲区。

3、分库分表

因为数据库压力过大,首先一个问题就是高峰期系统性能可能会降低,因为数据库负载过高对性能会有影响。另外一个,压力过大把你的数据库给搞挂了怎么办? 所以此时你必须得对系统做分库分表 + 读写分离,也就是把一个库拆分为多个库,部署在多个数据库服务上,这时作为主库承载写入请求。然后每个主库都挂载至少一个从库,由从库来承载读请求。

4、缓存集群

如果用户量越来越大,此时你可以不停的加机器,比如说系统层面不停加机器,就可以承载更高的并发请求。然后数据库层面如果写入并发越来越高,就扩容加数据库服务器,通过分库分表是可以支持扩容机器的,如果数据库层面的读并发越来越高,就扩容加更多的从库。 但是这里有一个很大的问题:数据库其实本身不是用来承载高并发请求的,所以通常来说,数据库单机每秒承载的并发就在几千的数量级,而且数据库使用的机器都是比较高配置,比较昂贵的机器,成本很高。如果你就是简单的不停的加机器,其实是不对的。所以在高并发架构里通常都有缓存这个环节,缓存系统的设计就是为了承载高并发而生。 所以单机承载的并发量都在每秒几万,甚至每秒数十万,对高并发的承载能力比数据库系统要高出一到两个数量级。所以你完全可以根据系统的业务特性,对那种写少读多的请求,引入缓存集群。 具体来说,就是在写数据库的时候同时写一份数据到缓存集群里,然后用缓存集群来承载大部分的读请求。这样的话,通过缓存集群,就可以用更少的机器资源承载更高的并发。

三、SQL语句的优化

1 尽量避免使用子查询,可以使用JOIN链接查询替代

常用的关系型数据库都支持子查询,子查询使用SELECT语句创建一个查询结果,然后把这个结果作为一张临时表用在另一个查询中。使用子查询可以一次完成多步SQL操作,也可以避免事务或者表锁死,且写起来比较容易。但是使用子查询MYSQL会在内存中创建一张临时表供外层查询使用,所以会降低查询的效率。这时候我们可以使用JOIN链接操作来替代子查询。

2 UNION All能满足业务需求不要使用UNION

如果我们需要将两个或者多个SELECT语句的结果作为合并为一个整体显示出来,我们可以用UNION或者UNION ALL关键字。UNION(联合)和UNION ALL的作用是将多个结果合并在一起显示出来。 两者的区别是: UNION会自动压缩多个结果集合中的重复结果,而UNION ALL 则将所有的结果全部显示出来,不管是不是重复。所以当UNION ALL能满足业务需求的时候,尽量使用UNION ALL而不用UNION。

3 WHERE子句尽量避免使用!=或<>操作符

在WHERE子句中使用!=或<>操作符,查询条件不会使用索引,会进行全表查询。即影响查询效率。

4 WHERE子句使用OR的优化

通常情况我们可以使用UNION ALL或UNION的方式替换OR会得到更好的效果。因为WHERE子句中使用了OR,将不会使用索引。 例如:SELECT ID FROM TABLENAME WHERE ID = 1 OR ID = 2 ; 优化:SELECT ID FROM TABLENAME WHERE ID = 1 UNION ALL SELECT ID FROM TABLENAME WHERE ID = 2 ;

5 WHERE子句使用IN或NOT IN优化

IN和NOT IN也要慎用,否则可能会导致全表扫描。 可用以下方案替换: 方案一:BETWEEN AND替换IN 例如:SELECT ID FROM TABLENAME WHERE ID IN(1,2,3); 优化:SELECT ID FROM TABLENAME WHERE ID BETWEEN 1 AND 3; 方案二:EXISTS替换IN 例如:SELECT ID FROM TABLEA WHERE ID IN (SELECT ID FROM TABLEB ) 优化:SELECT ID FROM TABLEA AS A WHERE ID EXISTS(SELECT 1 FROM TABLEB AS A WHERE B.ID = A.ID) 方案三:LEFT JOIN替换IN 例如:SELECT ID FROM TABLEA WHERE ID IN(SELECT ID FROM TABLEB) 优化:SELECT ID FROM TABLEA AS A LEFT JOIN TABLEB AS B ON A.ID = B.ID

6 WHERE子句中使用IS NULL或IS NOT NULL优化

在WHERE子句中使用IS NULL或IS NOT NULL判断,索引将被放弃使用,会进行全表查询。 例如:SELECT ID FROM TABLENAME WHERE AGE IS NULL 优化成AGE上设置默认值0,确保表中AGE没有NULL值, 优化:SELECT ID FROM TABLENAME WHERE AGE = 0

7 LIKE语句优化

一般情况下不建议使用LIKE操作,特别是数据量较大的表。 例如:SELECT NAME FROM TABLEA WHERE NAME LIKE ‘%张%’;不会使用索引 优化:SELECT NAME FROM TABLEA WHERE NAME LIKE ‘张%’;会使用索引

8 WHERE子句中避免对字段进行表达式操作

尽量不要在WHERE子句中的=左边进行函数、算数运算或其他表达式运算,否则系统将无法正确使用索引。 例如:SELECT ID FROM TABLENAME WHERE ID/2 = 50 优化:SELECT ID FROM TABLENAME WHERE ID = 50*2 例如:SELECT ID FROM TABLENAME WHERE substring(name,1,2) = ‘欧阳’ 优化:SELECT ID FROM TABLENAME WHERE LIKE ‘欧阳%’

9 一定不要用SELECT * FROM TABLENAME

在定义SQL语句字段列表替换"*",尽量避免返回无用的时候,要用具体的的字段。

10 LIMIT分页优化

MYSQL数据库实现分页一般都会使用LIMIT,但是当偏移量比较大时,LIMIT的效率会非常低,导致查询超时。 如下SQL: SELECT ID FROM TABLENAME LIMIT 1000,10 执行很快 SELECT ID FROM TABLENAME LIMIT 100000,10 执行很慢 优化方法: 方法一:SELECT ID FROM TABLENAME ORDER BY ID LIMIT 100000,10; 执行很快(因为用了ID主键做索引) 上述方法一是我们最常用的,但是如果表中的数据是千万级别的,即便使用方法一,查询速度可能还是比较慢,这时候我们可以把上一页ID的最大值作为查询条件来实现分页,如方法二。 方法二:SELECT ID FROM TABLENAME WHERE id > @MAXID limit 10; @MAXID的值是上一页查询结果中ID的最大值。

11 EXISTS代替IN

SELECT ID FROM TABLEA WHERE ID IN (SELECT ID FROM TABLEB) 如上SQL,IN执行的时候是在内存中遍历比较,IN(SELECT ID FROM TABLEB)括号中语句只执行一次,把TABLEB表中的所有ID字段缓存起来,之后检查TABLEA表的ID是否与TABLEB表中的ID相等,如果ID相等则将TABLEA表中的记录加入到结果集中,直到遍历完TABLEA表的所有记录。 SELECT ID FROM TABLEA WHERE ID EXISTS(SELECT ID FROM TABLEA.ID= TABLEB.ID) 如上SQL,EXISTS查询是遍历TABLEA中的数据,TABLEA中的每一条数据与TABLEB连表查询,如果有返回结果,则把该记录添加到结果集中,所以当TABLEB的数据量远大于TANLEA时,EXISTS效率大大优于IN.当TABLEA表数据与TABLEB表数据一样大时,IN与EXISTS效率差不多。

最新回复(0)