今天谈一谈SQL的优化分享知识,在DBA的角度来看,数据库的优化一直是DBA一项很重要的工作,作为任何一门技术来说都应该本着“要想打败他必须先要了解他”的原则,其实,任何数据库的SQL优化都离不开几方面的内容,大多皆大同小异。以下从几方面进行分享,不足之处还请多多指教,共同学习。
数据库性能优化法则归纳为5个层次:
从架构上来优化一般有会比较明显的效果,通常我们可以采用硬件升级、资源池化、分流、分片、缓存、Nosql的手段对架构进行优化,当然这些优化手段并不能盲目的使用,需要来针对不同的场景和公司和业务现状来选择不同的策略。
虽然硬件升级并算不上什么架构层面的优化,但是硬件升级的确是最简单而且效果最为立竿见影的方式,遇到性能问题我们第一步就应该考虑使用此方式,通过升级服务器的CPU、内存、磁盘来优化数据库性能通常是最快捷而且提升的性能又是整体性的,同时最省心的是硬件升级不会应用程序有任何影响,如果你的数据库服务器硬件配置还不高,然后又要求短期内的整体性的优化 ,那么请升级你的数据库硬件配置。
当然随着配置的越来越高,硬件升级的成本也会也来越高,升级带来的效益也会越来越低,所以通过硬件升级的方式会有一个临界点,当超过这个临界点,硬件上升级的成本会成指数级的上升,达到普通公司无法承受的程度。
应用和数据库交互频繁的建立连接是非常消耗性能的,所以数据库后来也衍生了很多连接池技术。而我们这里并不是想在这么多成熟的连接池基础上去重复造轮子,而是学习这种资源池的思维,让消耗资源性能的操作预先做好,等到需要需要的时候直接使用即可,池化技术不仅能够减少资源频繁创建销毁过程中的消耗,同时也可以通过池化技术来限制避免无节制的创建资源导致应用崩溃。
1、创建连接池之前我们首先要对连接池的大概情况有个规划,我们需要考虑连接池最多创建多少线程,然后刚创建的时候也许我们没必要一次性把所有连接都创建出来,而是跟随请求动态的递增,刚开始也许需要的连接很少,所以我们可以为连接池设置一个初始值。
2、当请求获取连接时,如果连接池有空间连接则直接返回一个可用的连接。
3、当请求获取连接时,如果连接池没有空闲的连接,则先看下连接池连接数量是否达到最大值,没有则创建一个连接返回。
4、如果空闲连接没了、线程池数量也已经达到最大值了,则看任务队列是否已满,如果任务队列还有未满则提交一个任务放到队列里面,在有限的超时时间内等待执行。
5、如果空闲连接没了、线程池数量也已经达到最大值了,然后任务队列也满了,那没办法就只能拒绝请求了。
如果是数据库连接池,那么会有连接超时问题,数据库对每个连接的有wait_timeout的限制,当数据库连接闲置多长时间后会主动关闭连接,而当用户使用这个已经关闭的连接会出现错误。
解决思路:
方案一:使用一个专门的线程专门用于检测连接的可用性,可以对检测的线程发送一条select 1查询指令 来检测连接是否可用,然后把断开的连接移出连接池。
方案二:在获取连接之前先检测连接的可用性,但这种方案会有额外的开销。
当数据库出现并发瓶颈时,我们首先可以考虑的是对请求进行分流,把请求分摊个多个节点。而读写分离的策略就是把流量分配给多个数据库,从而减轻单台数据库的压力,通过把写入的请求分配到主库,把读取的请求分发的从库,通过数据库主从的方式来分摊流量。读写分离依赖于数据库的主从机制,通过配置数据库主从复制把主库的数据同步到从库,主库主要处理数据变更请求,从库处理查询请求。这里说一个小插曲:之前面试滴滴公司的时候问到一个问题,题目是这样的“生产中一个核心系统数据库做了读写分离,不能影响业务的情况下主从不一致了如何解决?”,我的回答是:第一时间强制读主库,然后紧接着去恢复从库数据同步差集,通过master mysqlbinlog去找差集,后边具体不表了。
因为从库和主库之间进行数据同步需要经过一系列的过程,那么这个过程就必定会有一定的延时性,这就会造成一个问题,当我们一个地方往主库中写入数据后,另外一个地方在从库对这个数据进行查询,因为延时的问题,那么查询的时候很可能查询不到最新的数据。
解决方案:
1、即时返回: 如果需要得到最新的数据,那么可以在写入数据成功之后就直接返回最新的数据给应用程序。
2、数据冗余: 在写入数据库的同时也写入一份数据到缓存,应用优先从缓存查询数据。
3、从主库读取:实时性强的业务场景,选择直接从主库的数据中读取数据。
当数量越来越大时,数据备份的时间也加长,索引文件也会变大,当数据库无法缓存所有数据索引只能从磁盘读取数据的时候,我们查询数据的性能也必然会随之降低,那么当遇到这种因为数据量导致性能产生瓶颈的时候,我就可以采用数据分片的思路来解决问题。在数据库数据分片的方式就是进行分库分表,减少单个数据库和表的数据,来提升数据库的性能,数据库分片也是我们常说的分库分表,数据库分库分表通常以垂直拆分、水平拆分两种方式来进行。
垂直拆分通常是指通过业务的维度来切分数据,把不同业务维度的数据来切分到不通的数据库,来达到数据拆分的目的。通常垂直拆分会根据业务功能的模板的分类来对数据库进行垂直拆分,如果你的系统分为用户、订单、产品模快;那我们通常会把不同模快的数据库进行拆分。
水平拆分
水平拆分则是根据数据维度拆分,通常会先指定一个拆分规则,把特定规则的数据拆分到不同的数据库和表中。
拆分方式:
1、用ID 哈希方式拆分,对ID进行取余的方式决定数据存到哪个库或表。
2、按字段范围区间,比如时间、地区、属性,不同的时间区域数据存放到不同的数据表中。
因为数据拆分到了不同的数据库和表中,数据库的自增长ID就无法保证我们的ID唯一性了,所以我们需要自己来生成一个全局的唯一ID。
全局唯一ID的要求:最后生成的ID必须要保证的就是“全局唯一”性,然后因为日常检索的排序需求,这个ID最好能满足排序的需求,同时生成ID时可以自定义一些业务标识,方面我们直接通过ID就可以分析其所属业务场景和分类。
解决方案:
数据库生成:数据库生成唯一ID不用依赖其他组件,可有序递增,可增加业务标识性能不高。
Redis自增:redis生成的ID有序可自增,性能比数据库要高,可增加业务标识,但依赖Redis,性能偏低。
UUID:不依赖任何组件,性能高,但不具备有序性和业务含义,生成的ID由32 个 16 进制数字组成的字符串比较占用空间。
Snowflake :可生成有序递增的ID,也可增加业务标识,但依赖于系统的时间戳,一旦系统时间不准就有可能生成重复的 ID。
经过分库分表后就不能像以前一样直接查询某个表了,而是首先要根据其业务特性和数据规则定位到查询的数据在哪个表中,因为我们拆分数据的时候是根据某一个字段的规则进行拆分的,如果想要定位到数据的位置,那么我们每次查询的时候就必须带上分区键(分库分表依赖的字段)才行。
假如根据ID作为分库分表的字段. 那么每次查询都要带上ID,如果要通过名称查询数据,那么就需要通过名称先找到ID,再根据D查询数据,这样的话又会需要建立一个名称和ID的映射表才行。
经过分库分表后表会存在于不同的库中,所以我们就不能像之前一样通过Join去连表查询数据了。
解决方案:
1、冗余:可以冗余一些不太容易变化的数据避免使用Join查询,比如把哪些不太容易变更的表(比如配置信息)冗余一份到每个库中,也可以通过把需要join查询的列冗余一个字段到当前表中(比如过在订单表冗余一个用户姓名字段)。
2、代码筛选:先把多个表的数据查询出来然后在代码里再去做筛选。
拆分了数据之后同时也不能直接对表数据进行count了,因为数据都存在于不同的表,这样的话我们通常又会需要专门用一个表记录统计数据又或者放缓存里。
的一大杀器。通常我们可以在很多地方对数据进行缓存,从客户端缓存 到CDN缓存、代理缓存、
我们的运营系统通常需要查询的是某个业务块的所有数据,分页也是在所有数据的基础上进行的,而经过分库分表后数据被分布在不同的库和表中,这样我们就无法直接对数据进行分页了。
解决方案:
1、通常我们会通过中间键完成,中间键先把一定的数据查询到缓存里,然后在缓存里进行逻辑分页。但这种方式通常来说性能不佳,页数越靠后性能就会越慢,因为需要查询和扫描的数据越到后面越多了。
2、冗余一份全量数据,比如说把全量的数据放到Elasticsearch 、MogoDB里,后台通过查询Elasticsearch 查询数据进行分页。
对于单库来说,数据库事务ACID能保证数据的一致性,但是但我们操作的数据存在于不同的库来说,就需要一套全局的事务机制来保证数据的一致性了。
缓存不仅可以加速数据读取,而且通常缓存可以帮数据库抵挡住大部分的查询请求,缓存也是在缓解数据库查询压力分布式缓存、应用缓存、数据库缓存等,缓存的使用场景无处不在,但使用缓存也会有一些需要注意的事项。
缓存的使用场景无处不在,但使用缓存也会有一些需要注意的事项,这里主要以分布式缓存案例来了解缓存存在的一些通用问题。这里关于redis的问题比如数据一致性问题、缓存穿透、数据过期、雪崩等问题就不再一一表述了。
相对于传统数据库,非关系型数据库通常要比关系型数据库性能要快、而且变更方便,对内容格式要求宽松,增加删除内容属性不需像数据一样同步修改数据库的表结构。但是关系型数据库完善的事务机制,使得关系型数据库又是必须的,所以Nosql通常作为对关系型数据库的补充,在一些特定场景使用Nosql能让系统性能得到很大的改善,常用的NoSql数据库 有键值数据库、文档型数据库、列族数据库几种类型。
对于一些关系简单的热点数据我们可以使用redis这种键值数据库来存储。对于一些数据量大,数据格式多样化对查询性能又有要求的场景我们可以使用向MogoDB文档型的数据库来存储,而对于一些有全文搜索类的需求的我们可以使用Elasticsearch来满足搜索的需求。
这里不做详细表述了,之前的文章作了专题讨论。
CSDNhttps://mp.csdn.net/mp_blog/creation/editor/123078024
这里不具体表述了,其他小伙伴有写过类似的文章,包含了常见的参数调整,仅供参考。参数实际过程中需要DBA根据业务情况和实际机器性能进行微调到一个恰当的值。
达梦数据库:性能参数优化篇_祢真伟大的博客-CSDN博客_达梦数据库调优达梦数据库性能参数优化篇不分操作系统,根据服务器软硬件性能,对应的优化数据库性能:MPP–服务器环境:CPU:4路8核 /内存:200GB /磁盘阵列:10TB--call SF_SET_SYSTEM_PARA_VALUE ('参数名',参数值,1为后续生效,2修改ini文件参数);call SF_SET_SYSTEM_PARA_VALUE ('MAX_SESSIONS',3000,1,2);call SF_SET_SYSTEM_PARA_VALUE ('MAX_OS_MEMORY',80,https://blog.csdn.net/qq_37358909/article/details/109360981
首先索引是一种特殊的数据库结构,由数据表中的一列或多列组合而成,可以用来快速查询数据表中有某一特定值的记录。通过索引,查询数据时不用读完记录的所有信息,而只是查询索引列。否则,数据库系统将读取每条记录的所有信息进行匹配。(只要你不说索引就好比一本书的目录就不会被别人笑话你)
索引结构(索引底层的数据结构)包含:二叉树、红黑树、Hash、B-树(B+tree)。至于DM这边索引和oracle一样是btree。
建立索引的主要的一些原则有:
影响CBO的因素有哪些?
1、索引的原则性约束问题。
2、数据分布问题,比如索引列包含 null,数据直方图不均衡,影响聚簇因子。
3、表的扫描方式合理性。
4、表的连接方式
排序合并连接(Sort Merge Join (SMJ) ):通常情况下散列连接的效果都比排序合并连接要好,然而如果行源已经被排过序,在执行排序合并连接时不需要再排序了,这时排序合并连接的性能会优于散列连接。
群集连接 (CLUSTER JOIN) 很少用到。
笛卡尔连接 (CARTESIAN JOIN) 笛卡尔连接是指在sql语句中没有写出表连接的条件,优化器把第一个表的每一条记录和第二个表的所有纪录相连接。
5、索引分裂等。
2.1开启跟踪日志记录执行 SQL
跟踪日志文件是一个纯文本文件,以 dmsql_实例名_日期_时间
命名, 默认生成在 DM 安装目录的 log 子目录下。跟踪日志内容包含系统各会话执行的 SQL 语句、参数信息、错误信息、执行时间等。跟踪日志主要用于分析错误和分析性能问题,基于跟踪日志可以对系统运行状态进行分析。
跟踪日志配置方式
根据需要配置数据文件目录下的 sqllog.ini
,如下所示:
BUF_TOTAL_SIZE = 10240 #SQLs Log Buffer Total Size(K)(1024~1024000)
BUF_SIZE = 1024 #SQLs Log Buffer Size(K)(50~409600)
BUF_KEEP_CNT = 6 #SQLs Log buffer keeped count(1~100)
[SLOG_ALL]
FILE_PATH = ../log
PART_STOR = 0
SWITCH_MODE = 1
SWITCH_LIMIT = 100000
ASYNC_FLUSH = 0
FILE_NUM = 200
ITEMS = 0
SQL_TRACE_MASK = 2:3:23:24:25
MIN_EXEC_TIME = 0
USER_MODE = 0
USERS =
|
配置 dm.ini 中 SVR_LOG = 1
启用 sqllog.ini 配置,该参数为动态参数,可通过调用数据库函数直接修改,如下所示:
SP_SET_PARA_VALUE(1,'SVR_LOG',1);
|
如果对 sqllog.ini
进行了修改,可通过调用以下函数即时生效,无需重启数据库,如下所示:
SP_REFRESH_SVR_LOG_CONFIG(); |
2.2根据跟踪日志查找慢 SQL
当 INI 参数 ENABLE_MONITOR=1、MONITOR_TIME=1
打开时,显示系统最近 1000 条执行时间超过预定值的 SQL 语句。默认预定值为 1000 毫秒。可通过 SP_SET_LONG_TIME
系统函数修改,通过 SF_GET_LONG_TIME
系统函数查看当前值。
--两个参数均为动态参数,可直接调用系统函数进行修改
SP_SET_PARA_VALUE(1,'ENABLE_MONITOR',1);
SP_SET_PARA_VALUE(1,'MONITOR_TIME',1); |
注意:
通过 SP_SET_PARA_VALUE 方式修改的参数值仅对当前会话以及新建会话生效,对其它已建立会话不生效。
超过执行时间阈值的 SQL 语句记录在 V$LONG_EXEC_SQLS
系统视图中。
查询该视图获取结果,如下所示:
SELECT * FROM V$LONG_EXEC_SQLS; |
缩进越多的越先执行,同样缩进的上面的先执行,下面的后执行,上下的优先级高于内外。
ET 功能默认关闭,可通过配置 INI 参数中的 ENABLE_MONITOR=1、MONITOR_SQL_EXEC=1
开启该功能。
--两个参数均为动态参数,可直接调用系统函数进行修改
SP_SET_PARA_VALUE(1,'ENABLE_MONITOR',1);
SP_SET_PARA_VALUE(1,'MONITOR_SQL_EXEC',1);
--关闭 ET
SP_SET_PARA_VALUE(1,'ENABLE_MONITOR',0);
SP_SET_PARA_VALUE(1,'MONITOR_SQL_EXEC',0); |
ET 功能的开启将对数据库整体性能造成一定影响,优化工作结束后尽量关闭该功能以提升数据库整体运行效率。
5.1、手工收集统计信息的方法如下所示:
--收集指定用户下所有表所有列的统计信息:
DBMS_STATS.GATHER_SCHEMA_STATS('username',100,TRUE,'FOR ALL COLUMNS SIZE AUTO');
--收集指定用户下所有索引的统计信息:
DBMS_STATS.GATHER_SCHEMA_STATS('usename',1.0,TRUE,'FOR ALL INDEXED SIZE AUTO');
--或 收集单个索引统计信息:
DBMS_STATS.GATHER_INDEX_STATS('username','IDX_T2_X');
--收集指定用户下某表统计信息:
DBMS_STATS.GATHER_TABLE_STATS('username','table_name',null,100,TRUE,'FOR ALL COLUMNS SIZE AUTO');
--收集某表某列的统计信息:
STAT 100 ON table_name(column_name); |
注意:统计信息收集过程中将对数据库性能造成一定影响,避免在业务高峰期收集统计信息。
DM 数据库支持统计信息的自动收集,当全表数据量变化超过设定阈值后可自动更新统计信息。
--打开表数据量监控开关,参数值为 1 时监控所有表,2 时仅监控配置表
SP_SET_PARA_VALUE(1,'AUTO_STAT_OBJ',2);
--设置 SYSDBA.T 表数据变化率超过 15% 时触发自动更新统计信息
DBMS_STATS.SET_TABLE_PREFS('SYSDBA','T','STALE_PERCENT',15);
--配置自动收集统计信息触发时机
SP_CREATE_AUTO_STAT_TRIGGER(1, 1, 1, 1,'14:36', '2020/3/31',60,1);
|
--函数各参数介绍
SP_CREATE_AUTO_STAT_TRIGGER(
TYPE INT, --间隔类型,默认为天
FREQ_INTERVAL INT, --间隔频率,默认 1
FREQ_SUB_INTERVAL INT, --间隔频率,与 FREQ_INTERVAL 配合使用
FREQ_MINUTE_INTERVAL INT, --间隔分钟,默认为 1440
STARTTIME VARCHAR(128), --开始时间,默认为 22:00
DURING_START_DATE VARCHAR(128), --重复执行的起始时间,默认 1900/1/1
MAX_RUN_DURATION INT, --允许的最长执行时间(秒),默认不限制
ENABLE INT --0 关闭,1 启用 --默认为 1
); |
堆表的数据存储是无序存储,因此需要使无序变为有序。下面是提高聚簇因子的办法。
a、对于表上的多个索引以及组合索引的情形,索引的创建应考虑按应该按照经常频繁读取的大范围数据的读取顺序来创建索引。
b、定期重构表(针对堆表),也就是使得表与索引上的数据顺序更接近。注意,是重构表,而不是重建索引。
重建索引并不能显剧提高CF的值,因为索引列通常是有序的,无序的是原始表上的数据。
提取原始表上的数据到一个临时表,禁用依赖于该表的相关约束,truncate原始表,再将临时表的数据按索引访问顺序填充到原始表。
c、使用聚簇表来代替堆表。
以上针对SQL优化涉及的方面具体后边再详细针对每个知识点再写吧。
这边DM的执行计划固定我也不是特别清楚,应该是跟oracle差不多,Oracle的固定执行计划方式大概有4中,常用最多的是SQL PROFILE了,具体不表述了。
固定SQL执行计划方式:
数字类型:
1)不到不要使用DOUBLE,不仅仅只是存储长度的问题,同时还会存在精确性的问题。
2)固定精度的小数,也不建议使用DECIMAL
建议乘以固定倍数转换成整数存储,可以大大节省存储空间,且不会带来任何附加维护成本。
3)对于整数的存储,在数据量较大的情况下,建议区分开 TINYINT / INT / BIGINT 的选择
因为三者所占用的存储空间也有很大的差别,能确定不会使用负数的字段,建议添加unsigned定义。当然,如果数据量较小的数据库,也可以不用严格区分三个整数类型。
4)对于整型数值,mysql支持在类型名称后面的小括号内指定显示宽度
例如int(5)表示当数值宽度小于5位时候在数值前面填满宽度,一般配合zerofill属性使用。如果一个列指定为zerofill,则MySQL自动为该列添加unsigned属性。
5)在数据量较大时、建议把实数类型转为整数类型。
原因很简单:1. 浮点不精确;2.定点计算代价昂贵。例如:要存放财务数据精确到万分之一、则可以把所有金额乘以一百万、然后存在BIGINT下。
字符类型:
1)尽量不要使用 TEXT 数据类型,其处理方式决定了他的性能要低于char或者是varchar类型的处理。
定长字段,建议使用 CHAR 类型,不定长字段尽量使用 VARCHAR,且仅仅设定适当的最大长度,而不是非常随意的给一个很大的最大长度限定,因为不同的长度范围,MySQL也会有不一样的存储处理。
2)char会删除字符串尾部的空格,varchar不会,varchar向前补1-2字节;char定长。binary类似于char,binary只能保存二进制字符串。
char是固定长度,所以它的处理速度比varchar快得多,但缺点是浪费存储空间,不能在行尾保存空格。在MySQL中,MyISAM建议使用固定长度代替可变长度列;InnoDB建议使用varchar类型,因为在InnoDB中,内部行存储格式没有区分固定长度和可变长度。
3)enum类型忽略大小写。
4)text与blob区别:
blob保存二进制数据;text保存字符数据,有字符集。text和blob不能有默认值。
应用:text与blob主要区别是text用来保存字符数据(如文章,日记等),blob用来保存二进制数据(如照片等)。blob与text在执行了大量删除操作时候,有性能问题(产生大量的“空洞“),为提高性能建议定期optimize table 对这类表进行碎片整理。
关于text与blob我们有些看法建议:
BLOB和TEXT值也会引起自己的一些问题,特别是执行了大量的删除或更新操作的时候。删除这种值会在数据表中留下很大的"空洞",以后填入这些"空洞"的记录可能长度不同,为了提高性能,建议定期使用 OPTIMIZE TABLE 功能对这类表进行碎片整理.
在不必要的时候避免检索大型的BLOB或TEXT值。
把BLOB或TEXT列分离到单独的表中。在某些环境中,如果把这些数据列移动到第二张数据表中,可以让你把原数据表中 的数据列转换为固定长度的数据行格式,那么它就是有意义的。这会减少主表中的碎片,使你得到固定长度数据行的性能优势。它还使你在主数据表上运行 SELECT *查询的时候不会通过网络传输大量的BLOB或TEXT值。
时间类型:
1)尽量使用TIMESTAMP类型
因为其存储空间只需要 DATETIME 类型的一半。对于只需要精确到某一天的数据类型,建议使用DATE类型,因为他的存储空间只需要3个字节,比TIMESTAMP还少。不建议通过INT类型类存储一个unix timestamp 的值,因为这太不直观,会给维护带来不必要的麻烦,同时还不会带来任何好处。
2)根据实际需要选择能够满足应用的最小存储日期类型。
3)timestamp,日期类型中只有它能够和实际时区相对应。
ENUM & SET:
对于状态字段,可以尝试使用 ENUM 来存放,因为可以极大的降低存储空间,而且即使需要增加新的类型,只要增加于末尾,修改结构也不需要重建表数据。如果是存放可预先定义的属性数据呢?可以尝试使用SET类型,即使存在多种属性,同样可以游刃有余,同时还可以节省不小的存储空间。
LOB类型:
(1)强烈反对在数据库中存放 LOB 类型数据,虽然数据库提供了这样的功能,但这不是他所擅长的,我们更应该让合适的工具做他擅长的事情,才能将其发挥到极致。
(2)适当的冗余,增加计算列:(实际开发中必须思考的点)
数据库设计的实用原则是:在数据冗余和处理速度之间找到合适的平衡点。
满足范式的表一定是规范化的表,但不一定是最佳的设计。很多情况下会为了提高数据库的运行效率,常常需要降低范式标准:适当增加冗余,达到以空间换时间的目的。比如
我们有一个表,产品名称,单价,库存量,总价值。这个表是不满足第三范式的,因为“总价值”可以由“单价”乘以“数量”得到,说明“金额”是冗余字段。但是,增加“总价值”这个冗余字段,可以提高查询统计的速度,这就是以空间换时间的作法。合理的冗余可以分散数据量大的表的并发压力,也可以加快特殊查询的速度,冗余字段可以有效减少数据库表的连接,提高效率。
其中"总价值"就是一个计算列,在数据库中有两种类型:数据列和计算列,数据列就是需要我们手动或者程序给予赋值的列,计算列是源于表中其他的数据计算得来,比如这里的"总价值"
在SQL中创建计算列:
create table goods(
id int auto_increment not null,
c1 int,
c2 int,
c3 int as (c1+c2), //这个就是计算列啦
primary key(id)
)
(3)索引的设计:
表优化的重要途径,比如百万级别的表没有索引,注定卡死。
(4)主键和外键的必要性(实际项目开发的重要取舍)
概述:
主键与外键的设计,在全局数据库的设计中,占有重要地位。 因为:主键是实体的抽象,主键与外键的配对,表示实体之间的连接。
主键:
根据第二范式,需要有一个字段去标识这条记录,主键无疑是最好的标识,但是很多表也不一定需要主键,但是对于数据量大,查询频繁的数据库表,一定要有主键,主键可以增加效率、防止重复等优点。
主键的选择也比较重要,一般选择总的长度小的键,小的键的比较速度快,同时小的键可以使主键的B树结构的层次更少。
主键的选择还要注意组合主键的字段次序,对于组合主键来说,不同的字段次序的主键的性能差别可能会很大,一般应该选择重复率低、单独或者组合查询可能性大的字段放在前面。
外键:
外键作为数据库对象,很多人认为麻烦而不用,实际上,外键在大部分情况下是很有用的,理由是:外键是最高效的一致性维护方法。
数据库的一致性要求,依次可以用外键、CHECK约束、规则约束、触发器、客户端程序,一般认为,离数据越近的方法效率越高。但是!!!要谨慎使用级联删除和级联更新,因为级联删除和级联更新有些突破了传统的关于外键的定义,功能有点太过强大,使用前必须确定自己已经把握好其功能范围,否则,级联删除和级联更新可能让你的数据莫名其妙的被修改或者丢失。从性能看级联删除和级联更新是比其他方法更高效的方法。
实际项目中的主外键取舍设计:(在性能和可扩展性之间寻求平衡)
边缘模块指的是小功能不常用需求很少再改的模块;中心模块是指关联的东西太多的模块、是很多表的主表;物理键指的是在表建立主外键关联,逻辑主外键指的是利用字段去实现逻辑主外键关联;热点模块指的是需求经常要改的模块。
注意:
不用外键而用程序控制数据一致性和完整性时,应该写一层来保证,然后个个应用通过这个层来访问数据库。
外键是有性能问题的,不能过分追求。
(5)存储过程、视图、函数的适当使用 :
很多人习惯将复杂操作都放在应用程序层,但如果你要优化数据访问性能,将SQL代码移植到数据库上(使用存储过程,视图,函数和触发器)也是一个很大的改进原因如下:
1)存储过程减少了网络传输、处理及存储的工作量,且经过编译和优化,执行速度快,易于维护,且表的结构改变时,不影响客户端的应用程序
2)使用存储过程,视图,函数有助于减少应用程序中SQL复制的弊端,因为现在只在一个地方集中处理SQL
3)使用数据库对象实现所有的TSQL有助于分析TSQL的性能问题,同时有助于你集中管理TSQL代码,更好的重构TSQL代码。
(6)传说中的‘三少原则’:
1)数据库的表越少越好
2)表的字段越少越好
3)字段中的组合主键、组合索引越少越好
这里的少是相对的,是减少数据冗余的重要设计理念而已。
实际上,我们为了减少单表查询压力,会把去分表,从而分发记录量,避免一个超级表的诞生。
(7)分割你的表,减小表尺寸
如果你发现某个表的记录太多,例如超过一千万条,则要对该表进行水平分割。水平分割的做法是,以该表主键的某个值为界线,将该表的记录水平分割为两个表。
如果你若发现某个表的字段太多,例如超过八十个,则垂直分割该表,将原来的一个表分解为两个表
(8)字段设计原则:
字段是数据库最基本的单位,其设计对性能的影响是很大的。需要注意如下:
1)数据类型尽量用数字型,数字型的比较比字符型的快很多。
2)数据类型尽量小,这里的尽量小是指在满足可以预见的未来需求的前提下的。
3)尽量不要允许NULL,除非必要,可以用NOT NULL+DEFAULT代替。
NULL 类型比较特殊,SQL 难优化。虽然 MySQL NULL类型和 Oracle 的NULL 有差异,会进入索引中,但如果是一个组合索引,那么这个NULL 类型的字段会极大影响整个索引的效率。此外,NULL 在索引中的处理也是特殊的,也会占用额外的存放空间。
4)少用TEXT和IMAGE,二进制字段的读写是比较慢的,而且,读取的方法也不多,大部分情况下最好不用。
5)自增字段要慎用,不利于数据迁移.
其实,在表设计这方面个人觉得阿里开发规范上做的还是比较好的,分为【推荐】、【强制】、【建议】三个维度进行合理的规范化落地实施,后续分享阿里开发手册数据库规范,我们谈一谈为什么阿里要这么做,以上就分享到这里吧。谢谢