所有由luoben137发布的文章

使用索引解决mysql深度分页问题

 先说解决方案:覆盖索引+延迟关联。

比如查询语句如下:

select * from demo order by a ASC, b ASC, c ASC limit 1000000, 10;

给 id 列建主键,给(a,b,c)建联合索引:

create index idx_a_b_c on demo(a,b,c);

然后延迟关联,即先查主键 id,然后根据 id 查其它字段:

select a,b,c from demo,(select id from demo order by a ASC, b ASC, c ASC limit 1000000, 10) t where demo.id=t.id;

详细原理如下:

随着 mysql limit 的值越来越大,查询可能会非常慢,比如:

select * from demo order by a ASC, b ASC, c ASC limit 1000000, 10;

慢的真正原因,不是因为扫描了 1000000 行,而在于把这 1000000 万行数据重新排序。

explain 如下:

可以看到 Extra 是 Using filesort,表示外部排序,

索引有两个功能:查找和排序。

大家一般对索引的查找功能比较了解,却忽视了索引的排序功能,索引中的数据是已经排好序的,如果从索引中拿到的数据顺序跟我们需要排序的顺序是一致的,那就不要重新排序了。

怎么解决 Using filesort 呢?答案是给(a,b,c)这三列建联合索引:

create index idx_a_b_c on demo(a,b,c);

创建完索引之后,索引中的数据是按 a b c 这三列排序的,就是优先按 a 排序,当 a 相同时再按 b 排序,b 也相同就按 c 排序,大致如下:

执行 explain 现仍然是 Using filesort,原因是查询的列是*,包括了所有列,不能在索引中直接拿到最终查询数据,还需要回表查询,实现不了索引覆盖。

延迟关联:

延迟关联就是指先拿到主键 id,然后再根据 id 查询 select *。根据主键查找差不多能达到二分查找的速度,所以非常快。

给 id 列创建主键,然后 select id:

select id from demo order by a ASC, b ASC, c ASC limit 1000000, 10;

Extra 的中 Using index 就表示“覆盖索引”,表示整个查询过程仅读取了索引中的数据而没有回表查询。

合并在一个 sql 语句中:

select a,b,c from demo,(select id from demo order by a ASC, b ASC, c ASC limit 1000000, 10) t where demo.id=t.id;

mysql B+树索引:

主键索引和辅助索引(二级索引):

mysql 的主键索引叶字节点存的是主键所对应行的整行的全量数据,辅助索引(除主键索引以外的索引)叶字节点上存的是主键的值。

由于前面已经给(a,b,c)三列建了联合索引,所以只要select后面的字段在 a,b,c,id这4列中,就可以直接在索引中拿到最终查询结果。由于联合索引是按(a,b,c) 这3列按优先级从高到低顺序排序的,所以 索引中的数据已经按 order by a ASC, b ASC, c ASC 排好序了,拿出来直接使用,不用再外部排序。

(a,b,c)三列的联合索引的叶子节点上是主键的值,即id,并且当不同行的a、b、c相等时,id是有序的;即(a,b,c)的联合索引可以认为是最右侧为主键的联合索引:(a,b,c,id)。

如果 order by 的字段不是按联合索引的列最左匹配的,比如跳过了a b 直接对 c 排序,因为索引中 c 列是无序的,所以需要外部重新排序。

select id from demo order by c ASC limit 1000000, 10;

查找的结果集,可以不是索引中连续的结果集(结果集为 16 17 18 25 26 27),只要最终顺序和索引中的顺序一致,也可以实现覆盖索引,不用重排序:

对于索引的查找功能,最左匹配是指 where 条件中 and 的列;

对于索引的排序功能,最左匹配是指 order by 中的列;

总之,如果要用索引中现成的排序,order by 的字段必须按索引的顺序从左到右,中间不能中断(除非该列在where中指定了值,比如where a=1,则a可以不用参与排序),并且必须所有字段全升序或者全降序(叶子节点的双向链表可以正向/反向读取)。

由于 B+树是用双向链表将所有叶子节点串起来的,所以索引的范围查找会非常快,也就是说,只要走了 覆盖索引,limit 就算扫描过了 100 万条数据,耗时也会非常短。

100万条数据的扫描和排序,只需要 0.3 秒:

总结:

mysql深度分页问题的根因,不是因为扫描了大量数据,而是大量数据的重新排序太耗时,只要不重排序,就算扫描了大量数据,也不会有性能问题。

要使 深度分页 达到最优性能,explain 的 Extra 中一定不能有 Using filesort,一定要有 Using index。

使用mail,mailx,sendmail,curl发送邮件

【mail】
echo hello | mail -s ‘hello’ roben@test.com

【mailx】
echo “this is content” | mailx -s “this is title” roben@test.com

【sendmail】
{
echo “From: roben@test.com”
echo “To: roben@test.com”
echo “Subject: This is subject”
echo “This is body”
} | /usr/sbin/sendmail -t

 
【curl】
curl -V 查看版本;要支持SMTP(或者POP3)协议,curl的版本必须高于7.20(含)

发送邮件的命令如下:
需要认证:
curl -v -s –url “smtp://192.168.170.130:25″ –mail-from “roben@test.com” –mail-rcpt “roben@test.com” –upload-file mail.txt –user “roben:xxxxxx”

不需要认证:
curl -v -s –url “smtp://192.168.170.130:25″ –mail-from “roben@test.com” –mail-rcpt “roben@test.com” –upload-file mail.txt

mail.txt 内容如下,注意要有空行
cat mail.txt
From:roben@test.com
To:roben@test.com
Subject: this is subject

this is body
参数说明:
-v 回显调试信息
–url :smtp地址
–mail-from:发件人邮箱
–mail-rcpt:收件人邮箱
–upload-file:信件内容,包含发件人、收件人、标题、内容
–user:账号密码,中间用冒号分隔
[root@localhost Roben]# curl -v -s –url “smtp://192.168.170.130:25″ –mail-from “roben@test.com” –mail-rcpt “roben@test.com” –upload-file mail.txt
* About to connect() to 192.168.170.130 port 25 (#0)
* Trying 192.168.170.130…
* Connected to 192.168.170.130 (192.168.170.130) port 25 (#0)
< 220 test ESMTP Postfix
> EHLO mail.txt
< 250-test
< 250-PIPELINING
< 250-SIZE 10240000
< 250-VRFY
< 250-ETRN
< 250-AUTH LOGIN PLAIN
< 250-ENHANCEDSTATUSCODES
< 250-8BITMIME
< 250 DSN
> MAIL FROM:<roben@test.com> SIZE=102
< 250 2.1.0 Ok
> RCPT TO:<roben@test.com>
< 250 2.1.5 Ok
> DATA
< 354 End data with <CR><LF>.<CR><LF>
} [data not shown]
* We are completely uploaded and fine
< 250 2.0.0 Ok: queued as EAF6284038
* Connection #0 to host 192.168.170.130 left intact
[root@localhost Roben]#

两个postfix之间相互发邮件

首先为两台机器配置好hostname,配置hostname方法如下:

export HOSTNAME=test.com
echo $HOSTNAME>/etc/HOSTNAME
/etc/rc.d/boot.localnet stop
/etc/rc.d/boot.localnet start

配置OK:

test:/etc/postfix # hostname
test
test:/etc/postfix #

修改/etc/hosts,在其中指定host和ip的对应关系

test:/etc/postfix # cat /etc/hosts
192.168.170.130 test.com test
192.168.170.134 test2.com test2
test:/etc/postfix #

然后为postfix配置smtp_host_lookup,该参数表示在转发邮件时如何查询主机;native表示在本地/etc/hosts中查找,dns表示通过dns服务器查找

postconf -d 打印默认配置

postconf 打印当前配置

test:/ # postconf -d smtp_host_lookup
smtp_host_lookup = dns
test:/ # postconf smtp_host_lookup
smtp_host_lookup = native, dns
test:/ #

默认只查dns,需要增加native:

smtp_host_lookup = native, dns

这样两台机器就可以相互转发邮件了

另外要注意,mydestination 表示本机的域名,并不是目的机器的域名

不支持方法getClob(String),不应调用它

.在sybase环境中用mybatis的时候,一个错误

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.lang.UnsupportedOperationException: 不支持方法 com.sybase.jdbc3.jdbc.SybResultSet.getClob(String),不应调用它。

向同事了解,是跟CLOB相关。

SQL CLOB 是内置类型,它将字符大对象 (Character Large Object) 存储为数据库表某一行中的一个列值。默认情况下,驱动程序使用 SQL locator(CLOB) 实现 Clob 对象,这意味着 CLOB 对象包含一个指向 SQL CLOB 数据的逻辑指针而不是数据本身。Clob 对象在它被创建的事务处理期间有效。
mybatis 校验字段长度,当长度大于等于256时自动用clob封装,而因为使用的map接收的返回值,直接导致接收该字段的时候报错。
查看一下表,果然有一列的长度为256,修改为255就OK了。

http响应头中设置 Cache-Control: no-cache 时, IE10及以下版本无法显示 favicon

http响应头中设置 Cache-Control: no-cache 时, IE10及以下版本无法显示 favicon
微软官方论坛解释如下:
https://blogs.msdn.microsoft.com/jeffdav/2007/03/01/why-doesnt-the-favicon-for-my-site-appear-in-ie7/
Q: I verified that my favicon really is an icon, but it still doesn’t appear.
A: Since IE loads your icon out of the Temporary Internet Files folder, it must be able to actually store it there.  If you are setting the no-cache directive for the icon file, then IE will not be able to display your icon and will display the default icon instead.  You can use Fiddler to verify.

 

javax.mail 升级到 1.5.6 后,发邮件附件名比较长时,附件名显示异常

javax.mail 升级到 1.5.6 后,发邮件附件名比较长时,附件名显示异常

解决方案:

在new MimeMessage、new MimeMultipart、new MimeBodyPart 之前(一个比较靠前的位置,如果在new MimeMultipart之后添加,有可能无效),添加如下代码。

System.getProperties().setProperty(“mail.mime.splitlongparameters”, “false”);

一个很诡异的样式问题

有一次做完界面后,在本地调试显示是OK的;但是版本出来后,发现很多样式没有生效。定位发现css文件也引了,css文件里面的样式也在,但是样式就是没有生效。

将本地的样式文件拷到服务器上就OK了;本地的文件是没有压缩的,服务器上的是出版本的时候通过工具压缩的。当时就怀疑是哪个地方有语法错误引起的。

通过一部分一部分地删除代码,最终定位到了具体行,大概如下:

.site-title {margin: 0;”}

原因是多了一个双引号,而浏览器解析css文件时,如果遇到了语法错误,就会不会再解析后面的样式了;压缩之前由于样式是多行展示的,可能浏览器认为多一个双引号也可以:

.site-title {
margin: 0;”
}

 

html5 postMessage解决跨域、跨窗口消息传递

postMessage(data,origin)方法接受两个参数

data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器(比如IE8)只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。json2.js 的下载地址为:https://github.com/douglascrockford/JSON-js

用法:

var aa = {aa:1,bb:2}
var cc = JSON.stringify(aa)

cc
“{“aa”:1,”bb”:2}”

JSON.parse(cc)
Object {aa: 1, bb: 2}

JS关于多个函数多个参数如何动态调用,apply的灵活应用

转自:http://goodscript.iteye.com/blog/1164598

问题请看代码中的注解:

//回调函数1
function callback(a,b,c)
{
alert(a+b+c);
}
//回调函数2
function callback2(a,b)
{
alert(a+b);
}
//这个方法做了一些操作、然后调用回调函数
function doCallback(fn,args)
{
//do something
if(args.length==2)
{
fn.call(this,args[0],args[1])
}
if(args.length==3)
{
fn.call(this,args[0],args[1],args[2])
}
//假如参数有很多个的话 应该怎么写?难道一致if下去吗?
}

function test()
{
//动态调用方法、并传递参数
doCallback(callback2,[‘a’,’b’]);
doCallback(callback,[‘a’,’b’,’c’]);
}

 

后来经过网友的帮助又找到了一种解决的方法

//回调函数1
function callback(a,b,c)
{
alert(a+b+c);
}
//回调函数2
function callback2(a,b)
{
alert(a+b);
}
//这个方法做了一些操作、然后调用回调函数
function doCallback(fn,args)
{
var executeStr=”fn.call(this,@)”;
for(var i =0;i<fn.length;i++)
{
executeStr = executeStr.replace(“@”,”args[“+i+”],@”);
}
executeStr=executeStr.replace(“,@”,””);
eval(executeStr);
}

function test()
{

//动态调用方法、并传递参数
doCallback(callback2,[‘a’,’b’]);
doCallback(callback,[‘a’,’b’,’c’]);
}

 

最后发现、最佳的实现方式是:

//回调函数1
function callback(a,b,c)
{
alert(a+b+c);
}
//回调函数2
function callback2(a,b)
{
alert(a+b);
}
//这个方法做了一些操作、然后调用回调函数
function doCallback(fn,args)
{
fn.apply(this, args);
}

function test()
{

//动态调用方法、并传递参数
doCallback(callback2,[‘a’,’b’]);
doCallback(callback,[‘a’,’b’,’c’]);
}

通过以上三种方法更加清晰的看到apply在javascript中的作用、以及其强大的功能