大家好,我是melo,一名大三后台练习生
在我们上一篇文章中,讲到了索引的原理&&设计原则,知道了索引如何使用。
emm?那具体什么场景需要用到索引,我们要怎么分析SQL语句,并对其进行优化呢,这篇将从以下几点带你攻破ta:
注意,本文MySQL版本为5.6.43,部分结论在其他版本可能不适用!!!
建议通过侧边栏目录检索对您有帮助的部分,其中有emoji表情前缀属于重点部分,觉得对您有帮助的话,melo还会持续更进完善本篇文章和MySQL专栏。
好,现在我们已经掌握了索引的基本原理和使用方法了,要来大干一场优化SQL了!等等,我们要优化什么SQL来着,裤子都脱了,结果没对象可以.....
别着急,这篇既然挂着MySQL高级篇,自然MySQL还是很高级的,给我们提供了几种方法,来为我们找到SQL,并分析SQL。
本篇,我们先着重讲解如何分析,具体如何找到SQL,后续的实战篇,我们再来详细谈一谈。
explain中,包含了如下几个字段(不同版本可能会有所差异):
| 字段 | 含义 |
|---|---|
| id | select查询的序列号,是一组数字,表示的是查询中执行select子句或者是操作表的顺序。 |
| select_type | 表示 SELECT 的类型,常见的取值有 SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION 中的第二个或者后面的查询语句)、SUBQUERY(子查询中的第一个 SELECT)等 |
| table | 输出结果集的表 |
| partitions | 查询时匹配到的分区信息,对于非分区表值为NULL,当查询的是分区表时,partitions显示分区表命中的分区情况。 |
| type | 表示表的连接类型,性能由好到差的连接类型为( system ---> const -----> eq_ref ------> ref -------> ref_or_null----> index_merge ---> index_subquery -----> range -----> index ------> all ) |
| possible_keys | 表示查询时,可能使用的索引 |
| key | 表示查询时,实际使用的索引 |
| key_len | 索引字段的长度,可用来区分长短索引 |
| rows | 扫描行的数量 |
| filtered | 表里符合条件的记录数所占的百分比 |
| extra | 执行情况的说明和描述 |
看完是不是很懵,感觉好多要记忆的,别着急,下边我们通过实际案例,来加深记忆
id 字段是 select查询的序列号,是一组数字,表示的是查询中执行select子句或者是操作表的顺序。
id 情况有三种 :
此处是嵌套子查询,最内部的子查询,自然是最先执行的
| SELECT_TYPE | 含义 |
|---|---|
| SIMPLE | 简单的select查询,查询中不包含子查询或者UNION |
| PRIMARY | 查询中若包含任何复杂的子查询,最外层查询标记为该标识 |
| SUBQUERY | 在SELECT 或 WHERE 列表中包含了子查询 |
| DERIVED | 在FROM 列表中包含的子查询,被标记为 DERIVED(衍生) MYSQL会递归执行这些子查询,把结果放在临时表中 |
| UNION | 若第二个SELECT出现在UNION之后,则标记为UNION ; 若UNION包含在FROM子句的子查询中,外层SELECT将被标记为 : DERIVED |
| UNION RESULT | 从UNION表获取结果的SELECT |
| TYPE | 含义 |
|---|---|
| NULL | MySQL不访问任何表,索引,直接返回结果 |
| system | 表只有一行记录(等于系统表),这是const类型的特例,一般不会出现 |
| const | 表示通过索引一次就找到了,const 常用于primary key 或者 unique 索引(本质上都是唯一索引)。因为只匹配一行数据,所以很快。如将主键置于where列表中,MySQL 就能将该查询转换为一个常量。const于将 "主键" 或 "唯一" 索引的所有部分与常量值进行比较 |
| eq_ref | 类似ref,区别在于使用的是唯一索引,使用主键的关联查询,关联查询出的记录只有一条。常见于主键或唯一索引扫描 |
| ref | 非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,返回所有匹配某个单独值的所有行(多个) |
| range | 只检索给定返回的行,使用一个索引来选择行。 where 之后出现 between , < , > , in 等操作。 |
| index | index 与 ALL的区别为 index 类型只是遍历了索引树, 通常比ALL 快, ALL 是遍历数据文件。 |
| all | 将遍历全表以找到匹配的行 |
结果值从最好到最坏以此是:
NULL > system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
system > const > eq_ref > ref > range > index > ALL
一般至少要达到range级别,最好达到ref 。
唯一索引,非关联查询
eq_ref 跟 const 的区别是:两者都利用唯一索引,但前者是关联查询,后者只是普通查询?
eq_ref 跟 ref 的区别:后者是非唯一索引
都是读全表,区别在于index是遍历索引树读取,而ALL是从硬盘中读取。
不走索引就会遍历全表
possible_keys : 显示可能应用在这张表的索引, 一个或多个。
key :实际使用的索引, 如果为NULL, 则没有使用索引。(可能是没有走索引,需要分析)
key_len : 表示索引中使用的字节数, 在不损失精确性的前提下, 长度越短越好 。
显示索引的哪一列被使用了,如果可能的话,是一个常数。
t1.id是索引,且=号后边不是常量,故显示t1.id,即显示相应关联表的关联字段
扫描行的数量,一般越小越好
表里符合条件的记录数的所占的百分比。
其他的额外的执行计划信息,在该列展示 ,需要把前两个优化为using index。
| EXTRA | 含义 |
|---|---|
| using filesort | 说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取,表示无法利用索引完成的排序操作, 称为 “文件排序”, 效率低。 |
| using temporary | 使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于 order by 和 group by; 效率低 |
| using index | 表示相应的select操作使用了覆盖索引, 直接从索引中过滤掉不需要的结果,无需回表, 效率不错。 |
| using index condition | 索引下推!!查找使用了索引,但是需要回表查询数据,此时就是因为索引列没有完全包含查询列 |
具体using index condition中的索引下推是什么意思,可以参考这篇 索引的原理&&设计原则
不同版本好像不一样
5.7:表示 MySQL 首先从数据表(存储引擎)中读取记录,返回给 MySQL 的 server 层,然后在 server 层过滤掉不满足条件的记录,即无法直接在存储引擎过滤掉。
简单来说,就是查询时where中用的不是索引。
现在,我们知道怎么用explain来分析SQL语句了,自然可以来剖析我们的SQL语句的性能,不过早有先人给我们总结了几个需要优化的场景-->索引失效
create table `tb_seller` (
`sellerid` varchar (100),
`name` varchar (100),
`nickname` varchar (50),
`password` varchar (60),
`status` varchar (1),
`address` varchar (100),
`createtime` datetime,
primary key(`sellerid`)
)engine=innodb default charset=utf8mb4;
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('alibaba','阿里巴巴','阿里小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('baidu','百度科技有限公司','百度小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('huawei','华为科技有限公司','华为小店','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('itcast','传智播客教育科技有限公司','传智播客','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('itheima','黑马程序员','黑马程序员','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('luoji','罗技科技有限公司','罗技小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('oppo','OPPO科技有限公司','OPPO官方旗舰店','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('ourpalm','掌趣科技股份有限公司','掌趣小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('qiandu','千度科技','千度小店','e10adc3949ba59abbe56e057f20f883e','2','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('sina','新浪科技有限公司','新浪官方旗舰店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('xiaomi','小米科技','小米官方旗舰店','e10adc3949ba59abbe56e057f20f883e','1','西安市','2088-01-01 12:00:00');
insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) values('yijia','宜家家居','宜家家居旗舰店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
-- 创建联合索引
create index idx_seller_name_sta_addr on tb_seller(name,status,address);
所谓最左前缀,可以想象成一个爬楼梯的过程,假设我们有一个复合索引:name,status,address,那这个楼梯由低到高依次顺序是:name,status,address,最左前缀,要求我们不能出现跳跃楼梯的情况,否则会导致我们的索引失效:
这两个尽管where中字段的顺序不一样,第二个看起来越级了,但实际上效果是一样的
其实是因为我们MySQL有一个Optimizer(查询优化器),查询优化器会将SQL进行优化,选择最优的查询计划来执行。
范围查询之后的索引字段,会失效!!!但本身用来范围查询的那个索引字段依然有效,如图中的status。
对索引字段做运算,使用函数等都会导致索引失效。
索引字段为字符串类型,由于在查询时,没有对字符串加单引号,MySQL的查询优化器,会自动的进行类型转换,造成索引失效。
尽量使用覆盖索引(索引列完全包含查询列),减少select *
当查询列中包含了非索引项,虽然我们还是能够利用到索引,但是为了获取非索引项字段,我们需要回表去查询数据,效率会比较低。
用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。
示例,name字段是索引列 , 而createtime不是索引列,中间是or进行连接是不走索引的 :
explain select * from tb_seller where name='黑马程序员' or createtime = '2088-01-01 12:00:00'\G;
可以联系字典树Trie的匹配吧。
比如要找‘abc’,如果是%bc,一开始的根都找不到了,自然没办法利用到索引树
而如果是ab%,还能利用到前两个。
%开头的失效,%结尾的还能利用索引(实际上这里就相当于字符串的最左前缀原则,可以这么理解)
当真的需要两边都使用%来模糊查询时,只有当 作为模糊查询的条件字段(例子中的name)以及 想要查询出来的数据字段(例子中的 name & status & address)都在索引列上时,才能真正使用索引。
关于覆盖索引,可以参考这篇 -> 索引原理,设计原则
此处是由于数据的特殊性,‘北京市’所占的比例很高,还不如全表扫描
本质上跟上边是一样的
MySQL底层会自动判断,如果全表扫描快,则直接使用全表扫描,不走索引。如果表中该索引列数据绝大多数是非空值,则使用 is not null的时候走索引,使用 is null的时候不走索引(还不如全表扫描快),全表扫描;反之亦然。
如果表中is null的比较多,那自然就直接全表扫描,如果is null的很少,会走索引。
为了方便测试,我们单独建了一个status索引,观察该表数据,status中2很少,而1很多。
所以in('1')的话,不如走全表,没有用到索引
in('2')就会走索引
我们建立索引的时候,对于数据分布均匀且重复的字段,我们一般不考虑对其添加索引,因为此时MySQL会认为全表更快,会走全表扫描而非索引,导致我们的索引失效。
使用不等式也会导致索引失效
说完几个索引失效的场景,下边呢,是我们具体的应用场景,在如下几种特定情况下,我们需要采取不同的SQL优化方式,或采用索引,或利用外部条件
CREATE TABLE `tb_user_2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(45) NOT NULL,
`password` varchar(96) NOT NULL,
`name` varchar(45) NOT NULL,
`birthday` datetime DEFAULT NULL,
`sex` char(1) DEFAULT NULL,
`email` varchar(45) DEFAULT NULL,
`phone` varchar(45) DEFAULT NULL,
`qq` varchar(32) DEFAULT NULL,
`status` varchar(32) NOT NULL COMMENT '用户状态',
`create_time` datetime NOT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_user_username` (`username`) -- 唯一性约束
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
适当的设置可以提高导入的效率。
对于 InnoDB 类型的表,有以下几种方式可以提高导入的效率:
因为InnoDB类型的表是按照主键的顺序保存的,所以将导入的数据按照主键的顺序排列,可以有效的提高导入数据的效率。如果InnoDB表没有主键,那么系统会自动默认创建一个内部列作为主键,所以如果可以给表创建一个主键,将可以利用这点,来提高导入数据的效率。
脚本文件介绍 :
sql1.log ----> 主键有序
sql2.log ----> 主键无序
插入主键顺序排列数据:
主键无序:
执行:set global local_infile=on;
其实我们开启之后, 需要退出重新连接,再次连接时便可以正常操作了
mysql --local_infile=1 -u root -ppassword
在导入数据前执行 SET UNIQUE_CHECKS=0,关闭唯一性校验,在导入结束后执行 SET UNIQUE_CHECKS=1,恢复唯一性校验,可以提高导入的效率。
CREATE TABLE `emp` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`age` int(3) NOT NULL,
`salary` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
insert into `emp` (`id`, `name`, `age`, `salary`) values('1','Tom','25','2300');
insert into `emp` (`id`, `name`, `age`, `salary`) values('2','Jerry','30','3500');
insert into `emp` (`id`, `name`, `age`, `salary`) values('3','Luci','25','2800');
insert into `emp` (`id`, `name`, `age`, `salary`) values('4','Jay','36','3500');
insert into `emp` (`id`, `name`, `age`, `salary`) values('5','Tom2','21','2200');
insert into `emp` (`id`, `name`, `age`, `salary`) values('6','Jerry2','31','3300');
insert into `emp` (`id`, `name`, `age`, `salary`) values('7','Luci2','26','2700');
insert into `emp` (`id`, `name`, `age`, `salary`) values('8','Jay2','33','3500');
insert into `emp` (`id`, `name`, `age`, `salary`) values('9','Tom3','23','2400');
insert into `emp` (`id`, `name`, `age`, `salary`) values('10','Jerry3','32','3100');
insert into `emp` (`id`, `name`, `age`, `salary`) values('11','Luci3','26','2900');
insert into `emp` (`id`, `name`, `age`, `salary`) values('12','Jay3','37','4500');
create index idx_emp_age_salary on emp(age,salary);
直接能在索引列完成查询,无需回表,关于回表查询,可以参考 这篇文章 ,此时需要保证所查询的字段都是索引字段,才会是using index
但这个不太现实,不可能说我们要查的,都是索引的字段,所以很多情况下,我们并没有办法把using filesort优化为using index,只能退而求其次,尽量从filesort的角度去优化,通过外部条件。
比如图中的 select id,age,salary from emp order by salary,age;
为什么呢?这里我们得回顾一下复合索引是如何存储的,比如:我们建立一个复合索引(name,status,address),索引中也是按这个字段来存储的,类似图中表格这样:
复合索引树(只存储索引列和主键用于回表),而且是先按name排序,name相同了再按status排序,以此类推
| name | status | address | id(主键) |
|---|---|---|---|
| 小米1 | 0 | 1 | 1 |
| 小米2 | 1 | 1 | 2 |
所以如果我们不按照索引的先后顺序来order by的话,就跟索引树中的排序规则不一样了,索引此时排好的序,我们都没办法合理利用到,自然MySQL不会去走索引了。
对于Filesort , MySQL 有两种排序算法:
以这条SQL语句为例,我们来看看他是怎么执行的:
select * from emp where age=1 order by salary;
1) 两次扫描算法 :MySQL4.1 之前,使用该方式排序。
①首先根据where条件,过滤得到相应的满足age=1的salary,取出排序字段salary和对应的行指针信息(用于回表),然后在排序区 sort buffer 中排序,如果sort buffer不够,则在临时表 temporary table 中存储排序结果。
②完成排序之后,再根据行指针回表读取所有字段,而次该操作可能会导致大量随机I/O操作,是我们需要改进的地方。
这就是所谓的两次扫描,第一次扫描,我们拿到的只是排序字段,然后在sort buffer排好序;第二次扫描,才去回表读取所有字段,最终返回。
该如何优化呢?为什么要分成两次,有没有一种可能是空间不够呢?那我们如果有足够的空间,以空间换时间,是不是就可以开辟出一种新的方法,只需要一次扫描即可
2)一次扫描算法:一次性取出满足条件的所有字段,然后在排序区 sort buffer 中排序后直接输出结果集。排序时内存开销较大,但是排序效率比两次扫描算法要高,典型的以空间换时间的思想。
具体使用哪种算法呢?
MySQL 通过比较系统变量 max_length_for_sort_data 的大小和 Query语句取出的字段总大小, 来判定使用那种排序算法,如果max_length_for_sort_data 更大,那么使用一次扫描算法;否则使用两次扫描算法。
① 增大前者 max_length_for_sort_data
可以适当 max_length_for_sort_data 系统变量,来增大排序区的大小,提高排序的效率,这是典型的空间换时间的思想。
② 减小后者 Query语句取出的字段总大小
如果内存实在不够富裕的话,我们可以减少查询的字段,避免select *
③ 提高 sort_buffer_size :
由上文可知,通过增大该参数,可以让 MySQL 尽量减少在排序过程中对须要排序的数据进行分段,避免需要使用到临时表 temporary table 来存储排序结果,再把多次的排序结果串联起来。
可惜,MySQL无法查看它用了哪个算法。如果增加了max_Length_for_sort_data变量的值,磁盘使用率上升了,CPU使用率下降了,并且Sort_merge_passes状态变量相对于修改之前开始很快地上升,也许是MySQL强制让很多的排序使用了一次扫描算法。
具体的实战修改过程,需要结合MySQL中另一个工具--trace分析优化器,来分析执行计划,后续有机会,我们再来详细聊一聊!
由于GROUP BY 实际上也同样会进行排序操作,而且与ORDER BY 相比,GROUP BY 主要只是多了排序之后的分组操作。当然,如果在分组的时候还使用了其他的一些聚合函数,那么还需要一些聚合函数的计算。所以,在GROUP BY 的实现过程中,与 ORDER BY 一样也可以利用到索引。
先来看看无索引的情况:using temporary;using filesort
create index idx_emp_age_salary on emp(age,salary);
如果查询包含 group by 但是用户想要避免排序结果的消耗, 则可以执行order by null 禁止排序。如下 :
Mysql4.1版本之后,开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易。
但是,有些情况下,子查询是可以被更高效的连接(JOIN)替代的!!
示例 ,查找有角色的所有的用户信息 :
explain select * from t_user where id in (select user_id from user_role );
执行计划为 :
优化后 :
explain select * from t_user u , user_role ur where u.id = ur.user_id;
连接(Join)查询之所以更有效率一些 ,是因为MySQL不需要在内存中创建临时表来完成这个逻辑上需要两个步骤的查询工作。
对于包含OR的查询子句,如果要利用索引,则OR之间的每个条件列都必须用到索引 , 而且不能使用到复合索引; 如果没有索引,则应该考虑增加索引。
我们此处有一个id主键索引,和一个age,salary复合索引:
explain select * from emp where id = 1 or age = 30;
实际上等效于range,此处只是提供一个示例
SQL提示,是优化数据库的一个重要手段,简单 来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。
在查询语句中表名的后面,添加 use index 来提供希望MySQL去参考的索引列表,就可以让MySQL不再考虑其他可用的索引。
如果用户只是单纯的想让MySQL忽略一个或者多个索引,则可以使用 ignore index 作为 hint 。
强制走索引,即使MySQL认为全表更快,我们用force也可以强制走索引。
一个常见又非常头疼的问题就是 limit 2000000,10 ,此时需要MySQL排序 前2000010 记录,仅仅返回2000000 - 2000010 的记录,其他记录丢弃,查询排序的代价非常大 。
比如我们有这样一条语句,select * from tb_item limit 2000000,10 ;
此时默认是根据 id 排序的。
在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容。
该方案适用于主键自增的表,可以把Limit 查询转换成某个位置的查询 。(局限性:主键不能断层)
比如我们需要插入三条数据:
insert into tb_test values(1,'Tom');
insert into tb_test values(2,'Cat');
insert into tb_test values(3,'Jerry');
此时需要建立三次连接,每次连接都要消耗资源,为了提高单次连接的执行效率,我们会采取:
insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
同时,insert的时候最好是保持数据的有序插入
explain分析SQL中,其中比较重要的主要是type,key,ref以及extra,我们不需要死记硬背,多拿几条语句去explain比对比对,更有利于我们辅助记忆。
索引失效的几个场景,借用b站热评:
全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
Like百分写最右,覆盖索引不写星;
不等空值还有or,索引失效要少用;
VAR引号不可丢,SQL高级也不难!
最后,能看到这里真的是很不容易了,其实这篇文章,相比上篇,更多还是以黑马课程的思路来整理的,自己只是在其中补充和完善了一下小案例,指正了小错误,受众可能更偏向于b站视频用户。
当然了,这也只是初级阶段,跟着视频学,整理笔记,初级阶段过后呢,就需要在此基础上,去对实际项目中的SQL去优化,优化过后还需要深入理解如此优化的原理,本文的order by,还没有过多深入其底层原理,只是大概提及到了有 sort_buffer 这么个东西,以及对应的两种扫描算法,order by底层到底是如何实现的,怎么通过本文的方法,去优化诸如以下这种语句:
select * from table order by xxx;
这个xxx,不管是不是索引,按这样去查询的话,大概率都是using filesort且不会用到索引的,除非说我们去limit xx,这个xx还要很小,才会使用到索引。
这些,才是我们更进一步的底梁柱,笔记大家都有,只是谁整理得好看一点,多了一些自己的思考罢了。
这两篇,我们了解了索引的理论知识,并知道了如何避免索引失效,分析并优化SQL,SQL语句优化方面也暂时告一段落了,当然了,SQL优化也可以通过MySQL其他方面来进行,工具,日志,并发参数等,这些我们等到后边再详细了解。
下一篇呢,我们先把目光放到MySQL本体上,了解了解MySQL的逻辑架构与存储引擎
收藏=白嫖,点赞+关注才是真爱!!!本篇文章如有不对之处,还请在评论区指出,欢迎添加我的微信一起交流:Melo__Jun
我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou
我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun
我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳