TCP建立连接时一般要发送三次包,也就是俗称的三次握手。首先客户端向服务器端发送一个建立连接请求,告诉服务器自己的序列号;服务器收到这个请求包后,进行确认,同时告诉客户端自己的序列号;之后客户端对这个包进行确认。如果一切正常,三次握手就已经完成。

在网络状况不好的时候,如果在发送的过程中任意一个包丢失会怎样呢?

首先来看看第一个包。这种情况显而易见,如果客户端建立连接的请求包丢失,那么服务器端根本不知道有这么一个请求,客户端只有重新发送这个包。

再来看看第二个包。这种情况也是显而易见,如果服务器的确认包丢失,那么客户端无法知道服务器是否收到这个请求,此时服务器端必须再次发送这个确认包。

最后来看看第三个包。这种情况就不是那么显而易见了。如果客户端的确认包丢失,一个明显的解决办法是客户端再次发送这个确认包,然而这是不可行的,因为即使再次发送,客户端依然不知道服务器端是否收到这个确认包。解决的办法是服务器端再次发送三次握手的第二个包。这样就可以说明服务器端没有收到客户端的确认包,所以它需要再次发送第二个包,当客户端再次收到这个包时,也就知道自己此前发送的确认包丢失了,于是再次发送确认包。

联系作者

有些时候,想让脚步运行在后台中,而且只存在一个这样的脚步,这时一种可行的方法是将脚步的运行的进程ID写在一个文件中,当再次运行这个脚步时,去读取这个文件,读出ID,如果这个ID有进程在运行,就退出。这时需要知道进程ID,而在shell中,它是一个特殊变量,也就是$$.当运行shell脚步时,$$就是输出进程ID。

而有些时候,在shell中会编写一些函数,并返回结果,这是需要一个变量保存函数运行结果。这个变量就是$?.

而又有些时候,在shell中想知道运行脚步的名字,这个变量就是$0.

还有许多很有用的特殊变量,只是我不知道还有哪些。google之后可以知道,只是很好奇的是,这些人是怎么知道这些变量的呢?

联系作者

很早之前,在使用Sphinx搭建搜索服务时,遇到这个问题,到Sphinx for Chinese的群里请教,没有得到满意的答案,于是将sql_query_info 这个选项注释掉,就没有报错了。今天正好有时间,于是着手找到这个问题的症结,也算是为Sphinx做点贡献。

打开源代码,才发现用的是匈牙利命名法,看得不爽。也许因为没有Lucene那么出名,只有两个人在维护这个项目,代码里到处充斥这Fix Me,还好结构还算精良,要不然真不知道从和看起。本来想用GDB调试的,还不太熟练,于是就只好用最原始的printf输出。经过缩小范围,找到了一些蛛丝马迹,
在search.cpp中 的第331附近,主要的查询工作就在这里完成的,跳转过去之后
if ( !pIndex->MultiQuery ( &tQuery, pResult, 1, &pTop, NULL ) )
锁定了到下面这个函数
在sphinx.cpp中 17301 if ( !sphCheckQueryHeight ( tParsed.m_pRoot, pResult->m_sError ) )
继续跳转,到了下面这行
在sphinx.cpp中 16404 int64_t iQueryStack = sphGetStackUsed() + iHeightSPH_EXTNODE_STACK_SIZE;
输出之后,发现问题出在sphGetStackUsed这个函数里
在sphinxstd.cpp 中 1218行 int64_t sphGetStackUsed()
继续跳转,
sphinxstd.cpp 中 1221行
BYTE cStack;
BYTE
pStackTop = (BYTE*)sphMyStack();
线程栈的使用大小就是上面两个值的差,继续查找
在sphinxstd.cpp return sphThreadGet ( g_tMyThreadStack );

这里用到了线程私有数据,看到私有数据的设置还是很正常,所以依然不知道哪里出了问题。于是索性将
int64_t iQueryStack = sphGetStackUsed() + iHeightSPH_EXTNODE_STACK_SIZE;
这行改成
int64_t iQueryStack = iHeight
SPH_EXTNODE_STACK_SIZE;
这样sql_query_info就可以使用了,也不会再报query too complex not enough stack错误。
可是这个自己查询得到的中文显示出来都是乱码,我认为是没有设置SET NAMES utf8的原因,但又无法在sql_query_info这里添加这句。虽然在sql_query_pre = SET NAMES utf8已经设置了,但是因为不是同一个查询连接,所以无效。

所以最终我得到解决这个错误的结论,那就是注释掉sql_query_info这个选项。最坑人的是,官方的示例中是开启这个选项的。

联系作者

之前说过用Sphinx给同事搭建搜索服务,可是他提了一个要求,也就是文本中有牛皮癣这个词,搜牛皮时也要能搜到牛皮癣,这个要求在经过分词后是不可以完成的。于是只好去寻求一元分词和二元分词的办法。
http://lutaf.com/157.htm 这里看到,“sphinx只要把min_word_len设置为1,并配置charset_table,默认就是单字切分 ”于是试着配置,结果不行。于是只好看文档,在文档中找到,默认情况下,Sphinx已经支持一元分词。
只需设置
charset_type = utf-8 ,
ngram_len = 1,
ngram_chars = U+3000..U+2FA1F
这样,再次搜牛皮时,就可以搜到牛皮癣了。

联系作者

在前面的介绍中,都没有处理更新和删除问题,这里有必要说说。在关于sphinx引擎的一些想法中说过公司所用的引擎中,处理更新和删除的办法是在索引中增加一个属性来标志这条记录是否失效,每次做增量时,就要去主索引和增量索引中更改相应id的属性值,这确实可以解决问题。不过并不是一个很好的解决办法,Sphinx的作者也说过这种方法既麻烦又容易出错。既然有更新和删除这个需求,必然会提供解决的办法,这个办法就是kilst。所谓的klist,就是kill list,按照字面理解,就是删除列表。我们只需要在增量索引中保存一个id列表,搜索时,如果在主索引中搜到相关文档,而文档的id存在于增量索引的id列表中,则这个文档将被丢弃。

这里有一个需要注意的是,当文章被删除时,仅仅通过增量抓取,在增量索引中并不能知道主索引中哪一个文档被删除了,所以这就必须在表中文档被删除时,能够记录下被删除的id,这就需要用到触发器,也需要建立一个辅助表来保存这些id。辅助表的建立如下:

1
2
3
4
create table sphinxklist(
        id integer not null,
        ts timestamp not null
);

触发器的建立如下:

1
2
3
4
5
6
7
8
DELIMITER //
CREATE TRIGGER sphinx_kill
AFTER DELETE ON wp_posts
FOR EACH ROW
BEGIN
        INSERT INTO sphinxklist VALUES (OLD.ID, NOW());

END
//

有了这些准备工作后,我们就可以使用klist了,事实上在之前的配置文件的基础上,只需要修改一点点内容就好了。首先修改主索引

1
2
3
4
5
6
7
8
9
10
11
12
source srcmain : base{
        sql_query_pre = SET NAMES utf8
        sql_query_pre = SET SESSION query_cache_type=OFF
        sql_query_pre = UPDATE sphinx_helper SET main_tmp_maxts=NOW() WHERE appid='blog_search';
        sql_query = \
                SELECT ID, post_title, post_content, UNIX_TIMESTAMP(post_modified) AS post_modified FROM wp_posts WHERE \
                        post_status='publish' AND post_modified < (SELECT main_tmp_maxts FROM sphinx_helper WHERE appid='blog_search');
        sql_query_post_index = UPDATE sphinx_helper SET main_maxts=main_tmp_maxts WHERE appid='blog_search';
        sql_query_post_index = DELETE FROM sphinxklist WHERE ts < (SELECT main_maxts FROM sphinx_helper WHERE appid='blog_search');
        sql_attr_timestamp = post_modified
        sql_field_string = post_title
}

可以看到,相对于之前的配置,这里只添加了一行

1
sql_query_post_index = DELETE FROM sphinxklist WHERE ts < (SELECT main_maxts FROM sphinx_helper WHERE appid='blog_search');

添加这行是为了防止之前运行引擎时留下的id再次被使用。
之后修改临时索引:

1
2
3
4
5
6
7
8
9
10
11
12
source srcdelta_temp : srcmain {
        sql_query_pre = SET NAMES utf8
        sql_query_pre = SET SESSION query_cache_type=OFF
        sql_query_pre = SET @maxtsdelta:=NOW();
        sql_query_pre = UPDATE sphinx_helper SET delta_tmp_maxts=@maxtsdelta WHERE appid='blog_search';
        sql_query = SELECT ID, post_title, post_content, UNIX_TIMESTAMP(post_modified) AS post_modified FROM wp_posts WHERE \
                post_status='publish' AND post_modified >= (SELECT main_maxts FROM sphinx_helper WHERE appid='blog_search')\
                AND post_modified < @maxtsdelta;
        sql_query_killlist = SELECT ID FROM wp_posts WHERE post_modified >= (SELECT main_maxts FROM sphinx_helper WHERE \
                appid='blog_search') AND post_modified < @maxtsdelta UNION SELECT id FROM sphinxklist;
        sql_query_post_index = UPDATE sphinx_helper SET delta_maxts=delta_tmp_maxts WHERE appid='blog_search';
}

也只是添加了一行,也就是将这次抓取的id与sphinxlist中的id合并。
之后还需要修改Shell脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
baseDir=/home/long/sphinxforchinese/blog_search
conf=$baseDir/etc/blog_search.conf
binDir=$baseDir/bin
cd $binDir
while [ true ]
do
        #./indexer -c $conf --merge-klists --rotate --merge delta deltaTemp
        ./indexer -c $conf  --merge-klists --rotate --merge delta delta_temp
        if [ "$?" -eq "0" ]; then
                cat $baseDir/script/post_merge.sql | mysql -u root --password=123456 blog
                ./indexer -c $conf --rotate delta_temp
        fi
        sleep 60
done

这个脚本相对于原来的只增加了–merge-klists这个参数,这个参数的意义是,将delta_temp合并到delta时,并不会删除delta的klist,而是将delta_temp的klist和delta的klist合并,这正是我们想要的。经过这样的变化,一个可以处理更新和删除的main+delta索引就建好了。

感谢Sphinx团队,感谢Sphinx-for-chinese团队,给我们提供了一个这么好用的开源引擎。

联系作者

上篇中,我们介绍了一种建立主索引和增量索引的方法,这种方法有一种不足之处就是会改变主索引,因为每次增量索引都会与主索引合并成新的主索引。为此,我们可以想出另一种解决的办法,每次只改变增量索引,这就需要另外再建立一个临时索引。

这里只需要改变少量地方,一个是增量索引,另外还需新增一个临时索引,具体配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
source srcdelta : srcmain{
        sql_query_pre = SET NAMES utf8
        sql_query_pre = SET SESSION query_cache_type=OFF
        sql_query = SELECT ID, post_title, post_content, UNIX_TIMESTAMP(post_modified) AS post_modified FROM wp_posts WHERE \
                post_status='publish' limit 0;
        sql_query_post_index =
}
source srcdelta_temp : srcmain {
        sql_query_pre = SET NAMES utf8
        sql_query_pre = SET SESSION query_cache_type=OFF
        sql_query_pre = SET @maxtsdelta:=NOW();
        sql_query_pre = UPDATE sphinx_helper SET delta_tmp_maxts=@maxtsdelta WHERE appid='blog_search';
        sql_query = SELECT ID, post_title, post_content, UNIX_TIMESTAMP(post_modified) AS post_modified FROM wp_posts WHERE \
                post_status='publish' AND post_modified >= (SELECT main_maxts FROM sphinx_helper WHERE appid='blog_search')\
                AND post_modified < @maxtsdelta;
        sql_query_post_index = UPDATE sphinx_helper SET delta_maxts=delta_tmp_maxts WHERE appid='blog_search';
}
index delta_temp : main{
        source = srcdelta_temp
        path = /home/long/sphinxforchinese/blog_search/var/data/delta_temp
}

实际上,我们是先建立一个空的增量索引,之后临时索引中的数据慢慢合并到增量索引中。在这里,增量索引很像上篇中的主索引,而临时索引则像上篇中的增量索引。
此时我们需要修改dist_blog_search,即增加临时索引

1
2
3
4
5
6
7
8
9
index dist_blog_search {
    type = distributed
    local = main
    local = delta
    local = delta_temp
    agent_connect_timeout = 1000
    agent_query_timeout = 3000

}

此后还需改变Shell脚本的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
baseDir=/home/long/sphinxforchinese/blog_search
conf=$baseDir/etc/main_delta_temp.conf
binDir=$baseDir/bin
cd $binDir
while [ true ]
do
        ./indexer -c $conf  --rotate --merge delta delta_temp
        if [ "$?" -eq "0" ]; then
                cat $baseDir/script/post_merge.sql | mysql -u root --password=123456 blog
                ./indexer -c $conf --rotate delta_temp
        fi
        sleep 60
done

事实上,改变的内容还是很少的。经过这样的改变,我们就无需再改变主索引了。第一次建立主索引后,就一直保持不变,变化的是增量索引。

联系作者

虽然只建立主索引就可以满足许多应用,但当数据非常多时,每次都重建索引是一件非常耗时的事情,而且每次重建都会浪费CPU,这也是极为不好的。考虑这样一种情况,在数据库中一共有1千万个文档,而每天只新增一万个文档,如果每次都要重建索引,则第一次重建时,是1001万个文档,第二次时是1002万个文档,这都非常耗时的。如果建好主索引后,只对这些新增的一万个数据建一个增量索引,之后把它合并到主索引中,所需的时间将缩短。所以建立main+delta索引是一个不错的选择。

这里依然以之前的博客搜索为例。为了便于做增量,我们需要记录每次抓取的时间,而为了持久保存这个时间,我们需要在数据中建立一个辅助表,建表语句如下

1
2
3
4
5
6
7
8
create table sphinx_helper(
        appid varchar(300) not null primary key,
        main_maxts datetime,
        main_tmp_maxts datetime,
        delta_maxts datetime,
        delta_tmp_maxts datetime
);

insert into sphinx_helper (appid) values ('blog_search');

在wp_posts表中, post_modified这个时间字段是随着每次文章的更新而自动变化的,所以可以使用它来做增量。主要思路就是用一个值来保存上次增量索引的时间,当需要再做增量索引时,则只需索引从这个保存的时间到现在这段时间里的数据。在sphinx_helper中,这个值用main_maxts来标示。对于主索引,写成配置文件如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
source base{
        type = mysql
        sql_host = localhost
        sql_user = root
        sql_pass = 123456
        sql_db = blog
        sql_port = 3306
}
source srcmain : base{
        sql_query_pre = SET NAMES utf8
        sql_query_pre = SET SESSION query_cache_type=OFF
        sql_query_pre = UPDATE sphinx_helper SET main_tmp_maxts=NOW() WHERE appid='blog_search';
        sql_query = \
                SELECT ID, post_title, post_content, UNIX_TIMESTAMP(post_modified) AS post_modified FROM wp_posts WHERE \
                        post_status='publish' AND post_modified < (SELECT main_tmp_maxts FROM sphinx_helper WHERE appid='blog_search');
        sql_query_post_index = UPDATE sphinx_helper SET main_maxts=main_tmp_maxts WHERE appid='blog_search';
        sql_attr_timestamp = post_modified
        sql_field_string = post_title

}

以上就是主索引的配置,之所以需要将NOW()得到的时间保存到数据库中,之后在sql_query_post_index中取出来用,是因为sql_query_post_index和sql_query不是用一个数据连接。而之所以在sql_query_post_index里才更新main_maxts,是为了保证只有在索引成功建立后才更新这个值。而对于增量索引的配置,则如下:

1
2
3
4
5
6
7
8
9
10
source srcdelta : srcmain {
        sql_query_pre = SET NAMES utf8
        sql_query_pre = SET SESSION query_cache_type=OFF
        sql_query_pre = SET @maxtsdelta:=NOW();
        sql_query_pre = UPDATE sphinx_helper SET delta_tmp_maxts=@maxtsdelta WHERE appid='blog_search';
        sql_query = SELECT ID, post_title, post_content, UNIX_TIMESTAMP(post_modified) AS post_modified FROM wp_posts WHERE \
                post_status='publish' AND post_modified >= (SELECT main_maxts FROM sphinx_helper WHERE appid='blog_search')\
                AND post_modified < @maxtsdelta;
        sql_query_post_index = UPDATE sphinx_helper SET delta_maxts=delta_tmp_maxts WHERE appid='blog_search';
}

在sql_query中可以看到,每次增量索引的数据都是在[max_maxts, NOW()]之间,而只在sql_query_post_index中更新delta_maxts也是基于上述理由。剩下的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
index main {
        source = srcmain
        path = /home/long/sphinxforchinese/blog_search/var/data/main
        docinfo = extern
        charset_type = utf-8
        chinese_dictionary = /home/long/sphinxforchinese/blog_search/etc/xdict
}
index delta : main {
        source = srcdelta
        path = /home/long/sphinxforchinese/blog_search/var/data/delta
}

index dist_blog_search {
        type = distributed
        local = main
        local = delta
        agent_connect_timeout = 1000
        agent_query_timeout = 3000
}

这里我们多了一个dist_blog_search,它是结合main和delta的搜索结果,在客户端中搜索时,我们使用dist_blog_search这个索引的结果。剩下的配置与只有主索引时相同,这里就不累述了。

写好配置文件后,还需要有一个步骤。因为我们的策略是每隔一段时间将增量索引与主索引合并,当合并之后,我们需要更新main_maxts这个值。如果我们是每个60秒做一次增量索引,这需要写一个shell脚本,脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
baseDir=/home/long/sphinxforchinese/blog_search
conf=$baseDir/etc/main_delta.conf
binDir=$baseDir/bin
cd $binDir
while [ true ]
do
        ./indexer -c $conf --rotate --merge main delta
        if [ "$?" -eq "0" ]; then
                cat $baseDir/script/post_merge.sql | mysql -u root --password=123456 blog
                ./indexer -c $conf --rotate delta
        fi
        sleep 60
done

先执行

1
 ./indexer -c $conf --rotate --merge main delta

这句是将主索引和增量索引合并,当合并成功时,则需要到数据库中修改main_maxts这个值,这个句子在post_merge.sql中,post_merge.sql的内容如下:

1
2
UPDATE sphinx_helper SET main_maxts=delta_maxts\
        WHERE appid='blog_search';

之后进行增量抓取

1
./indexer -c $conf --rotate delta

这里说说–rotate这个选项,这个选项非常有用。在主索引和增量索引合并时,indexer程序会将这两个索引合并成一个索引,当合并成功后,程序会发送一个SIGHUP信号给searchd,之后searchd就好去加载这个新的索引。

到这里,一个main+delta的索引就完成了。

联系作者

因为一直都对Wordpress自带的搜索功能略有微词,可是又不想去改它,想想自己的博客一天都没有一个人会访问,更不用说这个搜索功能了。因为现在学习使用Sphinx-for-chinese,拿博客的数据来练练手。

先从最简单的情况开始,以后再一步一步的完善功能,这样才符合学习的线路,从易到难,而不是一开始就给你一个很完善的模型,然后改改路径就好了。最简单的情况就是只有一个主索引,然后隔一段时间重建索引。得益于Sphinx的高效,建索引的速度非常快,在文档中说达到了10M/s, 按照一篇文章为4KB计算,一秒钟可以给250篇文章建索引了,对于博客来说,已经足够了。对于其它的应用,当数据不多时,只有一个主索引也是可以的。

这里只使用了wp_posts表中的数据,只是用了ID, post_title, post_content, post_modified四个字段,所以非常的简单,直接上配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
source base{
        type = mysql
        sql_host = localhost
        sql_user = root
        sql_pass = 123456
        sql_db = blog
        sql_port = 3306
}
source srcmain : base{
        sql_query_pre = SET NAMES utf8
        sql_query_pre = SET SESSION query_cache_type=OFF
        sql_query = \
                SELECT ID, post_title, post_content, UNIX_TIMESTAMP(post_modified) AS post_modified FROM wp_posts WHERE \
                        post_status='publish' AND post_modified < NOW();
        sql_attr_timestamp = post_modified
        sql_field_string = post_title

}
index main {
        source = srcmain
        path = /home/long/sphinxforchinese/blog_search/var/data/main
        docinfo = extern
        charset_type = utf-8
        chinese_dictionary = /home/long/sphinxforchinese/blog_search/etc/xdict
}
indexer {
        mem_limit = 32M
}

searchd {
        listen = 9300
        log = /home/long/sphinxforchinese/blog_search/var/log/searchd.log
        query_log = /home/long/sphinxforchinese/var/log/query.log
        read_timeout = 5
        max_children = 30
        pid_file = /home/long/sphinxforchinese/var/log/searchd.pid
        max_matches = 1000
        seamless_rotate = 1
        preopen_indexes = 1
        unlink_old = 1
        workers = threads
        binlog_path = /home/long/sphinxforchinese/var/data
}

相关配置选项的意义可以查看示例,写的非常的详细。这里没有对post_content进行定义,因为只想对这个字段建索引,并不想保存它的原始内容,所以这里使用了默认行为,也就是只建索引。
建好索引,搜索跑步的相关文章,得到如下结果

  1. document=41, weight=2661, post_title=跑步一周年, post_modified=Sun Apr 7 10:11:56 2013
  2. document=286, weight=2660, post_title=跑步两周年, post_modified=Fri Jan 4 12:49:47 2013
  3. document=537, weight=1642, post_title=写在广州马拉松之前, post_modified=Sat Nov 9 00:00:45 2013
  4. document=39, weight=1632, post_title=看棒球英豪漫画, post_modified=Sun Apr 7 09:57:34 2013
  5. document=2, weight=1626, post_title=关于我, post_modified=Fri Jun 14 19:49:08 2013
  6. document=565, weight=1626, post_title=2013广州马拉松纪实, post_modified=Sun Nov 24 22:10:57 2013
  7. document=43, weight=1617, post_title=三个月来的小结, post_modified=Sun Apr 7 10:10:22 2013
  8. document=56, weight=1617, post_title=价值博客们, post_modified=Sun Apr 7 09:52:51 2013
  9. document=205, weight=1617, post_title=2012扬州马拉松纪实, post_modified=Tue Apr 2 11:29:04 2013
  10. document=5, weight=1602, post_title=2011年的阅读, post_modified=Tue May 29 11:19:49 2012
  11. document=305, weight=1602, post_title=羽毛球心结, post_modified=Mon Apr 8 08:33:37 2013
  12. document=40, weight=1574, post_title=通关manufactoria, post_modified=Sun Apr 7 10:01:06 2013
  13. document=233, weight=1574, post_title=当了一回胃扩张, post_modified=Fri Jul 20 15:46:35 2012
    搜索结果还行吧。

联系作者

许多情况下,都需要将mysql的查询结果转成一个数组,这个就可以遍历数组来显示,查询结果。在我的开发环境里,我使用mysqli_fetch_all函数,使用方法如下

1
2
$result = mysqli_query($con, $sql);
$posts =  mysqli_fetch_all($result, MYSQLI_ASSOC);

加上MYSQLI_ASSOC是为了使返回的是关联数组,之后就可以遍历$posts数组。当将这段代码放到线上环境时,发现没有结果,最后才知道原来是mysqli_fetch_all函数无法使用。 google之后才知道,mysqli_fetch_all这个函数只存在于mysqlnd中,也就是PHP的原生MySQL驱动中。原来链接MySQL存在两套驱动,一套是libmysql,一套是mysqlnd。本来mysqlnd是不存在的,后来因为mysql到了Oracle手上之后,驱动的认证就有些问题了,于是PHP开发组自己开发了一套mysql驱动。

可是在linux下,安装mysqli时还是默认使用libmysql,所以要么就得重新安装mysqli模块,使用mysqlnd驱动安装,或者自己来实现mysqli_fetch_all的功能。暂时先自己实现类似的功能。

1
2
3
4
5
$result = mysqli_query($con, $sql);
$posts = array();
while($row = mysqli_fetch_array($result)) {
$posts[] = $row;
}

联系作者

在数据库应用中,时间字段是极为常用的,而timestamp因为有一个很好的特性,所以经常用到。例如将timestamp设置为NOT NULL DEFAULT CURRENT_TIMESTAMP时,在数据第一次插入时,时间会自动设置为当前时间。

而如果再加上ON UPDATE CURRENT_TIMESTAMP,也就是将timestamp类型设置为 NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,这样在更新数据时,就会自动更新为当前时间。如此,就没有必要在更新数据时,使用now函数。

最近在做一个项目,令我意外的是,一个同事竟然不知道有这个类型,所以在更新数据时,他要使用now函数。他说在数据结构设计时,不会关心具体数据库提供的特性。即便如此,我还是认为,数据结构设计还是要接地气的。

联系作者