安平县建设局网站,移动应用与开发是干什么的,市场调研报告word模板,湖南企业网络推广服务索引: 索引是帮助MYSQL高效获取数据的排好序的数据结构 1)假设现在进行查询数据#xff0c;select * from user where userID89 2)没有索引是一行一行从MYSQL进行查询的#xff0c;还有就是数据的记录都是存储在MYSQL磁盘上面的#xff0c;比如说插入数据的时候是向磁盘上面… 索引: 索引是帮助MYSQL高效获取数据的排好序的数据结构 1)假设现在进行查询数据select * from user where userID89 2)没有索引是一行一行从MYSQL进行查询的还有就是数据的记录都是存储在MYSQL磁盘上面的比如说插入数据的时候是向磁盘上面的某一个点位去存放当去尝试插入第二条记录的时候并不是挨着点(第一条记录)位去存放的磁盘写数据是一个磁道一个磁道写的如果是过了好几天才插入第二条记录一张表的所有记录是可能随机分布在磁盘上的在磁盘上并不是连续的所以会产生随机IO 3)一次读取一条记录一次IO如果表中的数据非常多就需要进行多次IO 4)二叉树key是主键值value是数据所在的物理磁盘的地址 5)红黑树也是二叉平衡树一个节点只能存放一条记录高度也非常高当数据量高的时候高的高度也是比较高也是不可控的所以可以在一个页里面存储更多的数据让高度变得更低B树的叶子节点存储着所有的索引记录非叶子节点就是一些冗余索引来辅助B数据的增删改查但是B树叶子节点和叶子节点之间没有使用指针链接MYSQL在进行查找的时候会加载一个目录页中的数据然后使用二分法快速找到下一个数据的文件地址 6)出现一个索引那么旁边就会增加一个叶子结点的地址假设使用bigInt来存储是8个字节指针是占据着6个字节那么一个页中大概存放的数据就是16KB/861170条记录但是叶子节点可能存放的数据量比较少一张表大概一行记录大概是1KB所以叶子节点大概一可以存放16KB所以B树可以存放的数据载荷的个数就是1170*1170*16条数据此时数据的高度就是3最多经过三次的磁盘IO就可以找到最终想要的数据所以千万级别的数据如果要是走索引那速度是非常快的相比于磁盘遍历要扫描1000W次但是使用到了索引此时查询的速度就会非常快B树还支持范围查询但是B树只能优先遍历B树没有冗余索引 7)在同等条件下假设现在要存放2000万条记录使用B树那么此时的树的高度一个数据载荷是1KB一个页数据只能存放16条索引数据所以计算树的高度16^K2000WK3MYSIM存储引擎有三个文件.frm是表结构文件.myd是数据文件.MYI是索引文件但是innodb也是存储了两个文件.frm是表结构文件.ibd是索引数据 二分折半查找算法找到30位于15和56之间然后中间存放的是下一行的地址 8)哈希索引:不支持范围查询大于小于 为啥要一定使用自增主键 1)创建主键默认采用主键索引充当聚簇索引MYSQL就不会判断唯一索引生成6个字节的隐藏索引这样子可以减少MYSQL的工作 2)如果使用UUID使用UUID的ASCILL码的值来进行比较如果说有的UUID的前面很长串字符都相等唯独后面的不相等那么此时比较的效率就非常低下但是整数比较效率很高防止聚簇索引和非聚簇索引的空间非常大显然主键长度越小普通索引的叶子节点就越小普通索引占用的空间也就越小 3)整形数据占用的空间小要使用UUID做主键要解决固态硬盘存储空间的问题 4)防止造成页分裂 为什么非主键索引叶子节点存放的是主键值呢 首先是为了节省内存空间其次是为了一致性如果主键索引和非主键索引都是放的是数据的完整记录那么再进行数据更新的时候一致性更新的成本就会非常高更新值得话都更新成功才算成功这样子就会非常麻烦 联合索引:假设此时根据age字段查询select * from user where age30此时会发现age字段不是有序的此时就不能利用到索引有序的特性非叶子节点的辅助索引就是叶子结点的第一条记录 Explain执行: 1)id:执行顺序id越大越先执行要是id相等的记录那么实际的执行顺序就是谁在上面谁先执行 2)explains中的rows:查询出来的行数explain执行计划show warning会展现出MYSQL得优化思路可以看看MYSQL得优化思路 3)select_type:SQL语句执行的一种类型在select后面 from前面查询出来的表是子查询from后面要是还有SQL语句就是衍生查询他们都会产生临时表 primary是复杂查询通常这种查询要关联好几种查询是最外层的select simple简单查询查询不包含子查询和union subquery:包含在select中的子查询不在from子句中 derived:包含在from子句中的子查询MySQL会将结果存放在一个临时表中也称为派生表derived的英文含义table字段derived那么代表着这条SQL语句是从衍生表中查询出来的后面的derived数字代表着是从拿一张表进行衍生过来的 union:在union中的第二个和随后的select 4)table:当from子句中有子查询时table列是derivenN格式表示当前查询依赖idN的查询于是先执行idN的查询当有union时UNIONRESULT的table列的值为union1,21和2表示参与union的select行id 5)type:systemconsteq_refrefrangeindexAll一般来说得保证查询达到range级别最好达到ref这一列表示关联类型或访问类型即MySQL决定如何查找表中的行查找数据行记录的大概范围 1)system:和查询一个常量一样效率非常高只有一行记录是const一种特殊情况 2)const:唯一索引或者是主键索引和常数进行比较的时候所以表最多有一个匹配行读取1次速度比较快system是const的特例表里只有一条元组匹配时为system 用于primarykey或uniquekey的所有列与常数比较时所以表最多有一个匹配行读取1次速度比较快system是const的特例表里只有一条元组匹配时为system 3)eq_ref:使用主键或者是唯一索引进行关联查询去查询记录的时候直接使用主键ID去关联就可以了最多只会返回一条符合条件的记录这可能是在const之外最好的连接类型了简单的select查询不会出现这种type 4)ref:查询条件没使用主键索引或者是唯一索引使用非唯一二级索引查询并且记录有多条使用普通索引或者是唯一索引的前缀或者是联合索引的一部分相比eq_ref不使用唯一索引而是使用普通索引或者唯一性索引的部分前缀索引要和某个值相比较可能会找到多个符合条件的行 简单的select查询name是非唯一索引 5)range:范围扫描通常出现在in()between等操作中使用一个索引来检索给定范围的行虽然使用到了索引但是要在索引树向后查找 6)index:扫描全索引就能拿到结果一般是扫描某个二级索引这种扫描不会从索引树根节点开始快速查找而是直接对二级索引的叶子节点遍历和扫描速度还是比较慢的这种查询一般为使用覆盖索引二级索引一般比较小所以这种通常比ALL快一些查询的字段全部在索引中如果使用覆盖索引(查询的所有字段在二级索引和在主键索引中)优先使用二级辅助索引因为主键索引扫描的数据量比较大但是如果要查询的数据不在二级索引中都有而在主键索引中都有那么此时走主键索引而不走二级索引从而保证避免回表查询遍历索引效率也很低 7)All:就是扫描主键索引的叶子节点从头开始找扫描聚簇索引的所有叶子节点 注意select nameage from user和select name age from user where id1操作索引的区别 All和Index的区别:通常Index是二级索引扫描效率相对来说比较高一些All是主键索引从头开始扫描因为遍历的数据太大扫描的数据量更多所以效率最低 而说用到索引时从根节点开始向下折半查找来确定某一个值这样效率才高索引一次可能只是找几条记录几次折半查找搞定了虽然有可能都使用到了索引但是使用ref和使用index对于索引的操作是不同的index是遍历索引的叶子节点ref是通过二分法来查询要找的数据 possible_key:可能用到的索引 key:真正用到的索引 ref:这一列显示了在key列记录的索引中表查找值所用到的列或常量常见的有:const常量字段名 rows:这一列是mysql估计要读取并检测的行数注意这个不是结果集里的行数; key-len:使用到的索引的长度通常对联合索引有效如果字段允许是null需要使用到1个字节来记录null索引的最大长度是768字节当字符串长度过长的时候MYSQL会做一个类似于最左前缀索引的处理将左半部分的字符提取出来做索引 1)Using index:mysql执行计划explain结果里的key有使用索引如果select后面查询的字段都可以从这个索引的树中获取这种情况一般可以说是用到了覆盖索引extra里一般都有usingindex覆盖索引一般针对的是辅助索引整个查询结果只通过辅助索引就能拿到结果不需要通过辅助索引树找到主键再通过主键去主键索引树里获取其它字段值 2)Using where:使用where语句来处理结果并且查询的列未被索引覆盖没有使用到索引如图下面的name字段就没有索引 3)Using index condition:查询的列不完全被索引覆盖where条件中是一个前导列的范围 4)Usingtemporary:mysql需要创建一张临时表来处理查询出现这种情况一般是要进行优化的首先是想到用索引来优化 使用distinct查询:需要使用临时表进行去重先把查询所有结果加载到内存里面然后简称一张临时表进行去重想要进行优化直接在去重字段上加上覆盖索引因为索引树本身是有序的再扫描索引树的时候因为数据有序所以直接可以去重 5)文件排序:Using fileSort使用到文件排序 6)Selecttablesoptimizedaway:使用某些聚合函数比如maxmin来访问存在索引的某个字段 1)在索引树的非叶子节点里面都没有这么一个东西left(name3)是无法走索引的把字符串截取三个字符在索引树里面进行查找根本就查找不到对应的字符串无法进行比较如果那索引的一部分来查找数据是没有顺序的因为根本无法定位如果前三个字符相同呢根本无法通过二分法来定位到对应的数据最终还有可能找不到最终的数据 2)可能有时候可能全表扫描比走索引扫描更好MYSQL会进行一个评估因为有时候查询二级索引的时候本来表中就没有几条记录这个时候还要通过二级索引来进行回表查询还要回表此时还不如全表扫描走得快MYSQL会选择最小成本范围查询数据量太大可以将大的查询范围拆分成小范围 3)在第二个字段中如果是范围查询那么第三个字段就不是有序的第三个字段因为不是有序的所以就不走索引 4)不等于结果集太多了基本不走索引也有可能走索引 5)%是因为不知道要跳过多少字符%后面的字符串还是无序的但是XXX%在索引树是有序的用的是最左前缀的那一段字符串本身就是有序的 7)但是like k%%%无论数据量多少都走索引存储引擎不能使用索引中范围条件右边的列 explain select 查询列 force index(索引名字) where 条件 in exist走索引not innot exist不走索引 like K%% 一定会使用到索引下推但是一般不使用索引下推like结果集相比于数据量比较小底层结果集太大了剩下的结果集还要比对后面两个字段有可能还会将整个表查出来还要根据最终的过滤的数目条数 select * from user where username like lilei% and age20 and passwordaa 1)在MYSQL5.6之前查询过程是现在二级索引中查询到username likelilei%的所有字段的ID然后去主键索引树去查询所有的记录然后在进行筛选And后面的age20 and passwordaa的字段因为找完username以后age和password不是有序的所以后面的字段不会看直接回表因为使用到索引一定是使用到索引的有序性 2)但是MYSQL 5.6以后使用到了索引下推进行过滤的时候向下继续推断减少回表次数 3)MYSQL会分析扫描行数和扫描成本(回表次数)扫描行数不完全决定于扫描成本 4)select * from username in (ab) order by password age在这里面一定会使用到文件排序因为将usernamea和usernameb的结果加起来password和age仍然是没有顺序的 5)select * from user where usernamea order by username仍然不是用到索引最后要走全表扫描可能是数据量太大了还要进行回表查询但是如果修改成覆盖索引select username from user where usernamea order by username就是用到覆盖索引此时就使用到了索引排序 1)MYSQL有时候联合索引第一个字段就使用范围查询那么时候查询的数据量可能非常大需要进行回表查询效率非常低可能还不如全表扫描可以考虑使用覆盖索引优化可能MYSQL内部就认为第一个范围就使用范围回表次数非常多否则要想是强制走索引要么走覆盖索引会在辅助索引里面去扫虽然强制走索引扫描行数减少了但是回表次数也不少扫描行数也不能完全决定SQL执行时间的长短 2)所以基本可以认为like k%%是等值查询把它当作等值查询的原因是因为他使用到了索引下推like %kkk索引失效 3)or和in有可能走索引也有可能不走索引in和or在表数据量比较大的情况会走索引在表记录不多的情况下会选择全表扫描否则还要回表很麻烦但是like前缀还是会使用到索引 表中的记录比较多in和or会走索引因为要是全表扫描的话效率很低百万数据中遍历来找要找几行数据不容易 下面表中的记录比较少直接全表扫描都不需要回表了遍历成本也不高 4)在使用不等于或者not innot exists的时候无法使用索引会导致全表扫描这种情况可以将大范围拆分成多个小范围 5)对于范围查询来说大于小于betweenMYSQL内部优化器都会根据检索比例和表的大小来多个因素整体评估判断是否使用索引 6)解决like%字符串%索引不被使用的方法使用覆盖索引查询字段必须是建立覆盖索引字段 单路排序和双路排序:Sort Buffer和max_length_for_sort_data不建议去动 Using Index:使用读索引树来进行排序二级索引扫描快数据量少扫描效率高 Using fileSort:使用文件排序拿聚簇索引效率比较低 文件单路排序(不回表):直接将所有行记录加载到缓冲池中能够进行排序select后面需要扫描的字段全部加载到内存里面就是整张表数据加载到内存单路排序是一次性的取出所有满足条件行的所有字段然后再sort buffer中进行排序 文件双路排序(回表):只需要拿出结果集中的排序的字段和主键ID即可然后排好序以后再来根据主键ID再进行回表查询占用的空间小一些双路排序是首先根据相应的条件取出相应的排序字段和直接可以定位到行记录的主键ID然后再sortBuffer中进行排序最后在从索引树中找到最终返回的字段单路排序占用的内存高最终的结果就是想要的双路排序占用的内存低一些但是最终需要进行回表 0)注意:number_of_tmp_files:3这个是执行计划里面的字段表示使用临时文件的个数这个值为0表示使用的是sort_buffer进行的排序否则使用的是磁盘文件进行排序如果数据量比较小使用sort_buffer进行排序但是如果过滤的数据量比较大那么就使用临时文件进行排序最终还是需要使用临时文件加载到内存中进行排序的但是一般来说临时文件排序效率低于内存排序但是尽量也不要增大sort_buffer 1)sort_buffer小尽量使用双路排序可以适当减少max_length_for_sort_data的值 2)sort_buffer大尽量使用单路排序可以适当增加max_length_for_sort_data的值 索引的设计: 1)代码先行索引至上:是建完表马上就建立索引吗应该等到主体的业务都开发完成把涉及到的SQL都需要拿出来分析一下再来建立索引 2)联合索引尽量覆盖条件:大部分的SQL语句是好几个条件一起来查如果你建立了太多的单值索引最终MYSQL只会选择一个单值索引况且很多56个单值索引加起来的存储空间要远远大于针对几个字段建立起来的联合索引况且单值索引过滤的字段比较少如果使用联合索引过滤的字段更多但是如果是唯一索引防止数据重复还要建立单值索引不是查询字段快而是为了保证数据字段的唯一性比如可以设计一个或者两三个联合索引尽量少建单值索引让每一个联合索引都尽量去包含sql语句里的where、orderby、groupby的字段还要确保这些联合索引的字段顺序尽量满足sql查询的最左前缀原则范围查询应该是用在索引条件的最后一个字段 3)针对于区分度不高的值不要建立索引不要再小基数上面建立索引: 4)长字符串可以建立前缀索引:减少磁盘空间选择区分度高的长度建立前缀索引 5)如果范围查询不走索引可以把大范围拆分成多个小范围 6)开启慢查询日志:慢SQL查询会写入到文件里面后续程序员就可以分析了超时时间命令也可以进行设置但是开启慢SQL查询可能浪费性能 社交场景App:用户筛选根据地区省市性别年龄身高爱好来进行过滤按照评分(用户受欢迎程度)受欢迎程度进行排序还要进行分页 select XX from user where 列名XX and 名字XX order by XX limit A offset B 1)根据省市城市性别进行筛选还要分析业务场景select * from user where proviceXX and cityXX and sexXX可以建立联合索引union_index(provicecitysex) 2)根据省份城市年龄筛选:select * from user where proviceXX and cityXX and age10and age10union_index(provicecitysexage)但是这个时候需要注意age条件的查询一般都是范围查询范围查询在联合索引后面一般不会走索引建议范围的条件的列在后面建如果把age放在前面此时age后面字段直接索引失效 3)但是上面这种情况age不走索引但是这个时候可以优化SQL语句的写法: where proviceXX and cityXX and sex in(femal“mela) and age10and age10但是这只是适用于基数比较大的情况数据量比较小就不用了但是数据量比较大in一般在生产环境下都会走索引 4)处理爱好的字段和sex处理相似union_index(provicecitysexhobblyage)把爱好全部加到SQL里面去中间跳过的值直接拼接到SQL里面去尽量走索引树里面去 5)这个latest_login_time是为了过滤那些不经常登录的用户的但是从下面的角度来看也是需要使用到范围查询的此时在age后面索引就会失效了根据实际业务场景让更多的查询走索引select * from user where proviceXX and cityXX and sex in(femal“mela) and age10and age10 and login_time10 解决方案就是设置一个标志位在表中重新建立一个字段7日之内用户是否登录字段如果这个用户是在7天前登陆的就设置成0如果是在7天之内登陆过的就设置成1搞成一个定时任务7天内没有登陆过就设置成0select * from user where proviceXX and cityXX and sex in(femal“mela) and login_time1 and age 10and age10 6)如果有的人只是想要根据性别和评分来进行查询那么就可以根据性别和评分单独建立联合是可以建立多个联合辅助索引 7)假设此时出现了这么一种查询:select * from user where provice XX and cityXX and sex in(femal“mela) and login_time1 and age 10and age10 order by score先满足where条件再来进行order by因为经过where筛选以后order by的数据量非常小此时如果order by使用file sort也没关系但是如果先order by排序再来where条件筛选 SQL优化: 一)分页优化: select * from user limit 10000 offset 10在MYSQL的底层是这么来做的不是从第10000条记录向后查10条MYSQL先进行查询11000条记录然后删除前10000条记录最终只是展现10条查询记录 1)自增且连续的主键排序的分页查询的例子前提是按照主键自增还是连续的但是如果原来的记录删除了那么这个优化就不好使了 select * from use limit 900005既不走索引还是文件排序 最终可以修改成这样:select * from use90000 offset limit 5 2)name是联合索引的第一个字段 select * from user order by name limit 10000 offset 10 这里面可能不走索引因为MYSQL感觉结果集太大回表效率太低还不如全表扫描 explain select * from user as s1 inner join (select userID from user order by username limit 10000 offset 10) as s2 on s1.userIDs2.userID; 优化:可以使用覆盖索引来优化下面的子查询使用到了覆盖索引本质上是使用二级覆盖索引来扫描索引树的最终只是需要和s1表做关联即可还是用到了索引 1)生成了临时表只有5条记录 2)关联的时候使用的是主键关联 二)关联查询优化: 一)嵌套循环连接算法(NLG):默认有索引关联 一行一行地从第一张表中成为驱动表中读取行这行中找到关联字段根据关联字段在另一张表中也就是被驱动表中取出满足条件的行然后取出两张表的结果合集 explain select * from user inner join class on user.classIDclass.id; SQL语句的执行过程:执行过程是此时先查询user t1表在来进行查询class t2表 for(int i0;iuser;i){for(int j0;jclass;j){}
} 此时假设根据class的ID这个列建立了唯一主键索引这个时候仍然是拿着user表中的每一条记录去class表中进行匹配但是此时假设user表中有100条记录class表中有10000条记录此时最终结果就是user表扫描了100次class表其实因为走索引的原因也是只扫描了100次一共扫描的行其实就是200行 如果被驱动表的关联字段没索引使用NLJ算法性能会比较低mysql会选择BlockNested-LoopJoin算法 二)基于块的循环嵌套链接(BNL):不使用索引字段关联 假设t1表10000条记录t2表100条记录把驱动表t2中的数据读取到join buffer中然后扫描被驱动表把被驱动表中的每一行记录取出来和join buffer中的记录做对比 Extra中的Using join Buffer说明该关联查询使用的是BNL算法 1)将t2中的所有数据也就是驱动表中的所有数据放到joinBuffer里面 2)把表t1中的每一行数据取出来和joinbuffer中的数据做对比 3)返回满足join链接的数据 分析:整个过程对于表的t1和t2都做了一次全表扫描因此扫描的总行数是10000100条况且join buffer中的数据是无序的对于t1表中的每一行都要扫描整个进行100次判断所以在内存中的比较次数就是100000*100次 在这个例子中t2表才100行记录假设t2要是一张大表joinbuffer放不下怎么办呢 首先joinbuffer中的数据是依靠参数join_buffer_size决定的默认值是256K如果放不下t2表中的所有记录那么就分段放假设t2表中有10000条记录joinbuffer中最多只能存放800条记录那么执行过程就是先将t2表中的数据向join buffer中放800条记录然后从t1表里取数据跟join_buffer中数据对比得到部分结果然后清空join_buffer再放入t2表剩余200行记录再次从t1表里取数据跟join_buffer中数据对比,所以就多扫了一次t1表; 10000*80010000*200上面就是执行过程 如果单纯的让第二种方式按照第一种方式来走的话不适用内存那么扫描磁盘的次数就是表1的所有记录*表2的所有记录就是100W次 4)被驱动表的关联字段没有索引但是为什么要选择使用BNL算法而不是用NLJ算法呢 如果上面第二条SQL语句使用NLJ算法那么扫描的行数就是两张表总行数的乘积这个是磁盘扫描很显然来说使用BNL磁盘扫描扫描次数更少因为驱动表中的记录都已经加载到了内存里面MYSQL对于被驱动表的关联字段没有索引的关联查询会使用BNL算法如果有索引一般使用NLJ算法有索引的情况下NLJ算法更厉害 优化: 1)被驱动表也就是大表关联字段一定要加索引:你不加索引走的是BNJ 2)小表驱动大表:先执行的表是小表后执行的表是大表大表和小表不应该按照参与的数据集来进行判断当在写多表连接的SQL查询的时候如果明确知道哪一张表是小表可以采用straight_join的写法固定连接驱动方式省去MYSQL优化器自己判断的时间就拿第二种方式来说要将小表的数据放到join buffer中去但是如果使用的是大表有可能join buffer存不下要放两次下一次还要加载这种方式效率会很低 straight_join解释:straight_join和join功能是类似的但是可以让左边的表驱动右边的表能改表优化器对于连表查询的执行顺序比如说select * from t2 straight_join t1 on t2.at1.a 1)straight_join只是适用于inner_join并不适用于left joinright join因为他们已经默认制定了表的连接顺序 2)尽可能地让优化器做判断因为大部分情况下MYSQL优化器是比人要聪明的所以使用straight_join一定要慎重因为人不一定比优化器靠谱 对于小表定义的明确:再决定那张表做驱动表的时候应该是两张表按照各自的条件进行过滤计算参与join的各个字段的总数据量数据小的那个表就是小表应该作为驱动表 3)假设此时有t1表有100条记录t2表只有20条记录此时认为t1表是大表t2表是小表这里面大表和小表的定义是参与关联的数据集但是执行下面的SQL语句 select * from t1 inner join t2 on t1.idt2.id where t1.id10此时那么t1就是小表t2就是大表t1表参与关联的记录只有10行 三)in和exists的优化: 小表驱动大表:让小的数据集驱动大的数据集 1)in:当B表的数据集小于A表的数据集in优于exists先执行子查询再来执行后面的语句 select * from A where id in(select id from B)for(select id in B){select * from A where A.idB.id;
} 2)exists:当A的数据集小于B的数据集exists优于in将主查询中的数据放到子查询中做条件验证根据验证结果来判断主查寻中的数据是否保留先拿出外边查询的结果集然后再去里层去过滤肯定是适合于A表比较小的场景 四)查询总记录的行数 1)字段存在索引:count(*)count(1)count(字段)count(主键) 当字段有索引的时候count(字段)如果字段有索引走二级索引二级索引不包含所有字段二级索引的存储的数据比主键索引少所以count(字段)count(主键) 2)字段没有索引:count(*)count(1)count(主键)count(字段) 当字段没有索引count(字段)走不了索引count(主键ID)还可以走主键索引所以count(主键)count(id) 1)count(1)和count(字段):他们都是扫描的是二级索引树count(1)count(name)count(name)在进行扫描的时候会把name的索引值取出来放到内存中然后额外会使用一个计数器来统计name的个数但是count(1)不会把name从索引树中拿出来放到内存中只是遍历非聚簇索引的叶子节点有一条记录就在内存中有一个计数器1 2)count(*)不会把字段取出来不取值按行累加所以说效率很高 3)对于count(id)mysql最终选择辅助索引而不是主键聚集索引因为二级索引相对主键索引存储数据更少检索性能应该更高mysql内部做了点优化(应该是在5.7版本才优化) 4)count(字段)是不会统计null值得 但是如果表中的数据量依然很大的话这种count方式效率仍然很低 innodb存储引擎不同事务count(*)结果集不相同不适合做count(*)的维护 如果对总记录数要求不是特别准确 redis维护一个原子计数器可能使用缓存无法达到数据百分之百一致 维护表的总数:表名总数然后走索引效率非常高 有条件就比较麻烦了因为有条件还需要维护结果行记录 三张表不要join可以在java代码进行拆解MYSQL是不容易扩展的 第四种除非走覆盖索引 in得记录数比较少不会走索引 如果可以确定一个一个数字的取值范围那么使用更小的范围来存储如果确定是由符号无符号一定要构 timestampe尽量小公司使用因为占用的空间比较小datetime时间无限长 固定长度用char