单机数据库优化

数据库优化有不少能够讲,按照支撑的数据量来分能够分为两个阶段:单机数据库和分库分表,前者通常能够支撑500W或者10G之内的数据,超过这个值则须要考虑分库分表。另外,通常大企业面试每每会从单机数据库问起,一步一步问到分库分表,中间会穿插不少数据库优化的问题。本文试图描述单机数据库优化的一些实践,数据库基于mysql,若有不合理的地方,欢迎指正。



一、表结构优化


在开始作一个应用的时候,数据库的表结构设计每每会影响应用后期的性能,特别是用户量上来了之后的性能。所以,表结构优化是一个很重要的步骤。


1.一、字符集


通常来讲尽可能选择UTF-8,虽然在存中午的时候GBK比UTF-8使用的存储空间少,可是UTF-8兼容各国语言,其实咱们没必要为了这点存储空间而牺牲了扩展性。事实上,后期若是要从GBK转为UTF-8所要付出的代价是很高的,须要进行数据迁移,而存储空间彻底能够用花钱扩充硬盘来解决。


1.二、主键


在使用mysql的innodb的时候,innodb的底层存储模型是B+树,它使用主键做为聚簇索引,使用插入的数据做为叶子节点,经过主键能够很快找到叶子节点,从而快速获取记录。所以在设计表的时候须要增长一个主键,并且最好要自增。由于自增主键可让插入的数据按主键顺序插入到底层的B+树的叶子节点中,因为是按序的,这种插入几乎不须要去移动已有的其它数据,因此插入效率很高。若是主键不是自增的,那么每次主键的值近似随机,这时候就有可能须要移动大量数据来保证B+树的特性,增长了没必要要的开销。


1.三、字段


1.3.一、建了索引的字段必须加上not null约束,而且设置default值


1.3.二、不建议使用float、double来存小数,防止精度损失,建议使用decimal


1.3.三、不建议使用Text/blob来保存大量数据,由于对大文本的读写会形成比较大的I/O开销,同时占用mysql的缓存,高并发下会极大的下降数据库的吞吐量,建议将大文本数据保存在专门的文件存储系统中,mysql中只保存这个文件的访问地址,好比博客文章能够保存在文件中,mysql中只保存文件的相对地址。


1.3.四、varchar类型长度建议不要超过8K。


1.3.五、时间类型建议使用Datetime,不要使用timestamp,虽然Datetime占用8个字节,而timestamp只占用4个字节,可是后者要保证非空,并且后者是对时区敏感的。


1.3.六、建议表中增长gmt_create和gmt_modified两个字段,用来记录数据建立的修改时间。这两个字段创建的缘由是方便查问题。


1.四、索引建立


1.4.一、这个阶段因为对业务并不了解,因此尽可能不要盲目加索引,只为一些必定会用到索引的字段加普通索引。


1.4.二、建立innodb单列索引的长度不要超过767bytes,若是超过会用前255bytes做为前缀索引


1.4.三、建立innodb组合索引的各列索引长度不要超过767bytes,一共加起来不要超过3072bytes


二、SQL优化


通常来讲sql就那么几种:基本的增删改查,分页查询,范围查询,模糊搜索,多表链接


2.一、基本查询


通常查询须要走索引,若是没有索引建议修改查询,把有索引的那个字段加上,若是因为业务场景无法使用这个字段,那么须要看这个查询调用量大不大,若是大,好比天天调用10W+,这就须要新增索引,若是不大,好比天天调用100+,则能够考虑保持原样。另外,select * 尽可能少用,用到什么字段就在sql语句中加什么,没必要要的字段就别查了,浪费I/O和内存空间。


2.二、高效分页


limit m,n其实质就是先执行limit m+n,而后从第m行取n行,这样当limit翻页越日后翻m越大,性能越低。好比


select * from A limit 100000,10,这种sql语句的性能是不好的,建议改为下面的版本:


selec id,name,age from A where id >=(select id from A limit 100000,1) limit 10

2.三、范围查询


范围查询包括between、大于、小于以及in。Mysql中的in查询的条件有数量的限制,若数量较小能够走索引查询,若数量较大,就成了全表扫描了。而between、大于、小于等,这些查询不会走索引,因此尽可能放在走索引的查询条件以后。


2.四、模糊查询like


使用 like %name%这样的语句是不会走索引的,至关于全表扫描,数据量小的时候不会有太大的问题,数据量大了之后性能会降低的很厉害,建议数据量大了之后使用搜索引擎来代替这种模糊搜索,实在不行也要在模糊查询前加个能走索引的条件。


2.五、多表链接


子查询和join均可以实如今多张表之间取数据,可是子查询性能较差,建议将子查询改为join。对于mysql的join,它用的是Nested Loop Join算法,也就是经过前一个表查询的结果集去后一个表中查询,好比前一个表的结果集是100条数据,后一个表有10W数据,那么就须要在100*10W的数据集合中去过滤获得最终的结果集。所以,尽可能用小结果集的表去和大表作join,同时在join的字段上创建索引,若是建不了索引,就须要设置足够大的join buffer size。若是以上的技巧都没法解决join所带来的性能降低的问题,那干脆就别用join了,将一次join查询拆分红两次简单查询。另外,多表链接尽可能不要超过三张表,超过三张表通常来讲性能会不好,建议拆分sql。


三、数据库链接池优化


数据库链接池本质上是一种缓存,它是一种抗高并发的手段。数据库链接池优化主要是对参数进行优化,通常咱们使用DBCP链接池,它的具体参数以下:


3.1  initialSize


初始链接数,这里的初始指的是第一次getConnection的时候,而不是应用启动的时候。初始值能够设置为并发量的历史平均值


3.二、minIdle


最小保留的空闲链接数。DBCP会在后台开启一个回收空闲链接的线程,当该线程进行空闲链接回收的时候,会保留minIdle个链接数。通常设置为5,并发量实在很小能够设置为1.


3.三、maxIdle


最大保留的空闲链接数,按照业务并发高峰设置。好比并发高峰为20,那么当高峰过去后,这些链接不会立刻被回收,若是过一小段时间又来一个高峰,那么链接池就能够复用这些空闲链接而不须要频繁建立和关闭链接。


3.四、maxActive


最大活跃链接数,按照能够接受的并发极值设置。好比单机并发量可接受的极值是100,那么这个maxActive设置成100后,就只能同时为100个请求服务,多余的请求会在最大等待时间以后被抛弃。这个值必须设置,能够防止恶意的并发攻击,保护数据库。


3.五、maxWait


获取链接的最大等待时间,建议设置的短一点,好比3s,这样可让请求快速失败,由于一个请求在等待获取链接的时候,线程是不能够被释放的,而单机的线程并发量是有限的,若是这个时间设置的过长,好比网上建议的60s,那么这个线程在这60s内是没法被释放的,只要这种请求一多,应用的可用线程就少了,服务就变得不可用了。


3.六、minEvictableIdleTimeMillis


链接保持空闲而不被回收的时间,默认30分钟。


3.七、validationQuery


用于检测链接是否有效的sql语句,通常是一条简单的sql,建议设置


3.八、testOnBorrow


申请链接的时候对链接进行检测,不建议开启,严重影响性能


3.九、testOnReturn


归还链接的时候对链接进行检测,不建议开启,严重影响性能


3.十、testWhileIdle


开启了之后,后台清理链接的线程会没隔一段时间对空闲链接进行validateObject,若是链接失效则会进行清除,不影响性能,建议开启


3.十一、numTestsPerEvictionRun


表明每次检查连接的数量,建议设置和maxActive同样大,这样每次能够有效检查全部的连接。


3.十二、预热链接池


对于链接池,建议在启动应用的时候进行预热,在还未对外提供访问以前进行简单的sql查询,让链接池充满必要的链接数。


四、索引优化


当数据量增长到必定程度后,靠sql优化已经没法提高性能了,这时候就须要祭出大招:索引。索引有三级,通常来讲掌握这三级就足够了,另外,对于创建索引的字段,须要考虑其选择性。


4.一、一级索引


在where后面的条件上创建索引,单列能够创建普通索引,多列则创建组合索引。组合索引须要注意最左前缀原则。


4.二、二级索引


若是有被order by或者group by用到的字段,则能够考虑在这个字段上建索引,这样一来,因为索引自然有序,能够避免order by以及group by所带来的排序,从而提升性能。


4.三、三级索引


若是上面两招还不行,那么就把所查询的字段也加上索引,这时候就造成了所谓的索引覆盖,这样作能够减小一次I/O操做,由于mysql在查询数据的时候,是先查主键索引,而后根据主键索引去查普通索引,而后根据普通索引去查相对应的记录。若是咱们所须要的记录在普通索引里都有,那就不须要第三步了。固然,这种建索引的方式比较极端,不适合通常场景。


4.四、索引的选择性


在创建索引的时候,尽可能在选择性高的字段上创建。什么是选择性高呢?所谓选择性高就是经过这个字段查出来的数据量少,好比按照名字查一我的的信息,查出来的数据量通常会不多,而按照性别查则可能会把数据库一半的数据都查出来,因此,名字是一个选择性高的字段,而性别是个选择性低的字段。


五、历史数据归档


当数据量到了一年增长500W条的时候,索引也无能为力,这时候通常的思路都是考虑分库分表。若是业务没有爆发式增加,可是数据的确在缓慢增长,则能够不考虑分库分表这种复杂的技术手段,而是进行历史数据归档。咱们针对生命周期已经完结的历史数据,好比6个月以前的数据,进行归档。咱们可使用quartz的调度任务在凌晨定时将6个月以前的数据查出来,而后存入远程的hbase服务器。固然,咱们也须要提供历史数据的查询接口,以备不时之需。mysql

相关文章
相关标签/搜索