当前位置: 首页 > news >正文

湖南省网站网推app下载

湖南省网站,网推app下载,成都前端培训机构,教新手做网站难吗#xff08;以下内容均来自于网络#xff0c;如果有版权限制#xff0c;请联系我0.0#xff09; Mysql存储千亿级的数据#xff0c;是一项非常大的挑战。Mysql单表可以存储10亿级的数据#xff0c;只是这个时候性能非常差#xff0c;项目中大量的实验证明#xff0c;M…以下内容均来自于网络如果有版权限制请联系我0.0 Mysql存储千亿级的数据是一项非常大的挑战。Mysql单表可以存储10亿级的数据只是这个时候性能非常差项目中大量的实验证明Mysql单表容量在500万左右性能处于最佳状态。 优化的顺序是: 第一优化你的sql和索引 第二加缓存 memcached,redis 第三以上都做了后还是慢就做主从复制或主主复制读写分离可以在应用层做效率高也可以用三方工具第三方工具推荐360的atlas,其它的要么效率不高要么没人维护 第四如果以上都做了还是慢不要想着去做切分mysql自带分区表先试试这个对你的应用是透明的无需更改代码,但是sql语句是需要针对分区表做优化的sql条件中要带上分区条件的列从而使查询定位到少量的分区上否则就会扫描全部分区另外分区表还有一些坑在这里就不多说了 第五如果以上都做了那就先做垂直拆分其实就是根据你模块的耦合度将一个大的系统分为多个小的系统也就是分布式系统 第六才是水平切分针对数据量大的表这一步最麻烦最能考验技术水平要选择一个合理的sharding key,为了有好的查询效率表结构也要改动做一定的冗余应用也要改sql中尽量带sharding key将数据定位到限定的表上去查而不是扫描全部的表 mysql数据库一般都是按照这个步骤去演化的成本也是由低到高 一、查询优化 1、创建索引 有关索引的博客https://blog.csdn.net/fenglepeng/article/details/103141756 最简单也是最常用的优化就是查询。因为对于CRUD操作read操作是占据了绝大部分的比例所以read的性能基本上决定了应用的性能。对于查询性能最常用的就是创建索引。经过测试2000万条记录每条记录200字节两列varchar类型的。当不使用索引的时候查询一条记录需要一分钟而当创建了索引的时候查询时间可以忽略。但是当你在已有数据上添加索引的时候则需要耗费非常大的时间。我插入2000万条记录之后再创建索引大约话费了几十分钟的样子。 创建索引的弊端和场合。虽然创建索引可以很大程度上优化查询的速度但是弊端也是很明显的。一个是在插入数据的时候创建索引也需要消耗部分的时间这就使得插入性能在一定程度上降低另一个很明显的是数据文件变的更大。在列上创建索引的时候每条索引的长度是和你创建列的时候制定的长度相同的。比如你创建varchar(100)当你在该列上创建索引那么索引的长度则是102字节因为长度超过64字节则会额外增加2字节记录索引的长度。 从上图可以看到我在YCSB_KEY这一列(长度100)上创建了一个名字为index_ycsb_key的索引每条索引长度都为102想象一下当数据变的巨大无比的时候索引的大小也是不可以小觑的。而且从这也可以看出索引的长度和列类型的长度还不同比如varchar它是变长的字符类型(请看MySQL数据类型分析)实际存储长度是是实际字符的大小但是索引却是你声明的长度的大小。你创建列的时候声明100字节那么索引长度就是这个字节再加上2它不管你实际存储是多大。 除了创建索引需要消耗时间索引文件体积会变的越来越大之外创建索引也需要看的你存储数据的特征。当你存储数据很大一部分都是重复记录那这个时候创建索引是百害而无一利。所以当很多数据重复的时候索引带来的查询提升的效果是可以直接忽略的但是这个时候你还要承受插入数据的时候创建索引带来的性能消耗。 2、缓存的配置。 在MySQL中有多种多样的缓存有的缓存负责缓存查询语句也有的负责缓存查询数据。这些缓存内容客户端无法操作是由server端来维护的。它会随着你查询与修改等相应不同操作进行不断更新。通过其配置文件我们可以看到在MySQL中的缓存 在这里主要分析query cache它是主要用来缓存查询数据。当你想使用该cache必须把query_cache_size大小设置为非0。当设置大小为非0的时候server就会缓存每次查询返回的结果到下次相同查询server就直接从缓存获取数据而不是再执行查询。能缓存的数据量就和你的size大小设置有关所以当你设置的足够大数据可以完全缓存到内存速度就会非常之快。 但是query cache也有它的弊端。当你对数据表做任何的更新操作(update/insert/delete)等操作server为了保证缓存与数据库的一致性会强制刷新缓存数据导致缓存数据全部失效。所以当一个表格的更新数据表操作非常多的话query cache是不会起到查询提升的性能还会影响其他操作的性能。 3、slow_query_log分析。 其实对于查询性能提升最重要也是最根本的手段也是slow_query的设置。 当你设置slow_query_log为on的时候server端会对每次的查询进行记录当超过你设置的慢查询时间 (long_query_time)的时候就把该条查询记录到日志。而你对性能进行优化的时候就可以分析慢查询日志对慢查询的查询语句进行有目的的优化。可以通过创建各种索引可以通过分表等操作。 4、分库分表 分库分表应该算是查询优化的杀手锏了。上述各种措施在数据量达到一定等级之后能起到优化的作用已经不明显了。这个时候就必须对数据量进行分流。分流一般有分库与分表两种措施。而分表又有垂直切分与水平切分两种方式。 对于mysql其数据文件是以文件形式存储在磁盘上的。当一个数据文件过大的时候操作系统对大文件的操作就会比较麻烦与耗时而且有的操作系统就不支持大文件所以这个时候就必须分表了。另外对于mysql常用的存储引擎是Innodb它的底层数据结构是B树。当其数据文件过大的时候B树就会从层次和节点上比较多当查询一个节点的时候可能会查询很多层次而这必定会导致多次IO操作进行装载进内存肯定会耗时的。除此之外还有Innodb对于B树的锁机制。对每个节点进行加锁那么当更改表结构的时候这时候就会树进行加锁当表文件大的时候这可以认为是不可实现的。  所以综上我们就必须进行分表与分库的操作。 5、子查询优化 在查询中经常会用到子查询在子查询的时候一般使用in或者exist关键词。针对in和exist在查询的时候当数据量大到一定程度以后查询执行时间就差别比较大。但是为了避免此类情况出现最好的方式是使用join查询。因为在绝大多数情况下服务器对join的查询优化要远远高于子查询优化。在比较高的版本5.6mysql查询会自动把in查询优化成join查询就不会出现子查询比较慢的问题。有时候也可以采用distinct关键词来限制子查询的数量但是需要注意的是distinct很多时候会转化为group by这个时候就会出现一个 临时表就会出现copy数据到临时表的时延。 更多的子查询优化 请点击。 分表 如何进行分库分表目前互联网上有许多的版本比较知名的一些方案阿里的TDDLDRDS和cobar京东金融的sharding-jdbc民间组织的MyCAT360的Atlas美团的zebra其他比如网易58京东等公司都有自研的中间件。 这么多的分库分表中间件方案归总起来就两类client模式和proxy模式。 client模式 proxy模式 无论是client模式还是proxy模式。几个核心的步骤是一样的SQL解析重写路由执行结果归并。个人比较倾向于采用client模式它架构简单性能损耗也比较小运维成本低。 如何对业务类型进行分库分表。分库分表最重要的一步即sharding column的选取sharding column选择的好坏将直接决定整个分库分表方案最终是否成功。而sharding column的选取跟业务强相关。在我们的项目场景中sharding column无疑最好的选择是业务编号。通过业务编号将客户不同的绑定签约业务保存到不同的表里面去根据业务编号路由到相应的表中进行查询达到进一步优化sql的目的。 记录一次MySQL两千万数据的大表优化解决过程提供三种解决方案转 问题概述 使用阿里云rds for MySQL数据库就是MySQL5.6版本有个用户上网记录表6个月的数据量近2000万保留最近一年的数据量达到4000万查询速度极慢日常卡死。严重影响业务。 问题前提老系统当时设计系统的人大概是大学没毕业表设计和sql语句写的不仅仅是垃圾简直无法直视。原开发人员都已离职到我来维护。 方案概述 方案一优化现有mysql数据库。优点不影响现有业务源程序不需要修改代码成本最低。缺点有优化瓶颈数据量过亿就玩完了。 方案二升级数据库类型换一种100%兼容mysql的数据库。优点不影响现有业务源程序不需要修改代码你几乎不需要做任何操作就能提升数据库性能缺点多花钱 方案三一步到位大数据解决方案更换newsql/nosql数据库。优点扩展性强成本低没有数据容量瓶颈缺点需要修改源程序代码 以上三种方案按顺序使用即可数据量在亿级别一下的没必要换nosql开发成本太高。三种方案我都试了一遍而且都形成了落地解决方案 方案一详细说明优化现有mysql数据库 跟阿里云数据库大佬电话沟通 and Google解决方案 and 问群里大佬总结如下都是精华 1.数据库设计和表创建时就要考虑性能 mysql数据库本身高度灵活造成性能不足严重依赖开发人员能力。也就是说开发人员能力高则mysql性能高。这也是很多关系型数据库的通病所以公司的dba通常工资巨高。 设计表时要注意 表字段避免null值出现null值很难查询优化且占用额外的索引空间推荐默认数字0代替null。尽量使用INT而非BIGINT如果非负则加上UNSIGNED这样数值容量会扩大一倍当然能使用TINYINT、SMALLINT、MEDIUM_INT更好。使用枚举或整数代替字符串类型尽量使用TIMESTAMP而非DATETIME单表不要有太多字段建议在20以内用整型来存IP 索引 索引并不是越多越好要根据查询有针对性的创建考虑在WHERE和ORDER BY命令上涉及的列建立索引可根据EXPLAIN来查看是否用了索引还是全表扫描应尽量避免在WHERE子句中对字段进行NULL值判断否则将导致引擎放弃使用索引而进行全表扫描值分布很稀少的字段不适合建索引例如性别这种只有两三个值的字段字符字段只建前缀索引字符字段最好不要做主键不用外键由程序保证约束尽量不用UNIQUE由程序保证约束使用多列索引时主意顺序和查询条件保持一致同时删除不必要的单列索引 简言之就是使用合适的数据类型选择合适的索引 2.sql的编写需要注意优化 使用limit对查询结果的记录进行限定避免select *将需要查找的字段列出来使用连接join来代替子查询拆分大的delete或insert语句可通过开启慢查询日志来找出较慢的SQL不做列运算SELECT id WHERE age 1 10任何对列的操作都将导致表扫描它包括数据库教程函数、计算表达式等等查询时要尽可能将操作移至等号右边sql语句尽可能简单一条sql只能在一个cpu运算大语句拆小语句减少锁时间一条大sql可以堵死整个库OR改写成INOR的效率是n级别IN的效率是log(n)级别in的个数建议控制在200以内不用函数和触发器在应用程序实现避免%xxx式查询少用JOIN使用同类型进行比较比如用123和123比123和123比尽量避免在WHERE子句中使用!或操作符否则将引擎放弃使用索引而进行全表扫描对于连续数值使用BETWEEN不用INSELECT id FROM t WHERE num BETWEEN 1 AND 5列表数据不要拿全表要使用LIMIT来分页每页数量也不要太大 引擎 目前广泛使用的是MyISAM和InnoDB两种引擎 MyISAM引擎是MySQL 5.1及之前版本的默认引擎它的特点是 不支持行锁读取时对需要读到的所有表加锁写入时则对表加排它锁不支持事务不支持外键不支持崩溃后的安全恢复在表有读取查询的同时支持往表中插入新纪录支持BLOB和TEXT的前500个字符索引支持全文索引支持延迟更新索引极大提升写入性能对于不会进行修改的表支持压缩表极大减少磁盘空间占用 InnoDB在MySQL 5.5后成为默认索引它的特点是 支持行锁采用MVCC来支持高并发支持事务支持外键支持崩溃后的安全恢复不支持全文索引 总体来讲MyISAM适合SELECT密集型的表而InnoDB适合INSERT和UPDATE密集型的表 MyISAM速度可能超快占用存储空间也小但是程序要求事务支持故InnoDB是必须的故该方案无法执行放弃 3.分区 MySQL在5.1版引入的分区是一种简单的水平拆分用户需要在建表的时候加上分区参数对应用是透明的无需修改代码 对用户来说分区表是一个独立的逻辑表但是底层由多个物理子表组成实现分区的代码实际上是通过对一组底层表的对象封装但对SQL层来说是一个完全封装底层的黑盒子。MySQL实现分区的方式也意味着索引也是按照分区的子表定义没有全局索引 用户的SQL语句是需要针对分区表做优化SQL条件中要带上分区条件的列从而使查询定位到少量的分区上否则就会扫描全部分区可以通过EXPLAIN PARTITIONS来查看某条SQL语句会落在那些分区上从而进行SQL优化我测试查询时不带分区条件的列也会提高速度故该措施值得一试。 分区的好处是 可以让单表存储更多的数据分区表的数据更容易维护可以通过清楚整个分区批量删除大量数据也可以增加新的分区来支持新插入的数据。另外还可以对一个独立分区进行优化、检查、修复等操作部分查询能够从查询条件确定只落在少数分区上速度会很快分区表的数据还可以分布在不同的物理设备上从而搞笑利用多个硬件设备可以使用分区表赖避免某些特殊瓶颈例如InnoDB单个索引的互斥访问、ext3文件系统的inode锁竞争可以备份和恢复单个分区 分区的限制和缺点 一个表最多只能有1024个分区如果分区字段中有主键或者唯一索引的列那么所有主键列和唯一索引列都必须包含进来分区表无法使用外键约束NULL值会使分区过滤无效所有分区必须使用相同的存储引擎 分区的类型 RANGE分区基于属于一个给定连续区间的列值把多行分配给分区LIST分区类似于按RANGE分区区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择HASH分区基于用户定义的表达式的返回值来进行选择的分区该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL中有效的、产生非负整数值的任何表达式KEY分区类似于按HASH分区区别在于KEY分区只支持计算一列或多列且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值 我首先根据月份把上网记录表RANGE分区了12份查询效率提高6倍左右效果不明显故换id为HASH分区分了64个分区查询速度提升显著。问题解决 4.分表 分表就是把一张大表按照如上过程都优化了还是查询卡死那就把这个表分成多张表把一次查询分成多次查询然后把结果组合返回给用户。 分表分为垂直拆分和水平拆分通常以某个字段做拆分项。比如以id字段拆分为100张表 表名为 tableName_id%100 但分表需要修改源程序代码会给开发带来大量工作极大的增加了开发成本故只适合在开发初期就考虑到了大量数据存在做好了分表处理不适合应用上线了再做修改成本太高而且选择这个方案都不如选择我提供的第二第三个方案的成本低故不建议采用。 5.分库 把一个数据库分成多个建议做个读写分离就行了真正的做分库也会带来大量的开发成本得不偿失不推荐使用。 方案二详细说明升级数据库换一个100%兼容mysql的数据库 mysql性能不行那就换个。为保证源程序代码不修改保证现有业务平稳迁移故需要换一个100%兼容mysql的数据库。 开源选择 1.tiDB https://github.com/pingcap/tidb 2.Cubrid https://www.cubrid.org/ 3.开源数据库会带来大量的运维成本且其工业品质和MySQL尚有差距有很多坑要踩如果你公司要求必须自建数据库那么选择该类型产品。 云数据选择 阿里云POLARDB https://www.aliyun.com/product/polardb?spma2c4g.11174283.cloudEssentials.47.7a984b5cS7h4wH 官方介绍语POLARDB 是阿里云自研的下一代关系型分布式云原生数据库100%兼容MySQL存储容量最高可达 100T性能最高提升至 MySQL 的 6 倍。POLARDB 既融合了商业数据库稳定、可靠、高性能的特征又具有开源数据库简单、可扩展、持续迭代的优势而成本只需商用数据库的 1/10。 我开通测试了一下支持免费mysql的数据迁移无操作成本性能提升在10倍左右价格跟rds相差不多是个很好的备选解决方案 阿里云OcenanBase 淘宝使用的扛得住双十一性能卓著但是在公测中我无法尝试但值得期待 阿里云HybridDB for MySQL (原PetaData) https://www.aliyun.com/product/petadata?spma2c4g.11174283.cloudEssentials.54.7a984b5cS7h4wH 官方介绍云数据库HybridDB for MySQL 原名PetaData是同时支持海量数据在线事务OLTP和在线分析OLAP的HTAPHybrid Transaction/Analytical Processing关系型数据库。 我也测试了一下是一个olap和oltp兼容的解决方案但是价格太高每小时高达10块钱用来做存储太浪费了适合存储和分析一起用的业务。 腾讯云DCDB https://cloud.tencent.com/product/dcdb_for_tdsql 官方介绍DCDB又名TDSQL一种兼容MySQL协议和语法支持自动水平拆分的高性能分布式数据库——即业务显示为完整的逻辑表数据却均匀的拆分到多个分片中每个分片默认采用主备架构提供灾备、恢复、监控、不停机扩容等全套解决方案适用于TB或PB级的海量数据场景。 腾讯的我不喜欢用不多说。原因是出了问题找不到人线上问题无法解决头疼但是他价格便宜适合超小公司玩玩。 方案三详细说明去掉mysql换大数据引擎处理数据 开源解决方案 hadoop家族。hbase/hive怼上就是了。但是有很高的运维成本一般公司是玩不起的没十万投入是不会有很好的产出的 云解决方案 这个就比较多了也是一种未来趋势大数据由专业的公司提供专业的服务小公司或个人购买服务大数据就像水/电等公共设施一样存在于社会的方方面面。国内做的最好的当属阿里云。 我选择了阿里云的MaxCompute配合DataWorks使用超级舒服按量付费成本极低。 MaxCompute可以理解为开源的Hive提供sql/mapreduce/ai算法/python脚本/shell脚本等方式操作数据数据以表格的形式展现以分布式方式存储采用定时任务和批处理的方式处理数据。DataWorks提供了一种工作流的方式管理你的数据处理任务和调度监控。 当然你也可以选择阿里云hbase等其他产品我这里主要是离线处理故选择MaxCompute基本都是图形界面操作大概写了300行sql费用不超过100块钱就解决了数据处理问题。 千万条数据优化建议30条 1、对查询进行优化、应尽量避免全表扫描、首先应考虑在 where 及 order by 涉及的列上建立索引。 2、应尽量避免在 where 子句中对字段进行 null 值判断、否则将导致引擎放弃使用索引而进行全表扫描、如 select id from t where num is null; --可以在num上设置默认值0、确保表中num列没有null值、然后这样查询 select id from t where num0; 3、应尽量避免在 where 子句中使用!或操作符、否则将引擎放弃使用索引而进行全表扫描。 4、应尽量避免在 where 子句中使用 or 来连接条件、否则将导致引擎放弃使用索引而进行全表扫描、如 select id from t where num10 or num20 --可以这样查询 select id from t where num10 union all select id from t where num20; 5、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; 6、下面的查询也将导致全表扫描 select id from t where name like %abc%; --若要提高效率、可以考虑全文检索。 7、如果在 where 子句中使用参数、也会导致全表扫描。因为SQL只有在运行时才会解析局部变量、但优化程序不能将访问计划的选择推迟到运行时它必须在编译时进行选择。然而、如果在编译时建立访问计划、变量的值还是未知的、因而无法作为索引选择的输入项。如下面语句将进行全表扫描 select id from t where numnum --可以改为强制查询使用索引 select id from t with(index(索引名)) where num num ; 8、应尽量避免在 where 子句中对字段进行表达式操作、这将导致引擎放弃使用索引而进行全表扫描。如 select id from t where num/2100; --应改为: select id from t where num100*2; 9、应尽量避免在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 createdate2005-11-30 and createdate2005-12-1; 10、不要在 where 子句中的“”左边进行函数、算术运算或其他表达式运算、否则系统将可能无法正确使用索引。 11、在使用索引字段作为条件时、如果该索引是复合索引、那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引、否则该索引将不会被使用、并且应尽可能的让字段顺序与索引顺序相一致。 12、不要写一些没有意义的查询、如需要生成一个空表结构 select col1,col2 into #t from t where 10;  --这类代码不会返回任何结果集、但是会消耗系统资源的、应改成这样 create table #t(...); 13、很多时候用 exists 代替 in 是一个好的选择 select num from a where num in(select num from b); --用下面的语句替换 select num from a where exists(select 1 from b where numa.num); 14、并不是所有索引对查询都有效、SQL是根据表中数据来进行查询优化的、当索引列有大量数据重复时、SQL查询可能不会去利用索引、如一表中有字段sex、male、female几乎各一半、那么即使在sex上建了索引也对查询效率起不了作用。 15、索引并不是越多越好、索引固然可以提高相应的 select 的效率、但同时也降低了 insert 及 update 的效率、因为 insert 或 update 时有可能会重建索引、所以怎样建索引需要慎重考虑、视具体情况而定。一个表的索引数最好不要超过6个、若太多则应考虑一些不常使用到的列上建的索引是否有必要。 16、应尽可能的避免更新 clustered 索引数据列、因为 clustered 索引数据列的顺序就是表记录的物理存储顺序、一旦该列值改变将导致整个表记录的顺序的调整、会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列、那么需要考虑是否应将该索引建为 clustered 索引。 17、尽量使用数字型字段、若只含数值信息的字段尽量不要设计为字符型、这会降低查询和连接的性能、并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符、而对于数字型而言只需要比较一次就够了。 18、尽可能的使用 varchar/nvarchar 代替 char/nchar 、因为首先变长字段存储空间小、可以节省存储空间、其次对于查询来说、在一个相对较小的字段内搜索效率显然要高些。 19、任何地方都不要使用 select * from t 、用具体的字段列表代替“*”、不要返回用不到的任何字段。 20、尽量使用表变量来代替临时表。如果表变量包含大量数据、请注意索引非常有限只有主键索引。 21、避免频繁创建和删除临时表、以减少系统表资源的消耗。 22、临时表并不是不可使用、适当地使用它们可以使某些例程更有效、例如、当需要重复引用大型表或常用表中的某个数据集时。但是、对于一次性事件、最好使用导出表。 23、在新建临时表时、如果一次性插入数据量很大、那么可以使用 select into 代替 create table、避免造成大量 log 、以提高速度如果数据量不大、为了缓和系统表的资源、应先create table、然后insert。 24、如果使用到了临时表、在存储过程的最后务必将所有的临时表显式删除、先 truncate table 、然后 drop table 、这样可以避免系统表的较长时间锁定。 25、尽量避免使用游标、因为游标的效率较差、如果游标操作的数据超过1万行、那么就应该考虑改写。 26、使用基于游标的方法或临时表方法之前、应先寻找基于集的解决方案来解决问题、基于集的方法通常更有效。 27、与临时表一样、游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法、尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许、基于游标的方法和基于集的方法都可以尝试一下、看哪一种方法的效果更好。 28、在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON 、在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。 29、尽量避免大事务操作、提高系统并发能力。 30、尽量避免向客户端返回大数据量、若数据量过大、应该考虑相应需求是否合理。 MySQL 性能优化的建议 1. 为查询缓存优化你的查询 大多数的MySQL服务器都开启了查询缓存。这是提高性最有效的方法之一而且这是被MySQL的数据库引擎处理的。当有很多相同的查询被执行了多次的时候这些查询结果会被放到一个缓存中这样后续的相同的查询就不用操作表而直接访问缓存结果了。这里最主要的问题是对于程序员来说这个事情是很容易被忽略的。因为我们某些查询语句会让MySQL不使用缓存。请看下面的示例 // 查询缓存不开启 $r  mysql_query( SELECT username FROM user WHERE signup_date CURDATE() );// 开启查询缓存 $today  date ( Y-m-d ); $r  mysql_query( SELECT username FROM user WHERE signup_date $today ); 上面两条SQL语句的差别就是 CURDATE() MySQL的查询缓存对这个函数不起作用。所以像 NOW() 和 RAND() 或是其它的诸如此类的SQL函数都不会开启查询缓存因为这些函数的返回是会不定的易变的。所以你所需要的就是用一个变量来代替MySQL的函数从而开启缓存。 2. EXPLAIN 你的 SELECT 查询 EXPLAIN 博客https://blog.csdn.net/fenglepeng/article/details/103392319 使用 EXPLAIN 关键字可以让你知道MySQL是如何处理你的SQL语句的。这可以帮你分析你的查询语句或是表结构的性能瓶颈。 EXPLAIN 的查询结果还会告诉你你的索引主键被如何利用的你的数据表是如何被搜索和排序的……等等。 3. 当只要一行数据时使用 LIMIT 1 当你查询表的有些时候你已经知道结果只会有一条结果但因为你可能需要去fetch游标或是你也许会去检查返回的记录数。在这种情况下加上 LIMIT 1 可以增加性能。这样一样MySQL数据库引擎会在找到一条数据后停止搜索而不是继续往后查少下一条符合记录的数据。下面的示例只是为了找一下是否有“中国”的用户很明显后面的会比前面的更有效率。第一条中是Select *第二条是Select 1 // 没有效率的 $r mysql_query( SELECT * FROM user WHERE country China ); if (mysql_num_rows( $r ) 0) {// ... }// 有效率的 $r mysql_query( SELECT 1 FROM user WHERE country China LIMIT 1 ); if (mysql_num_rows( $r ) 0) {// ... } 4. 为搜索字段建索引 索引并不一定就是给主键或是唯一的字段。如果在你的表中有某个字段你总要会经常用来做搜索那么请为其建立索引吧。 从上图你可以看到那个搜索字串 “last_name LIKE ‘a%’”一个是建了索引一个是没有索引性能差了4倍左右。另外你应该也需要知道什么样的搜索是不能使用正常的索引的。例如当你需要在一篇大的文章中搜索一个词时如 “WHERE post_content LIKE ‘%apple%’”索引可能是没有意义的。你可能需要使用MySQL 全文索引 或是自己做一个索引比如说搜索关键词或是Tag什么的 5. 在Join表的时候使用相当类型的列并将其索引 如果你的应用程序有很多 JOIN 查询你应该确认两个表中Join的字段是被建过索引的。这样MySQL内部会启动为你优化Join的SQL语句的机制。而且这些被用来Join的字段应该是相同的类型的。例如如果你要把 DECIMAL 字段和一个 INT 字段Join在一起MySQL就无法使用它们的索引。对于那些STRING类型还需要有相同的字符集才行。两个表的字符集有可能不一样 // 在state中查找company $r  mysql_query(SELECT company_name FROM usersLEFT JOIN companies ON (users.state companies.state)WHERE users.id  $user_id );// 两个 state 字段应该是被建过索引的而且应该是相当的类型相同的字符集。 千万不要 ORDER BY RAND() 想打乱返回的数据行随机挑一个数据真不知道谁发明了这种用法但很多新手很喜欢这样用。但你确不了解这样做有多么可怕的性能问题。如果你真的想把返回的数据行打乱了你有N种方法可以达到这个目的。这样使用只让你的数据库的性能呈指数级的下降。这里的问题是MySQL会不得 不去执行RAND()函数很耗CPU时间而且这是为了每一行记录去记行然后再对其排序。就算是你用了Limit 1也无济于事因为要排序 下面的示例是随机挑一条记录 // 千万不要这样做 $r  mysql_query( SELECT username FROM user ORDER BY RAND() LIMIT 1 );// 这要会更好 $r  mysql_query( SELECT count(*) FROM user ); $d  mysql_fetch_row( $r ); $rand  mt_rand(0, $d [0] - 1); $r  mysql_query( SELECT username FROM user LIMIT $rand, 1 ); 7. 避免 SELECT *。 从数据库里读出越多的数据那么查询就会变得越慢。并且如果你的数据库服务器和WEB服务器是两台独立的服务器的话这还会增加网络传输的负载。所以你应该养成一个需要什么就取什么的好的习惯。 // 不推荐 $r  mysql_query( SELECT * FROM user WHERE user_id 1 ); $d  mysql_fetch_assoc( $r ); echo Welcome {$d[username]} ;// 推荐 $r  mysql_query( SELECT username FROM user WHERE user_id 1 ); $d  mysql_fetch_assoc( $r ); echo Welcome {$d[username]} ; 8.永远为每张表设置一个ID 我们应该为数据库里的每张表都设置一个ID做为其主键而且最好的是一个INT型的推荐使用UNSIGNED并设置上自动增加的 AUTO_INCREMENT标志。就算是你 users 表有一个主键叫 “email”的字段你也别让它成为主键。使用 VARCHAR 类型来当主键会使用得性能下降。另外在你的程序中你应该使用表的ID来构造你的数据结构。而且在MySQL数据引擎下还有一些操作需要使用主键在这些情况下主键的性能和设置变得非常重要比如集群分区……在这里只有一个情况是例外那就是“关联表”的“外键”也就是说这个表的主键通过若干个别的表的主键构成。我们把这个情况叫做“外键”。比 如有一个“学生表”有学生的ID有一个“课程表”有课程ID那么“成绩表”就是“关联表”了其关联了学生表和课程表在成绩表中学生ID和课 程ID叫“外键”其共同组成主键。 9. 使用 ENUM 而不是 VARCHAR ENUM 类型是非常快和紧凑的。在实际上其保存的是 TINYINT但其外表上显示为字符串。这样一来用这个字段来做一些选项列表变得相当的完美。 如果你有一个字段比如“性别”“国家”“民族”“状态”或“部门”你知道这些字段的取值是有限而且固定的那么你应该使用 ENUM 而不是 VARCHAR。 MySQL也有一个“建议”见第十条告诉你怎么去重新组织你的表结构。当你有一个 VARCHAR 字段时这个建议会告诉你把其改成 ENUM 类型。使用 PROCEDURE ANALYSE() 你可以得到相关的建议。 10. 从 PROCEDURE ANALYSE() 取得建议 PROCEDURE ANALYSE() 会让 MySQL 帮你去分析你的字段和其实际的数据并会给你一些有用的建议。只有表中有实际的数据这些建议才会变得有用因为要做一些大的决定是需要有数据作为基础 的。 例如如果你创建了一个 INT 字段作为你的主键然而并没有太多的数据那么PROCEDURE ANALYSE()会建议你把这个字段的类型改成 MEDIUMINT 。或是你使用了一个 VARCHAR 字段因为数据不多你可能会得到一个让你把它改成 ENUM 的建议。这些建议都是可能因为数据不够多所以决策做得就不够准。 在phpmyadmin里你可以在查看表时点击 “Propose table structure” 来查看这些建议 一定要注意这些只是建议只有当你的表里的数据越来越多时这些建议才会变得准确。一定要记住你才是最终做决定的人。 11. 尽可能的使用 NOT NULL 除非你有一个很特别的原因去使用 NULL 值你应该总是让你的字段保持 NOT NULL。这看起来好像有点争议请往下看。 首先问问你自己“Empty”和“NULL”有多大的区别如果是INT那就是0和NULL如果你觉得它们之间没有什么区别那么你就不要 使用NULL。你知道吗在 Oracle 里NULL 和 Empty 的字符串是一样的) 不要以为 NULL 不需要空间其需要额外的空间并且在你进行比较的时候你的程序会更复杂。 当然这里并不是说你就不能使用NULL了现实情况是很复杂的依然会有些情况下你需要使用NULL值。 下面摘自MySQL自己的文档 “NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.” 12. Prepared Statements Prepared Statements很像存储过程是一种运行在后台的SQL语句集合我们可以从使用 prepared statements 获得很多好处无论是性能问题还是安全问题。 Prepared Statements 可以检查一些你绑定好的变量这样可以保护你的程序不会受到“SQL注入式”攻击。当然你也可以手动地检查你的这些变量然而手动的检查容易出问题 而且很经常会被程序员忘了。当我们使用一些framework或是ORM的时候这样的问题会好一些。 在性能方面当一个相同的查询被使用多次的时候这会为你带来可观的性能优势。你可以给这些Prepared Statements定义一些参数而MySQL只会解析一次。 虽然最新版本的MySQL在传输Prepared Statements是使用二进制形势所以这会使得网络传输非常有效率。 当然也有一些情况下我们需要避免使用Prepared Statements因为其不支持查询缓存。但据说版本5.1后支持了。 在PHP中要使用prepared statements你可以查看其使用手册mysqli 扩展 或是使用数据库抽象层如 PDO . // 创建 prepared statement if ( $stmt  $mysqli -prepare( SELECT username FROM user WHERE state? )) {// 绑定参数$stmt -bind_param( s , $state );// 执行$stmt -execute();// 绑定结果$stmt -bind_result( $username );// 移动游标$stmt -fetch();printf( %s is from %s\n , $username , $state );$stmt -close(); } 正常的情况下当你在当你在你的脚本中执行一个SQL语句的时候你的程序会停在那里直到没这个SQL语句返回然后你的程序再往下继续执行。你可 以使用无缓冲查询来改变这个行为。13. 无缓冲的查询 关于这个事情在PHP的文档中有一个非常不错的说明 mysql_unbuffered_query() 函数 “mysql_unbuffered_query() sends the SQL query query to MySQL without automatically fetching and buffering the result rows as mysql_query() does. This saves a considerable amount of memory with SQL queries that produce large result sets, and you can start working on the result set immediately after the first row has been retrieved as you don’t have to wait until the complete SQL query has been performed.” 上面那句话翻译过来是说mysql_unbuffered_query() 发送一个SQL语句到MySQL而并不像mysql_query()一样去自动fethch和缓存结果。这会相当节约很多可观的内存尤其是那些会产生大 量结果的查询语句并且你不需要等到所有的结果都返回只需要第一行数据返回的时候你就可以开始马上开始工作于查询结果了。 然而这会有一些限制。因为你要么把所有行都读走或是你要在进行下一次的查询前调用 mysql_free_result()清除结果。而且 mysql_num_rows() 或 mysql_data_seek() 将无法使用。所以是否使用无缓冲的查询你需要仔细考虑。 14. 把IP地址存成 UNSIGNED INT 很多程序员都会创建一个 VARCHAR(15) 字段来存放字符串形式的IP而不是整形的IP。如果你用整形来存放只需要4个字节并且你可以有定长的字段。而且这会为你带来查询上的优势尤其是当 你需要使用这样的WHERE条件IP between ip1 and ip2。 我们必需要使用UNSIGNED INT因为 IP地址会使用整个32位的无符号整形。 而你的查询你可以使用 INET_ATON() 来把一个字符串IP转成一个整形并使用 INET_NTOA() 把一个整形转成一个字符串IP。在PHP中也有这样的函数 ip2long() 和 long2ip() 。 $r  UPDATE users SET ip INET_ATON({$_SERVER[REMOTE_ADDR]}) WHERE user_id $user_id ; 15. 固定长度的表会更快 如果表中的所有字段都是“固定长度”的整个表会被认为是 “static” 或 “fixed-length” 。 例如表中没有如下类型的字段 VARCHARTEXTBLOB。只要你包括了其中一个这些字段那么这个表就不是“固定长度静态表”了这样MySQL 引擎会用另一种方法来处理。 固定长度的表会提高性能因为MySQL搜寻得会更快一些因为这些固定的长度是很容易计算下一个数据的偏移量的所以读取的自然也会很快。而如果 字段不是定长的那么每一次要找下一条的话需要程序找到主键。 并且固定长度的表也更容易被缓存和重建。不过唯一的副作用是固定长度的字段会浪费一些空间因为定长的字段无论你用不用他都是要分配那么多 的空间。 使用“垂直分割”技术见下一条你可以分割你的表成为两个一个是定长的一个则是不定长的。 16. 垂直分割 “垂直分割”是一种把数据库中的表按列变成几张表的方法这样可以降低表的复杂度和字段的数目从而达到优化的目的。以前在银行做过项目见过 一张表有100多个字段很恐怖 示例一 在Users表中有一个字段是家庭地址这个字段是可选字段相比起而且你在数据库操作的时候除了个 人信息外你并不需要经常读取或是改写这个字段。那么为什么不把他放到另外一张表中呢 这样会让你的表有更好的性能大家想想是不是大量的时候我对于用户表来说只有用户ID用户名口令用户角色等会被经常使用。小一点的表总是会有 好的性能。 示例二  你有一个叫 “last_login” 的字段它会在每次用户登录时被更新。但是每次更新时会导致该表的查询缓存被清空。所以你可以把这个字段放到另一个表中这样就不会影响你对用户 ID用户名用户角色的不停地读取了因为查询缓存会帮你增加很多性能。 另外你需要注意的是这些被分出去的字段所形成的表你不会经常性地去Join他们不然的话这样的性能会比不分割时还要差而且会是极数级 的下降。 17. 拆分大的 DELETE 或 INSERT 语句 如果你需要在一个在线的网站上去执行一个大的 DELETE 或 INSERT 查询你需要非常小心要避免你的操作让你的整个网站停止相应。因为这两个操作是会锁表的表一锁住了别的操作都进不来了。 Apache 会有很多的子进程或线程。所以其工作起来相当有效率而我们的服务器也不希望有太多的子进程线程和数据库链接这是极大的占服务器资源的事情尤其是 内存。 如果你把你的表锁上一段时间比如30秒钟那么对于一个有很高访问量的站点来说这30秒所积累的访问进程/线程数据库链接打开的文件数可 能不仅仅会让你泊WEB服务Crash还可能会让你的整台服务器马上掛了。 所以如果你有一个大的处理你定你一定把其拆分使用 LIMIT 条件是一个好的方法。下面是一个示例 while (1) {//每次只做1000条mysql_query( DELETE FROM logs WHERE log_date 2009-11-01 LIMIT 1000 );if (mysql_affected_rows() 0) {// 没得可删了退出break ;}// 每次都要休息一会儿usleep(50000); } 18. 越小的列会越快 对于大多数的数据库引擎来说硬盘操作可能是最重大的瓶颈。所以把你的数据变得紧凑会对这种情况非常有帮助因为这减少了对硬盘的访问。 参看 MySQL 的文档 Storage Requirements 查看所有的数据类型。 如果一个表只会有几列罢了比如说字典表配置表那么我们就没有理由使用 INT 来做主键使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 会更经济一些。如果你不需要记录时间使用 DATE 要比 DATETIME 好得多。 当然你也需要留够足够的扩展空间不然你日后来干这个事你会死的很难看参看Slashdot 的例子 2009年11月06日一个简单的ALTER TABLE语句花了3个多小时因为里面有一千六百万条数据。 19. 选择正确的存储引擎 在 MySQL 中有两个存储引擎 MyISAM 和 InnoDB每个引擎都有利有弊。以前文章《MySQL: InnoDB 还是 MyISAM? 》讨论和这个事情。 MyISAM 适合于一些需要大量查询的应用但其对于有大量写操作并不是很好。甚至你只是需要update一个字段整个表都会被锁起来而别的进程就算是读进程都 无法操作直到读操作完成。另外MyISAM 对于 SELECT COUNT(*) 这类的计算是超快无比的。 InnoDB 的趋势会是一个非常复杂的存储引擎对于一些小的应用它会比 MyISAM 还慢。他是它支持“行锁” 于是在写操作比较多的时候会更优秀。并且他还支持更多的高级应用比如事务。 下面是MySQL的手册 target”_blank”MyISAM Storage EngineInnoDB Storage Engine 20. 使用一个对象关系映射器Object Relational Mapper 使用 ORM (Object Relational Mapper)你能够获得可靠的性能增涨。一个ORM可以做的所有事情也能被手动的编写出来。但是这需要一个高级专家。 ORM 的最重要的是“Lazy Loading”也就是说只有在需要的去取值的时候才会去真正的去做。但你也需要小心这种机制的副作用因为这很有可能会因为要去创建很多很多小的查 询反而会降低性能。 ORM 还可以把你的SQL语句打包成一个事务这会比单独执行他们快得多得多。 目前个人最喜欢的PHP的ORM是Doctrine 。 21. 小心“永久链接” “永久链接”的目的是用来减少重新创建MySQL链接的次数。当一个链接被创建了它会永远处在连接的状态就算是数据库操作已经结束了。而且自 从我们的Apache开始重用它的子进程后——也就是说下一次的HTTP请求会重用Apache的子进程并重用相同的 MySQL 链接。 PHP 手册mysql_pconnect() 在理论上来说这听起来非常的不错。但是从个人经验也是大多数人的上来说这个功能制造出来的麻烦事更多。因为你只有有限的链接数内存问 题文件句柄数等等。 而且Apache 运行在极端并行的环境中会创建很多很多的了进程。这就是为什么这种“永久链接”的机制工作地不好的原因。在你决定要使用“永久链接”之前你需要好好地 考虑一下你的整个系统的架构。 阿里巴巴JAVA编程规范之MySQL规范 (一) 建表规约 1. 【强制】表达是与否概念的字段必须使用is_xxx的方式命名数据类型是unsigned tinyint。 1表示是0表示否此规则同样适用于odps建表。 说明任何字段如果为非负数必须是unsigned。 解读从优化角度来讲应该按字段的用途来定义合适的类型。表达是与否用长度为1个字节的tinyint足以 2. 【强制】表名、字段名必须使用小写字母或数字禁止出现数字开头禁止两个下划线中间只出现数字。数据库字段名的修改代价很大因为无法进行预发布所以字段名称需要慎重考虑。 正例getter_admintask_configlevel3_name 反例GetterAdmintaskConfiglevel_3_name 说明MySQL 在 Windows 下不区分大小写但在 Linux 下默认是区分大小写。因此数据库名、 表名、字段名都不允许出现任何大写字母避免节外生枝。 解读Win环境下开发代码中用的表名是小写本地数据库用的是大写那么在win环境下没有问题但发布到linux环境会有问题。Linux下MySQL安装完后默认区分表名的大小写不区分列名的大小写 3. 【强制】表名不使用复数名词。 说明表名应该仅仅表示表里面的实体内容不应该表示实体数量对应于DO类名也是单数形式符合表达习惯。 4. 【强制】禁用保留字如desc、range、match、delayed等参考官方保留字。 5. 【强制】唯一索引名为uk_字段名普通索引名则为idx_字段名。 说明uk_即 unique keyidx_即index的简称。 6. 【强制】小数类型为decimal禁止使用float和double。 说明float和double在存储的时候存在精度损失的问题很可能在值的比较时得到不正确的结果。如果存储的数据范围超过decimal的范围建议将数据拆成整数和小数分开存储。 解读float和double都是浮点型而decimal是定点型。MySQL 浮点型和定点型可以用类型名称后加MD来表示M表示该值的总共长度D表示小数点后面的长度。FLOAT和DOUBLE在不指定精度时默认会按照实际的精度来显示而DECIMAL在不指定精度时默认整数为10小数为0。所以建议在定义表时定义MD。float和double在设置超过定义长度的数值时会自动四舍五入decimal会截断并给出一条警告。精度损失问题float和double类型的列在做sum计算时会丢失精度而decimal会精确计算。 7. 【强制】如果存储的字符串长度几乎相等使用CHAR定长字符串类型。 解读从优化角度来说如果一个表的所有字段都是定长的那么每一条数据也就是定长的数据库就可以直接计算出下一条数据的偏移量查询速度会更快。 8. 【强制】varchar是可变长字符串不预先分配存储空间长度不要超过5000如果存储长度大于此值定义字段类型为TEXT独立出来一张表用主键来对应避免影响其它字段索引效率。 解读MySQL5.0以上版本varchar最大可以存储65535字节数据内容开头用1-2个字节存储长度信息超过255时用两个字节所以最大65535。我们通常编码设置为U8每个字符最多占3个字节那么最大长度不能超过21845。若定义的时候超过上述限制则varchar字段会被强行转为text类型并产生warning。此外受MYSQL行长度限制影响MySQL要求一个行的定义长度不能超过65535。若定义的表长度超过这个值则提示ERROR 1118 (42000): Row size too large。数据库中定义的varchar(20)指的是20个字符。关于5000的建议由于通常定义的U8每个字符占3个字节那么5000字符需要15000个字节考虑行最大长度限制和别的列以及查询性能推荐5000。 9. 【强制】表必备三字段id, gmt_create, gmt_modified。 说明其中id必为主键类型为unsigned bigint、单表时自增、步长为1分表时改为从TDDL Sequence取值确保分表之间的全局唯一。gmt_create,gmt_modified的类型均为date_time类型。 10.【推荐】表的命名最好是加上“业务名称_表的作用”避免上云梯后再与其它业务表关联时有混淆。 正例tiger_task / tiger_reader / mpp_config 11.【推荐】库名与应用名称尽量一致。 12.【推荐】如果修改字段含义或对字段表示的状态追加时需要及时更新字段注释。 13.【推荐】字段允许适当冗余以提高性能但是必须考虑数据同步的情况。冗余字段应遵循 不是频繁修改的字段。不是varchar超长字段更不能是text字段。 正例各业务线经常冗余存储商品名称避免查询时需要调用IC服务获取。商品类目名称使用频率高字段长度短名称基本一成不变可在相关联的表中冗余存储类目名称避免关联查询。 14.【推荐】单表行数超过500万行或者单表容量超过2GB才推荐进行分库分表。 说明如果预计三年后的数据量根本达不到这个级别请不要在创建表时就分库分表。 反例某业务三年总数据量才2万行却分成1024张表问你为什么这么设计答分1024张表不是标配吗 15.【参考】合适的字符存储长度不但节约数据库表空间、节约索引存储更重要的是提升检索速度。 正例人的年龄用unsigned tinyint表示范围0-255人的寿命不会超过255岁海龟就必须是small int但如果是太阳的年龄就必须是int如果是所有恒星的年龄都加起来那么就必须使用bigint。 (二) 索引规约 1. 【强制】业务上具有唯一特性的字段即使是组合字段也必须建成唯一索引。 说明不要以为唯一索引影响了insert速度这个速度损耗可以忽略但提高查找速度是明显的另外即使在应用层做了非常完善的校验和控制只要没有唯一索引根据墨菲定律必然有脏数据产生。 2. 【强制】超过三个表禁止join。需要join的字段数据类型保持绝对一致多表关联查询时保证被关联的字段需要有索引。 说明即使双表join也要注意表索引、SQL性能。 3. 【强制】在varchar字段上建立索引时必须指定索引长度没必要对全字段建立索引根据实际文本区分度决定索引长度。 说明索引的长度与区分度是一对矛盾体一般对字符串类型数据长度为20的索引区分度会高达90%以上可以使用count(distinct left(列名,索引长度))/count(*)的区分度来确定。 解读区分度是指不重复的索引值和数据表的记录总数的比值范围(0,1]值越高则查询效率越高。对于blob,text,varchar的列必须使用前缀索引MySQL不允许索引这些列的完整长度。最好选择足够长的前缀保证较高的区分度也不能太长节省空间。 4. 【强制】页面搜索严禁左模糊或者全模糊如果需要请走搜索引擎来解决。 说明索引文件具有B-Tree的最左前缀匹配特性如果左边的值未确定那么无法使用此索引。 5. 【推荐】如果有order by的场景请注意利用索引的有序性。order by 最后的字段是组合索引的一部分并且放在索引组合顺序的最后避免出现file_sort的情况影响查询性能。 正例where a? and b? order by c; 索引a_b_c 反例索引中有范围查找那么索引有序性无法利用如WHERE a10 ORDER BY b; 索引a_b无法排序。 解读只有在order by 数据列的时候才可能会出现using filesort而且如果你不对进行order by的这一列设置索引的话无论列值是否有相同的都会出现using filesort。因此只要用到order by 的这一列都应该为其建立一个索引。 只有当索引的列顺序和order by子句的顺序完全一致并且所有列的排序方向都一样时才能用索引排序。如果查询需要关联多张表只有当order by子句引用的字段全部为第一个表时才能使用索引排序。 6. 【推荐】利用覆盖索引来进行查询操作来避免回表操作。 说明如果一本书需要知道第11章是什么标题会翻开第11章对应的那一页吗目录浏览一下就好这个目录就是起到覆盖索引的作用。 正例IDB能够建立索引的种类主键索引、唯一索引、普通索引而覆盖索引是一种查询的一种效果用explain的结果extra列会出现using index. 解读如果一个索引包含所有需要查询的字段的值称之为“覆盖索引”。由于覆盖索引必须要存储索引列的值哈希索引、空间索引和全文索引都不存储列的值MySQL只有B-Tree索引可以做覆盖索引。如对id,name,title三个字段建立索引在索引中会存储这三个列的值如果查询select id,name,title from table where id 10; 通过explain会看到extra为using index。如果查询select * from table where id 10;就不会使用覆盖索引因为索引中没有包含所有的列值。 7. 【推荐】利用延迟关联或者子查询优化超多分页场景。 说明MySQL并不是跳过offset行而是取offsetN行然后返回放弃前offset行返回N行那当offset特别大的时候效率就非常的低下要么控制返回的总页数要么对超过特定阈值的页数进行SQL改写。 解读案例 select count(*) from user_game_info; // 共有956176条数据 select * from user_game_info a limit 900000, 20; // 此查询耗时0.547S select t1.* from user_game_info t1, (select id from user_game_info limit 900000, 20) t2 where t1.id t2.id; // 优化后耗时0.178S 8. 【推荐】SQL性能优化的目标至少要达到 range级别要求是ref级别如果可以是consts最好。 说明 consts单表中最多只有一个匹配行主键或者唯一索引在优化阶段即可读取到数据。ref指的是使用普通的索引。normal indexrange对索引进范围检索。 反例explain表的结果typeindex索引物理文件全扫描速度非常慢这个index级别比较range还低与全表扫描是小巫见大巫。 9. 【推荐】建组合索引的时候区分度最高的在最左边。 正例如果where a? and b? a列的几乎接近于唯一值那么只需要单建idx_a索引即可。 说明存在非等号和等号混合判断条件时在建索引时请把等号条件的列前置。如wherea? and b? 那么即使a的区分度更高也必须把b放在索引的最前列。 10.【推荐】 防止因字段类型不同造成的隐式转换导致索引失效。 解读例如给上文提到的tb_user_account表的username(varchar)字段加索引由于字段是varchar类型的所以上图中的查询类型匹配命中索引下图是用int类型匹配的无法命中索引。 以字符串形式查找命中索引 因隐式转换未命中索引 隐式转换规则 两个参数至少有一个是 NULL 时比较的结果也是 NULL例外是使用 对两个 NULL 做比较时会返回 1这两种情况都不需要做类型转换两个参数都是字符串会按照字符串来比较不做类型转换两个参数都是整数按照整数来比较不做类型转换十六进制的值和非数字做比较时会被当做二进制串有一个参数是 TIMESTAMP 或 DATETIME并且另外一个参数是常量常量会被转换为 timestamp有一个参数是 decimal 类型如果另外一个参数是 decimal 或者整数会将整数转换为 decimal 后进行比较如果另外一个参数是浮点数则会把 decimal 转换为浮点数进行比较所有其他情况下两个参数都会被转换为浮点数再进行比较 11.【参考】创建索引时避免有如下极端误解 误认为一个查询就需要建一个索引。误认为索引会消耗空间、严重拖慢更新和新增速度。误认为唯一索引一律需要在应用层通过“先查后插”方式解决。 (三) SQL规约 1. 【强制】不要使用count(列名)或count(常量)来替代count(*)count(*)就是SQL92定义的标准统计行数的语法跟数据库无关跟NULL和非NULL无关。 说明count(*)会统计值为NULL的行而count(列名)不会统计此列为NULL值的行。 2. 【强制】count(distinct col) 计算该列除NULL之外的不重复数量。注意 count(distinct col1, col2) 如果其中一列全为NULL那么即使另一列有不同的值也返回为0。 3. 【强制】当某一列的值全是NULL时count(col)的返回结果为0但sum(col)的返回结果为 NULL因此使用sum()时需注意NPE问题。 正例可以使用如下方式来避免sum的NPE问题SELECTIF(ISNULL(SUM(g)),0,SUM(g))FROM table; 4. 【强制】使用ISNULL()来判断是否为NULL值。注意NULL与任何值的直接比较都为NULL。 说明 NULLNULL的返回结果是NULL不是false。NULLNULL的返回结果是NULL不是true。NULL1的返回结果是NULL而不是true。 5. 【强制】在代码中写分页查询逻辑时若count为0应直接返回避免执行后面的分页语句。 6. 【强制】不得使用外键与级联一切外键概念必须在应用层解决。 说明概念解释学生表中的student_id是主键那么成绩表中的student_id则为外键。如果更新学生表中的student_id同时触发成绩表中的student_id更新则为级联更新。外键与级联更新适用于单机低并发不适合分布式、高并发集群级联更新是强阻塞存在数据库更新风暴的风险外键影响数据库的插入速度。 7. 【强制】禁止使用存储过程存储过程难以调试和扩展更没有移植性。 8. 【强制】IDB数据订正时删除和修改记录时要先select避免出现误删除确认无误才能提交执行。 9. 【推荐】in操作能避免则避免若实在避免不了需要仔细评估in后边的集合元素数量控制在1000个之内。 10.【参考】因阿里巴巴全球化需要所有的字符存储与表示均以utf-8编码那么字符计数方法注意 说明 SELECT LENGTH(阿里巴巴) 返回为12 SELECT CHARACTER_LENGTH(阿里巴巴) 返回为4 如果要使用表情那么使用utfmb4来进行存储注意它与utf-8编码。 11.【参考】TRUNCATE TABLE 比 DELETE速度快且使用的系统和事务日志资源少但TRUNCATE无事务且不触发trigger有可能造成事故故不建议在开发代码中使用此语句。 说明TRUNCATE TABLE 在功能上与不带 WHERE子句的 DELETE语句相同。 (四) ORM规约 1. 【强制】在表查询中一律不要使用 *作为查询的字段列表需要哪些字段必须明确写明。 说明1增加查询分析器解析成本。2增减字段容易与resultMap配置不一致。3无用字段增加网络消耗尤其是 text 类型的字段。 2. 【强制】POJO类的boolean属性不能加is而数据库字段必须加is_要求在resultMap中进行字段与属性之间的映射。 说明参见定义POJO类以及数据库字段定义规定在sql.xml增加映射是必须的。 3. 【强制】不要用resultClass当返回参数即使所有类属性名与数据库字段一一对应也需要定义反过来每一个表也必然有一个与之对应。 说明配置映射关系使字段与DO类解耦方便维护。 4. 【强制】xml配置中参数注意使用#{}#param#不要使用${}此种方式容易出现SQL注入。 解读#与$的区别在预编译中的处理是不一样的。#{} 在预处理时会把参数部分用一个占位符 ? 代替如select * from user where name ?;而 ${} 则只是简单的字符串替换在动态解析阶段该 sql 语句会被解析成select * from user where name zhangsan’;以上#{} 的参数替换是发生在 DBMS 中而 ${} 则发生在动态解析过程中。 5. 【强制】iBATIS自带的queryForList(String statementName,int start,int size)不推荐使用。 说明其实现方式是在数据库取到statementName对应的SQL语句的所有记录再通过subList取start,size的子集合线上因为这个原因曾经出现过OOM。 正例在sqlmap.xml中引入 #start#, #size# MapString, Object map new HashMapString,Object(); map.put(start, start); map.put(size, size); 6. 【强制】不允许直接拿HashMap与HashTable作为查询结果集的输出。 反例某同学为避免写一个resultMap直接使用HashTable来接收数据库返回结果结果出现日常是把bigint转成Long值而线上由于数据库版本不一样解析成BigInteger导致线上问题。 7. 【强制】更新数据表记录时必须同时更新记录对应的gmt_modified字段值为当前时间。 8. 【推荐】不要写一个大而全的数据更新接口传入为POJO类不管是不是自己的目标更新字段都进行update table set c1value1,c2value2,c3value3; 这是不对的。执行SQL时尽量不要更新无改动的字段一是易出错二是效率低三是binlog增加存储。 9. 【参考】Transactional事务不要滥用。事务会影响数据库的QPS另外使用事务的地方需要考虑各方面的回滚方案包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。 10.【参考】isEqual中的compareValue是与属性值对比的常量一般是数字表示相等时带上此条件isNotEmpty表示不为空且不为null时执行isNotNull表示不为null值时执行。 阿里十年java老兵总结的50条mysql使用军规 一.数据库配置         1. innodb_flush_log_at_trx_commit,这个对支付业务来说是关键性的设置之一可选的参数值有0,1,2, 支付需要设置成1. 2. 对交易以及记账部分来说必须是innode引擎,以支持事务 3. 事务隔离级别权衡安全性和效率使用可重复读 4. innodb_lock_wait_timeout,这个是锁超时时间不建议太大怕引起雪崩 二.业务设计     1. 不用物理删除即尽量避免用delete语句drop命令等;通过软删除处理即通过额外的字段标 2. 明记录的删除状态;虽然对写代码来讲有些麻烦但实践证明是非常值得的 3. 在系统设计之初就要定好编码规范对存入的数据做相应转换并做好escape处理 4. 除列表查询外尽量用主键操作;核心交易系统中尽量避免非主键操作 5. 避免schema中1对多设计概念简单编码很难 6. 就mysql来说尽量使用其最简单的功能不用其高级功能如触发器连表等 7. 就mysql来说尽量不让其做计算功能而是让业务层来实现计算逻辑 8. 当要锁多条记录时 要考虑死锁的可能以及预防的措施 9. 按业务垂直划分原则尽量把不同的业务方不同的库中 10. 坚持小事务一个大事务与将其拆分成的十个小事务相比小事务对数据库压力更小;另外事务 中做尽可能少的事情神马参数校验之类的事情能拉出去就拉出去 11. 数据库连接长时间不用超时断开是常见的应用中需要考虑 12. 对超大型系统来说分布式事务是有价值的;但在大多下情况下单机事务能很好的满足需要 13. 主从延迟总是会有的有时候会很大设计中要考虑 14. 读写账号分开读账号select权限即可写账号updateinsert即可 15. where条件keyvalue的模式中加上单引号总是对的不加在某些情况下有很多令人意外的副 作用 16. 尽量使用简单的数据类型char系列int系列以及date系列即可 17. 事务的使用上在线交易使用悲观锁 18. 事务框架的选择上使用控制力度比较大的直接TransactionManager不推荐使用声明式事务 19. 采用InnoDB引擎UTF8编码 20. 有状态字段的记录状态的取值不宜太多 6 ~ 7个应该是上限了 最好不要超过 4个 21. 每个表都应该有自己的主键且尽量让表内主键保持递增 22. 不使用自增主键 23. 在线查询的字段一定要建立覆盖索引 24. 分页查找一定不能直接limit mn一定要做优化 三. 应用规范     1. 当进行账户余额变化操作时总是校验账户是否被冻 2. 对单据如TradeCharge进行处理并发总是要考虑的需要锁记录后进行校验;从数据库查询的 时候请先起动事务并用select...for update;防止并发带来的问题;从性能上将锁交易单本身不 会成为性能瓶颈 3. 更新账户余额之前必须加锁即起事务select...for update; 更新余额的语句建议是update table set 余额余额/-发生额 自然在代码逻辑中做各种边界值校验包括不溢出不小于0等 4.金额都统一单位以分为单位合适;数据类型为有符号64位整形数合适 5. 对金额计算时对溢出的预防总是需要的 6.使用select时慎用*,尽量明确的枚举出字段名 7.与外部交互的地方记录下外部发生时间 8. db命名采用cashpay前缀 9. 表命名采用t_前缀 10. 字段命名采用F_前缀 11. 每个表都会有一个字段记录上次更新时间 12. 在一个session中的所有数据库更新记录上次更新时间都是一致的
http://www.pierceye.com/news/360046/

相关文章:

  • 视频模板网站企业所得税优惠政策2022年
  • 坪山附近公司做网站建设哪家效益快wordpress 置顶 插件
  • 品牌网站建设服务机构内容网站管理系统
  • 电商网站建设基础ppt个人简单网站页
  • 移动端网站模板专业建站工作室
  • 企业网站建设的重要性及意义建设银行忘记密码网站首页
  • 易雅达网站建设公司广告设计公司设计收费标准
  • 行业门户网站php网站开发程序
  • 广州微信网站建设报价表上海注销营业执照流程
  • 陕西省建设执业资格注册中心网站科技有限公司 翻译
  • 做推广都有哪些网站网站怎么上传源码
  • discuz门户网站模板手机电子商务网站规划书范文
  • vps能同时做网站同时做其它事吗wordpress 支持小工具
  • 网站建设制作网络公司wordpress 汽车模板
  • 有哪些做外贸的网站网站快速搭建平台
  • wordpress搜索代码制做优化精灵
  • 连云港做网站推广东莞seo
  • 专业网站设计公司和普通设计公司的区别微信分销网站建设
  • 青海个人旅游网站建设网站建设教程软件下载
  • 做AMC12的题的网站龙华网站建设专业公司
  • 莱州网站制作友情链接交换形式
  • 如何编写网站做美食类网站现状
  • 一站式推广平台做家装模型的效果图网站
  • 企业电子商务网站开发实验报告苏州建筑设计公司排名
  • 网站的优化与网站建设有关吗网站先做移动站在做pc站可行吗
  • 河北网站制作公司电话建设网站的情况说明
  • 高校网站平台建设wordpress小工具不见了
  • 网站建设 会计处理北京垡头网站建设公司
  • 唐山网站制作案例网站建设中标
  • 网站开发培训费济南网络优化推广公司哪家好