1.优化sql以及索引
1.1优化sql
1、有索引但未被用到的情况(不建议)
(1)避免like的参数以通配符开头时
尽量避免Like的参数以通配符开头,否则数据库引擎会放弃使用索引而进行全表扫描。
以通配符开头的sql语句,例如:select * from t_credit_detail where Flistid like '%0'\G
这是全表扫描,没有使用到索引,不建议使用。
不以通配符开头的sql语句,例如:select * from t_credit_detail where Flistid like '2%'\G
很明显,这使用到了索引,是有范围的查找了,比以通配符开头的sql语句效率提高不少。
(2) 避免where条件不符合最左前缀原则。最左前缀原则:mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整(IN和=可以乱序)。
(3) 使用!= 或 <> 操作符时
尽量避免使用!= 或 <>操作符,否则数据库引擎会放弃使用索引而进行全表扫描。使用>或<会比较高效。
select * from t_credit_detail where Flistid != '2000000608201108010831508721'\G
(4) 避免索引列参与计算
应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。
select * from t_credit_detail where Flistid +1 > '2000000608201108010831508722'\G
(5) 避免对字段进行null值判断
应尽量避免在where子句中对字段进行null值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: 低效:select * from t_credit_detail where Flistid is null ;
可以在Flistid上设置默认值0,确保表中Flistid列没有null值,然后这样查询: 高效:select * from t_credit_detail where Flistid =0;
(6) 避免使用or来连接条件
应尽量避免在where子句中使用or来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如: 低效:select * from t_credit_detail where Flistid = '2000000608201108010831508721' or Flistid = '10000200001';
可以用下面这样的查询代替上面的 or 查询: 高效:select from t_credit_detail where Flistid = '2000000608201108010831508721' union all select from t_credit_detail where Flistid = '10000200001';
2、避免select *
在解析的过程中,会将'*' 依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。
所以,应该养成一个需要什么就取什么的好习惯。
3、order by 语句优化
任何在Order by语句的非索引项或者有计算表达式都将降低查询速度。
方法:1.重写order by语句以使用索引;2.为所使用的列建立另外一个索引 3.绝对避免在order by子句中使用表达式。
4、GROUP BY语句优化
提高GROUP BY 语句的效率, 可以通过将不需要的记录在GROUP BY 之前过滤掉(如下例,先用where语句过滤掉一部分数据)
低效:
SELECT JOB , AVG(SAL) FROM EMP GROUP by JOB HAVING JOB = ‘PRESIDENT' OR JOB = ‘MANAGER'
高效:
SELECT JOB , AVG(SAL) FROM EMP WHERE JOB = ‘PRESIDENT' OR JOB = ‘MANAGER' GROUP by JOB
5、用 exists 代替 in
很多时候用 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)
6、使用 varchar/nvarchar 代替 char/nchar
尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
7、能用DISTINCT的就不用GROUP BY
SELECT OrderID FROM Details WHERE UnitPrice > 10 GROUP BY OrderID
可改为:
SELECT DISTINCT OrderID FROM Details WHERE UnitPrice > 10
8、能用UNION ALL就不要用UNION
UNION ALL不执行SELECT DISTINCT函数,这样就会减少很多不必要的资源。(UNION ALL允许重复)
9、在Join表的时候使用相当类型的例,并将其索引
如果应用程序有很多JOIN 查询,你应该确认两个表中Join的字段是被建过索引的。这样,MySQL内部会启动为你优化Join的SQL语句的机制。
而且,这些被用来Join的字段,应该是相同的类型的。例如:如果你要把 DECIMAL 字段和一个 INT 字段Join在一起,MySQL就无法使用它们的索引。对于那些STRING类型,还需要有相同的字符集才行。(两个表的字符集有可能不一样)
SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo FROM Persons INNER JOIN Orders ON Persons.Id_P = Orders.Id_P ORDER BY Persons.LastName
1.2优化索引
很多数据库系统性能不理想是因为系统没有经过整体优化,存在大量性能低下的SQL 语句。这类SQL语句性能不好的首要原因是缺乏高效的索引。没有索引除了导致语句本身运行速度慢外,更是导致大量的磁盘读写操作,使得整个系统性能都受之影响而变差。解决这类系统的首要办法是优化这些没有索引或索引不够好的SQL语句。优化SQL语句的关键是尽可能减少语句的logical reads(是指语句执行时需要访问的单位为8K的数据页) logical reads 越少,其需要的内存和CPU时间也就越少,语句执行速度就越快。不言而喻,索引的最大好处是它可以极大减少SQL语句的logical reads数目,从而极大减少语句的执行时间。创建索引的关键是索引要能够大大减少语句的logical reads。一个索引好不好,主要看它减少的logical reads多不多。 运行set statistics io命令可以得到SQL语句的logical reads信息。Logical reads中包含该语句从内存数据缓冲区中访问的页数和从物理磁盘读取的页数。而physical reads表示那些没有驻留在内存缓冲区中需要从磁盘读取的数据页。
1对出现在where子句中的字段加索引
全表扫描的性能通常是很差的,要尽量避免。 创建索引的技巧之一是对经常出现在where条件中的字段创建索引
2.组合索引
单字段索引是指只有一个字段的索引,而组合索引指有多个字段构成的索引。如果where语句中有多个字段,那么可以考虑创建组合索引。组合索引中字段的顺序是非常重要的,越是唯一的字段越是要靠前。(根据最左前缀准则)另外,无论是组合索引还是单个列的索引,尽量不要选择那些唯一性很低的字段。所以如果对单字段进行索引,建议使用set statistics profile(会输出语句的执行计划)来验证索引确实被充分使用。logical reads越少的索引越好。
3.覆盖索引
覆盖索引能够使得语句不需要访问表仅仅访问索引就能够得到所有需要的数据。 因为聚集索引叶子节点就是数据所以无所谓覆盖与否,所以覆盖索引主要是针对非聚集索引而言。执行计划中除了index seek外,还有一个Bookmark Lookup关键字。 Bookmark Lookup表示语句在访问索引后还需要对表进行额外的Bookmark Lookup操作才能得到数据。也就是说为得到一行数据起码有两次IO,一次访问索引,一次访问基本表。 如果语句返回的行数很多,那么Bookmark Lookup操作的开销是很大的。 覆盖索引能够避免昂贵的Bookmark Lookup操作,减少IO的次数,提高语句的性能。 覆盖索引需要包含select子句和WHERE子句中出现的所有字段。所以创建覆盖索引是减少logical reads提升语句性能的非常有用的优化技巧。
问题1,是否值得在identity字段(自增字段)上建立聚集索引。
答案取决于identity 字段如何在语句中使用。如果你经常根据该字段搜索返回很少的行,那么在其上建立索引是值得的。反之如果identity字段根本很少在语句中使用,那么就不应该对其建立任何索引。
问题2,一个表应该建立多少索引合适。
如果表的80%以上的语句都是读操作,那么索引可以多些。但是不要太多。 特别是不要对那些更新频繁的表其建立很多的索引。很少表有超过5个以上的索引。过多的索引不但增加其占用的磁盘空间,也增加了SQL Server 维护索引的开销。
问题4:为什么SQL Server 在执行计划中没有使用你认为应该使用的索引?原因是多样的。
一种原因是该语句返回的结果超过了表的20%数据,使得SQL Server 认为scan比seek更有效。另一种原因可能是表字段的statistics过期了,不能准确反映数据的分布情况。你可以使用命令UPDATE STATISTICS tablename with FULLSCAN来更新它。只有同步的准确的statistics才能保证SQL Server 产生正确的执行计划。
问题5、什么使用聚集索引,什么时候使用非聚集索引
在SQL Server 中索引有聚集索引和非聚集索引两种。它们的主要差别是前者的索引叶子就是数据本身,而后者的叶子节点包含的是指向数据的书签(即数据行号或聚集索引的key)。来自聚集索引的键值由所有非聚集索引作为查找键使用,因此存储在每个非聚集索引的叶条目内。对一个表而言聚集索引只能有一个,而非聚集索引可以有多个。只是聚集索引没有Bookmark Lookup操作。
在创建聚集索引之前,应先了解您的数据是如何被访问的。可考虑将聚集索引用于:
- 包含大量非重复值的列。
- 使用下列运算符返回一个范围值的查询:BETWEEN、>、>=、< 和 <=。
- 被连续访问的列。
- 返回大型结果集的查询。
- 经常被使用联接或 GROUP BY 子句的查询访问的列;一般来说,这些是外键列。对 ORDER BY 或 GROUP BY 子句中指定的列进行索引,可以使 SQL Server 不必对数据进行排序,因为这些行已经排序。这样可以提高查询性能。
聚集索引不适用于:
- 频繁更改的列,这将导致整行移动(因为 SQL Server 必须按物理顺序保留行中的数据值)。这一点要特别注意,因为在大数据量事务处理系统中数据是易失的。
1.3缓存
1.4主从复制、主主复制
MySQL主从复制是其最重要的功能之一。主从复制是指一台服务器充当主数据库服务器,另一台或多台服务器充当从数据库服务器,主服务器中的数据自动复制到从服务器之中。MySQL主从复制的基础是主服务器对数据库修改记录二进制日志,从服务器通过主服务器的二进制日志自动执行更新。
主从复制的作用
1、做数据的热备,作为后备数据库,主数据库服务器故障后,可切换到从数据库继续工作,避免数据丢失。
2、架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的频率,提高单个机器的I/O性能。(请求发到多部机子上)
3、读写分离,使数据库能支撑更大的并发。一主多从的部署方案,将涉及数据写的操作放在Master端操作,而将数据读的操作分散到众多的Slave当中。降低了Master的负载,提高数据写入的响应效率;多台从服务器提供读,分担了请求,提高了读的效率。在报表中尤其重要。由于部分报表sql语句非常的慢,导致锁表,影响前台服务。如果前台使用master,报表使用slave,那么报表sql将不会造成前台锁,保证了前台速度。
主从复制的原理
1.数据库有个bin-log二进制文件,记录了所有sql语句。
2.我们的目标就是把主数据库的bin-log文件的sql语句复制过来。
3.让其在从数据的relay-log重做日志文件中再执行一次这些sql语句即可。
主从复制步骤以及涉及的线程:
一:主库db的更新事件(update、insert、delete)被写到binlog
二:从库发起连接,连接到主库
三:此时主库创建一个binlog dump thread线程,把binlog的内容发送到从库
四:从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log.
五:还会创建一个SQL线程,从relay log里面读取内容,从Exec_Master_Log_Pos位置开始
1.binlog输出线程:每当有从库连接到主库的时候,主库都会创建一个线程输出binlog,然后发送binlog内容到从库。
2.从库I/O线程:当START SLAVE语句在从库开始执行之后,从库创建一个I/O线程,该线程连接到主库并请求主库发送binlog里面的更新记录到从库上。(从主库先传输下来)从库I/O线程读取主库的binlog输出线程发送的更新并拷贝这些更新到本地文件,其中包括relay log文件(再更新relay log)。
3.从库的SQL线程:从库创建一个SQL线程,这个线程读取从库I/O线程写到relay log的更新事件并执行。
做主从后主服务器挂了怎么办
假设发生了突发事件,master宕机,现在的需求是要将从库提升为主库,另外一个为从库:
1.确保所有的从库的relay log全部更新完毕,在每个从库上执行stop slave io_thread; show processlist;直到看到Has read all relay log,则表示从库更新都执行完毕了
2.登陆所有从库,查看master.info文件,选择一个从库为新的主库
3.登陆该从库,执行stop slave; 并进入数据库目录,删除master.info和relay-log.info文件, 配置my.cnf文件,开启log-bin,如果有log-slaves-updates和read-only则要注释掉,执行reset master
4.创建用于同步的用户并授权slave,同第五大步骤
5.登录另外一台从库,执行stop slave停止同步
6.根据第七大步骤连接到新的主库
7.执行start slave;
8.修改新的master数据,测试slave是否同步更新
主从复制延迟
主库针对读写操作,顺序写 binlog,从库单线程去主库读"写操作的binlog",从库取到 binlog在本地原样执行(随机写),来保证主从数据逻辑上一致.mysql的主从复制都是单线程的操作,主库对所有DDL和DML产生 binlog,binlog是顺序写,所以效率很高,slave的Slave_IO_Running线程到主库取日志,效率比较高,下一步问题来了,slave的 slave_sql_running线程将主库的 DDL和DML操作在 slave实施。DML,DDL的IO操作是随机的,不能顺序的,成本高很多,还有可能slave上的其他查询产生 lock,由于 slave_sql_running也是单线程的,所以 一个 DDL卡住了,需求需求执行一段时间,那么所有之后的DDL会等待这个 DDL执行完才会继续执行,这就导致了延迟.由于master可以并发,Slave_sql_running线程却不可以,所以主库执行 DDL需求一段时间,在slave执行相同的DDL时,就产生了延迟.
主从同步延迟产生原因
当主库的TPS并发较高时,产生的DDL数量超过Slave一个 sql线程所能承受的范围,那么延迟就产生了,当然还有就是可能与 slave的大型 query语句产生了锁等待
首要原因:数据库在业务上读写压力太大,CPU计算负荷大,网卡负荷大,硬盘随机IO太高
次要原因:读写 binlog带来的性能影响,网络传输延迟
主从同步延迟解决方案
架构方面:mysql压力变小,延迟自然会变小
1.业务的持久化层的实现采用分库架构,mysql服务可平行扩展分散压力
2.单个库读写分离,一主多从,主写从读,分散压力。
3.服务的基础架构在业务和mysql之间加放 cache层
4.不同业务的mysql放在不同的机器
5.使用比主加更好的硬件设备作slave
1.5读写分离
MySQL的主从复制和MySQL的读写分离两者有着紧密联系,首先部署主从复制,只有主从复制完了,才能在此基础上进行数据的读写分离。读写分离就是只在主服务器上写,只在从服务器上读,基本的原理是让主数据库处理事务性查询,而从数据库处理select查询,数据库复制被用来把事务性查询导致的改变更新同步到集群中的从数据库,即主从复制。
1.基于程序代码内部实现
在代码中根据select,insert进行路由分类,这类方法也是目前生产环境应用最广泛的,优点是性能好,因为在程序代码中实现,不需要曾加额外的设备作为硬件开支,缺点是需要开发人员来实现,运维人员无从下手。
2.基于中间代理层实现
代理一般位于客户端和服务器之间,代理服务器接到客户端请求后通过判断后转发到后端数据库,有两个代表性程序。
(1)mysql-proxy 为mysql开源项目,通过其自带的lua脚本进行SQL判断,虽然是mysql的官方产品,但是mysql官方不建议将其应用到生产环境
(2)Amoeba (变形虫)由陈思儒开发,曾就职与阿里巴巴,该程序由java语言进行开发,阿里巴巴将其应用于生成环境,它不支持事物和存储过程
通过程序代码实现mysql读写分离自然是一个不错的选择,但是并不是所有的应用都适合在程序代码中实现读写分离,像一些大型复杂的java应用,如果在程序代码中实现读写分离对代码改动就较大,像这种应用一般会考虑使用代理层来实现。
MySQL Proxy是一个处于你的client端和MySQL server端之间的简单程序,它可以监测、分析或改变它们的通信。它使用灵活,没有限制,常见的用途包括:负载平衡,故障、查询分析,查询过滤和修改等等。MySQL Proxy就是这么一个中间层代理,简单的说,MySQL Proxy就是一个连接池,负责将前台应用的连接请求转发给后台的数据库,并且通过使用lua脚本,可以实现复杂的连接控制和过滤,从而实现读写分离和负载平衡。对于应用来说,MySQL Proxy是完全透明的,应用则只需要连接到MySQL Proxy的监听端口即可。当然,这样proxy机器可能单点失效,但完全可以使用多个proxy机器做为冗余,在应用服务器的连接池配置中配置到多个proxy的连接参数即可。MySQL Proxy更强大的一项功能是实现“读写分离”,基本原理是让主数据库处理事务性查询,让从库处理SELECT查询。数据库复制被用来把事务性查询导致的变更同步到集群中的从库。
读写分离的好处: 1)物理服务器增加,负荷增加 2)主从只负责各自的写和读,极大程度的缓解X锁和S锁争用 3)从库可配置myisam引擎,提升查询性能以及节约系统开销 4)从库同步主库的数据和主库直接写还是有区别的,通过主库发送来的binlog恢复数据,但是,最重要区别在于主库向从库发送binlog是异步的,从库恢复数据也是异步的 5)读写分离适用与读远大于写的场景,如果只有一台服务器,当select很多时,update和delete会被这些select访问中的数据堵塞,等待select结束,并发性能不高。 对于写和读比例相近的应用,应该部署双主相互复制 6)可以在从库启动是增加一些参数来提高其读的性能,例如--skip-innodb、--skip-bdb、--low-priority-updates以及--delay-key-write=ALL 当然这些设置也是需要根据具体业务需求来定得,不一定能用上 7)分摊读取。假如我们有1主3从,不考虑上述1中提到的从库单方面设置,假设现在1 分钟内有10条写入,150条读取。那么,1主3从相当于共计40条写入,而读取总数没变,因此平均下来每台服务器承担了10条写入和50条读取(主库不 承担读取操作)。因此,虽然写入没变,但是读取大大分摊了,提高了系统性能。另外,当读取被分摊后,又间接提高了写入的性能。所以,总体性能提高了,说白 了就是拿机器和带宽换性能。MySQL官方文档中有相关演算公式:官方文档 见6.9FAQ之“MySQL复制能够何时和多大程度提高系统性能” 8)MySQL复制另外一大功能是增加冗余,提高可用性,当一台数据库服务器宕机后能通过调整另外一台从库来以最快的速度恢复服务,因此不能光看性能,也就是说1主1从也是可以的。
1.6分区、垂直分表、水平分表
- 表分区
表分区其实就是将一张大数据量表中的数据按照不同的分区策略分配到不同的系统分区、硬盘或是不同的服务器设备上,实现数据的均衡分配,这样做的好处是均衡大数据量数据到不同的存储介子中,这样每个分区均摊了一部分数据,然后可以定位到指定的分区中,对数据表进行需求操作,另外,也方便管理水表,比如要删除某个时间段的数据,就可以按照日期分区,然后直接删除该日期分区即可,并且效率相对于传统的DELETE数据效率高很多,这里以Mysql为例进行说明。
分区和分表的区别:
分区和分表针对的都是数据表,而分表是真正的生成数据表,是将一张大数据量的表分成多个小表实现数据均衡;
分区并不是生成新的数据表,而是将表的数据均衡分摊到不同的硬盘,系统或是不同服务器存储介子中,实际上还是一张表。
另外,分区和分表都可以做到将表的数据均衡到不同的地方,提高数据检索的效率,降低数据库的频繁IO压力值,
分区的优点如下:
1、相对于单个文件系统或是硬盘,分区可以存储更多的数据;
2、数据管理比较方便,比如要清理或废弃某年的数据,就可以直接删除该日期的分区数据即可;
3、精准定位分区查询数据,不需要全表扫描查询,大大提高数据检索效率;
4、可跨多个分区磁盘查询,来提高查询的吞吐量;
5、在涉及聚合函数查询时,可以很容易进行数据的合并;
表的分区的原理理解起来比较简单,其实就是把一张大数据量的表,根据分区策略进行分区,分区设置完成之后,由数据库自身的储存引擎来实现分发数据到指定的分区中去,正如上图所示,一张数据表被分成了n个分区,并且分区被放入到不同的介子disk中,每个disk中包含自少一个分区,这就实现了数据的均衡以及通过跨分区介子检索提高了整体的数据操作IO吞吐率。
表分区的策略:
目前在MySql中支持四种表分区的方式,分别为HASH、RANGE、LIST及KEY,当然在其它的类型数据库中,分区的实现方式略有不同,但是分区的思想原理是相同,具体如下。
Hash:HASH分区主要用来确保数据在预先确定数目的分区中平均分布,而在RANGE和LIST分区中,必须明确指定一个给定的列值或列值集合应该保存在哪个分区中,而在HASH分区中,MySQL自动完成这些工作,你所要做的只是基于将要被哈希的列值指定一个列值或表达式,以及指定被分区的表将要被分割成的分区数量。
CREATE TABLE t_product_item ( id int(7) not null, title varchar(40) not null, subtitle varchar(60) null, price double not null, imgurl varchar(70) not null, producttype int(2) not null, createtime datetime not null )ENGINE=InnoDB DEFAULT CHARSET=utf8 PARTITION BY HASH(YEAR(createtime)) //指定Hash的列值或表达式 PARTITIONS 10 //指定分区数量
Range:基于属于一个给定连续区间的列值,把多行分配给同一个分区,这些区间要连续且不能相互重叠,使用VALUES LESS THAN操作符来进行定义。
CREATE TABLE t_product_item ( id int(7) not null, title varchar(40) not null, subtitle varchar(60) null, price double not null, imgurl varchar(70) not null, producttype int(2) not null, createtime datetime not null )ENGINE=InnoDB DEFAULT CHARSET=utf8 PARTITION BY RANGE(producttype) ( //指定producttype作为range划分的列,并对值进行区域划分 PARTITIONP0 VALUES LESS THAN(2), PARTITIONP1 VALUES LESS THAN(4), PARTITIONp2 VALUES LESS THAN(6), PARTITIONp3 VALUES LESS THAN MAXVALUE );
List:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择分区的。LIST分区通过使用“PARTITION BY LIST(expr)”来实现,其中“expr” 是某列值或一个基于某个列值、并返回一个整数值的表达式,然后通过“VALUES IN (value_list)”的方式来定义每个分区,其中“value_list”是一个通过逗号分隔的整数列表。
CREATE TABLE t_product_item ( id int(7) not null, title varchar(40) not null, subtitle varchar(60) null, price double not null, imgurl varchar(70) not null, producttype int(2) not null, createtime datetime not null )ENGINE=InnoDB DEFAULT CHARSET=utf8 PARTITION BY LIST(producttype) ( //利用枚举出列值或表达式-->整型集合 PARTITIONP0 VALUES IN (0,1), //利用IN进行分区 PARTITIONP1 VALUES IN (2,3), PARTITIONP2 VALUES IN (4,5), PARTITIONP3 VALUES IN (6,7,8,9,10,11,12) )
Key:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL 服务器提供其自身的哈希函数。必须有一列或多列包含整数值。
表分区的注意:
1、引擎的统一
在对同一个表进行分区时,必须保证数据表的引擎相同,比如:不能对一个分区的表为InnoDB,而另一个分区的引擎为MySIAM。
2、分区关联性
在对数据表分区时,不能只对数据进行分区,需要连同其对应的索引等属性一同分区动作,某种程度上可以保持数据属性的完整。
3、分区的级别
对表进行分区之后,如果某个分区中的数据量依然很大或是增长迅速,那么你同样可以再进行子分区操作,将该数据再分区到其它分区中。另外,如果在一个分区中使用了子分区,那么其它的子分区也必须定义。
4、LIST分区
LIST分区没有类似如“VALUESLESS THAN MAXVALUE”这样的包含其他值在内的定义。将要匹配的任何值都必须在值列表中找到。
5、Linear线性
分区策略KEY和HASH都支持使用线性LINEAR的算法,也就是分区的编号是通过2的幂(powers-of-two)算法得到,而不是通过模数算法。
- 垂直分表、水平分表
垂直分表
1.减少记录的字段可使内存加载更多行数据,有利于查询。
2.受限于操作系统中的文件大小限制。
切分原则:把不常用或业务逻辑不紧密或存储内容比较多的字段分到新的表中可使表存储更多数据。另外垂直分割可以使得数据行变小,一个数据页就能存放更多的数据,在查询时就会减少I/O次数。其缺点是需要管理冗余列,查询所有数据需要join操作。
水平分表
1.随着数据量的增大,table行数巨大,查询的效率越来越低。表很大,分割后可以降低在查询时需要读的数据和索引的页数,同时也降低了索引的层数,提高查询速度。
2.同样受限于操作系统中的文件大小限制,数据量不能无限增加,当到达一定容量时,需要水平切分以降低单表(文件)的大小。
切分原则:
增量区间或散列或其他业务逻辑。使用哪种切分方法要根据实际业务逻辑判断:比如对表的访问多是近期产生的新数据,历史数据访问较少,可以考虑根据时间增量把数据按照一定时间段(比如每年)切分。如果对表的访问较均匀,没有明显的热点区域,则可以考虑用范围(比如每500w一个表)或普通Hash或一致性Hash来切分。
全局主键问题:原本依赖数据库生成主键(比如自增)的表在拆分后需要自己实现主键的生成,因为一般拆分规则是建立在主键上的(拆分后仍然要保证主键在全局的唯一性),所以在插入新数据时需要确定主键后才能找到存储的表。