先说一下笔者曾经应对的情况:
每天数据更新量在几十万以上
每天PV为千万级以上
数据分布在多个数据库下,数据表相同,但更新频率有差异
根据实际情况,使用的硬件包括有多台数据库与索引服务器,索引服务器根据业务启用多个端口运行索引实例,避免单点并负载均衡。
建议环境:
数据库: 一台或多台(MySQL)
索引服务器:一台或多台(Sphinx)
负载均衡: NetScaler或F5(可选)
操作系统: Linux/FreeBSD
一、安装
安装比较简单,在此不再用多说了。
假设我们将sphinx安装在 /usr/local/webserver/sphinx
MySQL安装在 /usr/local/webserver/mysql
二、配置Sphinx
假设我们为了应对大数据量更新以及大的查询量,根据业务需要开启三个不同端口的sphinx实例,配置文件分别为 /usr/local/webserver/sphinx/etc/sphinx_[port].conf
为了保证列表页数据一致性,索引也必须是一致的,所以数据源最好使用同一台服务器(暂不考虑MySQL复制延时的问题),这样维护也比较方便。比如我们的数据源数据库服务器host是srcdb。
比如在sphinx_[port].conf配置中的source配置: sql_host = srcdb
如果需要切换数据库源,只要修改各索引服务器的/etc/hosts文件,将srcdb指向的IP更改即可。
另外,sphinx的配置文件编写,最好能利用好继承关系,这样可以写出更简洁更清淅的配置文件。
具体的配置在这不再赘述,下面我们来设计一个比较合理的索引架构。
三、索引组合方式
使用过Sphinx的朋友如果看过Sphinx的文档,应该对文档中的main + delta索引设计有印象。不过这种设计在应对大数据量并且数据更新比较频繁的应用时,明显会有很大的不足,特别表现在索引的实时性上。因为随着时间的推 移,在main不作变化的情况下,delta需要索引的数据量会越来越大,这样情况造成的影响一是建索引的时间会花费比较多,二是对数据库压力增大,三是 内网的网络开销增大,而且这些影响将与delta建索引的频率成正比。
针对这种情况,我们只需要将delta索引的数据尽量最小化就可以了,我们可以中间增加一个索引,组成main + today + last的索引方式。main索引即主索引可以每天更新一次(重建或合并均可),today索引保存的是当天的数据,N分钟重建一次,last索引索引的 是最新更新的数据,T秒更新一次,主要是将today上一次索引之后到当前时间的更新数据放入索引。所以last索引数据的时间与today索引重建的时 间间隔有关。假如today索引重建的时间间隔为10分钟,那么last索引就应该索引当前时间往前10分钟的数据。这样main + today + last组合起来的索引就将是完整的数据索引,而且因为last索引的数据量小,可以将重建的时间间隔降到最小,从理论上达到了实时索引的效果。
可以看出,main + today + last的索引组合方式,拆分逻辑主要找的是时间点,而不是文档ID,所以我们不能照搬Sphinx文档中利用ID拆分索引的方式(根据ID重建有可能会 因为一个很老的贴子更新了,而遍历大量是无用的数据)。每一行数据记录,应该有一个创建时间与更新时间(假设字段名为update_time),时间点主 要对update_time判断即可。
具体的配置在这也不用再赘述了。
四、索引的重建
main索引的更新,一天做一次即可。方式有重建或合并,都很简单,定时设置使用Linux的crontab就可以了。
假设我们每个端口有一个重建main索引的脚本,比如/usr/local/webserver/sphinx/bin/main_renew_[port].sh
我们在每一台索引服务器上做一下设置
[root@indexserv1 ~]# crontab -e
00 1 * * * /usr/local/webserver/sphinx/bin/main_renew_3312.sh
00 2 * * * /usr/local/webserver/sphinx/bin/main_renew_3313.sh
00 3 * * * /usr/local/webserver/sphinx/bin/main_renew_3314.sh
可以看到,main索引重建的时间有所不同,而且不管如何设置重建的时间,在时间跨过了0点之后main索引更新完之前,today索引应该要有一个处理 逻辑,不能只是创建每一天0点或拟定的拆分时间点之后的数据,否则就有可能在main索引更新完之前丢失一天的数据。这个处理逻辑可以写在source的 sql_query_range里,具体实现根据自身main索引重建的时间做设定。
上面的main索引重建,我们可以看到,使用的是linux的crontab来做的定时任务。现在我们讨论一下today索引的重建。
前面我们提过了,today索引10分钟更新一次。我们是不是也可以像main索引一样,使用crontab来做定时呢?假如只有一台索引服务器,理论上 是可以的。但是如果有多台索引服务器的话,索引服务器之间可能会有几秒的时间差异,而这点时间差异就有可能做成列表页数据不一致的情况。服务器时间差异是 一个问题,这个问题理论上可以使用ntpdate来弥补,但这不是一个好办法。另外,如果是同一端口下有许多的today索引,而数据是来自许多不同的数 据库,如果盲目地只是10分钟重建一次,那会造成有许多冗余的重建操作。比如,有一些数据表可能超过10分钟都没有更新,那重建索引无疑是浪费资源。由此 可以想到,last索引的重建也会有这样的问题。
为了将重建索引对服务器的性能损耗降到最低,我们应该要做到对每一个today、last索引是否需要重建作判断,只要是没有数据更新的,就不需要进行重建。并且为了避免服务器的时间差异,应该要让多台索引服务器同时进行重建。
五、实时的C/S应用
很容易,我们就可以想到C/S架构。每一台索引服务器作为Server端,负责接收重建指令并执行重建。使用一台服务器作为Client端,Client端主要负责对索引是否重建作判断,并根据索引分布规则同时对规则中的服务端发送重建指令。
我们定义一套很简单的规则,Server端接收的命令格式:[port]|[--rotate] [index] [index] [index] |
上面我们使用了”|”作为分割符,为了避免无用数据的干扰,指命后面最好有一个”|”。
(1)server端
我们可以看到sphinx/bin/下面有indexer、search、searchd,那么我们的server端就叫indexerd好了。
手工写一份indexerd.c源码,使用3333端口。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#define SERV_PORT 3333
#define BACKLOG 15
#define MAXSIZE 1000
void split(char **arr, char *str, const char *del)
{
char *s = strtok(str, del);
while(s != NULL)
{
*arr++ = s;
s = strtok(NULL, del);
}
}
int main()
{
int sockfd,client_fd;
char *indexer_pre = "/usr/local/webserver/sphinx/bin/indexer --quiet --config /usr/local/webserver/sphinx/etc/sphinx_";
struct sockaddr_in my_addr;
struct sockaddr_in remote_addr;
//create socket
if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
{
perror("socket create failed!");
exit(1);
}
//bind port
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(SERV_PORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero),8);
if (bind(sockfd, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) == -1)
{
perror("bind error!");
exit(1);
}
//listen
if (listen(sockfd, BACKLOG) == -1)
{
perror("listen error");
exit(1);
}
printf("\n");
printf("Sphinx Indexer Daemon\nAuthor:Keeff <Keeff@bluekee.com>\n");
printf("Service running...");
printf("\n");
while (1)
{
int sin_size = sizeof(struct sockaddr_in);
if ((client_fd = accept(sockfd, (struct sockaddr*)&remote_addr,&sin_size)) == -1)
{
perror("Accept error!");
continue;
}
int pid;
pid = fork();
if ( !pid )
{
char indexer_shell[1500];
strcpy(indexer_shell, indexer_pre);
int rval;
char receive_string[MAXSIZE];
if ((rval = read(client_fd, receive_string, MAXSIZE)) < 0)
{
perror("Reading stream error!");
continue;
}
char *param_arr[3];
const char *del = "|";
split(param_arr, receive_string, del);
if ( strstr(param_arr[0],";") != NULL || strstr(param_arr[0],"`") != NULL || strstr(param_arr[1],";") != NULL || strstr(param_arr[1],"`") != NULL)
{
//send msg to client
char *msg = "Bad command!" ;
if (send(client_fd, msg, strlen(msg), 0) == -1) perror("send error!");
close(client_fd);
exit(0);
}
strcat(indexer_shell, param_arr[0]);
strcat(indexer_shell, ".conf ");
strcat(indexer_shell, param_arr[1]);
system(indexer_shell);
//send msg to client
char* msg = "Indexer running...";
if (send(client_fd, msg, strlen(msg), 0) == -1) perror("send error!");
close(client_fd);
exit(0);
}
close(client_fd);
waitpid(pid, NULL, 0);
}
return 0;
}
indexerd.c保存到每一台索引服务器的/usr/local/webserver/sphinx/bin/下,然后编译
[root@indexserv1 bin]# gcc -o indexerd indexerd.c
在每台索引服务器上启动indexerd
[root@indexserv1 bin]# nohup ./indexerd > /dev/null &
检查一下服务是否启用
[root@indexserv1 bin]# netstat -nlp|grep 3333
tcp 0 0 0.0.0.0:3333 0.0.0.0:* LISTEN 31676/indexerd
可以看到indexerd已经正常启用了。
(2) client端的指令发送器
指令发送器不负责任何逻辑处理,只负责向服务端发送指令
我们把它命名为indexer_send
现在编写一份indexer_send.c的源码
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#define SERVPORT 3333
#define MAXDATASIZE 1500
int main(int argc, char* argv[])
{
if ( argv[1] == NULL )
{
printf("Sphinx Indexer Sender\nAuthor:Keeff <Keeff@bluekee.com>\nUsage:\n/path/to/indexer_send [ip] \"[port]|[--rotate] [index] [index]|\" [-debug]\n");
exit(1);
}
int sockfd, recvbytes;
char buf[MAXDATASIZE];
struct hostent *host;
struct sockaddr_in serv_addr;
if (( sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket error!");
exit(1);
}
bzero(&serv_addr,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERVPORT);
serv_addr.sin_addr.s_addr= inet_addr(argv[1]);
if (connect(sockfd, (struct sockaddr *)&serv_addr,sizeof(struct sockaddr)) == -1)
{
perror("connect error!");
exit(1);
}
write(sockfd, argv[2], strlen(argv[2]));
//debug
if ( argv[3] != NULL && strcmp("-debug",argv[3]) == 0 )
{
printf("command: %s\n",argv[2]);
if ((recvbytes = recv(sockfd, buf, MAXDATASIZE,0)) == -1)
{
perror("recv error!");
exit(1);
}
buf[recvbytes] = '\0';
printf("Response: %s",buf);
}
close(sockfd);
return 0;
}
编译
[root@indexclient bin]# gcc -o indexer_send indexer_send.c
调试发送器,假设一台索引服务器IP为192.168.1.111,其中的3312端口下有类似beijing_store_today,shanghai_store_today的索引
[root@indexclient bin]#./indexer_send 192.168.1.111 "3312|--rotate beijing_store_today shanghai_store_today |" -debug
command: 3312|--rotate beijing_store_today shanghai_store_today |
Response: Indexer running...
可以看到,indexerd返回了执行重建的消息,说明indexerd与indexer_send都能正常使用。
(3) client端
today索引的client程序我们命名为today_indexer
last索引的client程序我们命名为last_indexer
我们先写一份简单的today_indexer.c源码
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <mysql.h>
#define INTERVAL 600
int main(int argc, char *argv[] )
{
if ( argv[1] == NULL )
{
printf("Sphinx today Indexer\nAuthor:Keeff <Keeff@bluekee.com>\nUsage:\n/path/to/today_indexer 3312 3313 3314 -debug\n\n");
exit(1);
}
MYSQL *conn;
MYSQL_RES *res;
MYSQL_ROW row;
//数据库配置
char *server = "srcdb";
char *user = "srcdbuser";
char *password = "srcdbpass";
char *database = "mysql";
//debug
int debug = 0;
char *exec_script = "/usr/local/webserver/sphinx/bin/indexer_send";
char cmd_del[] = "|";
char cmd_pre[] = "--rotate ";
char cmd_post[] = "&|";
char *port_3312 = "3312";
char *port_3313 = "3313";
char *port_3314 = "3314";
char cmd_3312[5] = "";
char cmd_3313[5] = "";
char cmd_3314[5] = "";
strcpy(cmd_3312, port_3312);
strcpy(cmd_3313, port_3313);
strcpy(cmd_3345, port_3314);
strcat(cmd_3312, cmd_del);
strcat(cmd_3313, cmd_del);
strcat(cmd_3314, cmd_del);
strcat(cmd_3312, cmd_pre);
strcat(cmd_3313, cmd_pre);
strcat(cmd_3314, cmd_pre);
char real_exec_script[100];
strcpy(real_exec_script, exec_script);
strcat(real_exec_script, " ");
while ( 1 )
{
//指令开关
int run_3312 = 0;
int run_3313 = 0;
int run_3314 = 0;
char shell_3312[1500] = "";
char shell_3313[1500] = "";
char shell_3314[1500] = "";
strcpy(shell_3312, cmd_3312);
strcpy(shell_3313, cmd_3313);
strcpy(shell_3314, cmd_3314);
conn = mysql_init(NULL);
if (!mysql_real_connect(conn, server,user, password, database, 0, NULL, 0))
{
fprintf(stderr, "%s\n", mysql_error(conn));
exit(1);
}
mysql_query(conn,"SET SESSION query_cache_type=off");
mysql_query(conn,"SET NAMES utf8");
if (mysql_query(conn, "SELECT table_schema, table_name FROM information_schema.tables WHERE table_name IN ('store','flea','job') AND unix_timestamp(update_time) > unix_timestamp()-615"))
{
fprintf(stderr, "%s\n", mysql_error(conn));
exit(1);
}
res = mysql_use_result(conn);
while ((row = mysql_fetch_row(res)) != NULL)
{
for(int i=0; i<argc; i++ )
{
if ( argv != NULL )
{
if ( strcmp("-debug",argv) == 0 )
{
debug = 1;
}
if ( strcmp("3312",argv) == 0 )
{
if ( strcmp( "store", row[1] ) == 0 )
{
run_3312 = 1;
strcat(shell_3312,row[0]);
strcat(shell_3312,"_");
strcat(shell_3312,row[1]);
strcat(shell_3312,"_today ");
}
}
if ( strcmp("3313",argv) == 0 )
{
if ( strcmp( "flea", row[1] ) == 0 )
{
run_3313 = 1;
strcat(shell_3313,row[0]);
strcat(shell_3313,"_");
strcat(shell_3313,row[1]);
strcat(shell_3313,"_today ");
}
}
if ( strcmp("3314",argv) == 0 )
{
if ( strcmp( "job", row[1] ) == 0 )
{
run_3314 = 1;
strcat(shell_3314,row[0]);
strcat(shell_3314,"_");
strcat(shell_3314,row[1]);
strcat(shell_3314,"_today ");
}
}
}
}
}
mysql_free_result(res);
mysql_close(conn);
//execute shell
if ( run_3312 == 1 )
{
char exec_3312[1500] = "";
strcpy(exec_3312, real_exec_script);
strcat(exec_3312, "192.168.1.111 \"");
strcat(exec_3312, shell_3312);
strcat(exec_3312, cmd_post);
strcat(exec_3312, "\"");
if ( debug == 1 )
{
printf("%s\n",exec_3312);
strcat(exec_3312, " -debug");
}
system(exec_3312);
strcpy(exec_3312, real_exec_script);
strcat(exec_3312, "192.168.1.112 \"");
strcat(exec_3312, shell_3312);
strcat(exec_3312, cmd_post);
strcat(exec_3312, "\"");
if ( debug == 1 )
{
printf("%s\n",exec_3312);
strcat(exec_3312, " -debug");
}
system(exec_3312);
}
if ( run_3313 == 1 )
{
char exec_3313[1500] = "";
strcpy(exec_3313, real_exec_script);
strcat(exec_3313, "192.168.1.113 \"");
strcat(exec_3313, shell_3313);
strcat(exec_3313, cmd_post);
strcat(exec_3313, "\"");
if ( debug == 1 )
{
printf("%s\n",exec_3313);
strcat(exec_3313, " -debug");
}
system(exec_3313);
strcpy(exec_3313, real_exec_script);
strcat(exec_3313, "192.168.1.114 \"");
strcat(exec_3313, shell_3313);
strcat(exec_3313, cmd_post);
strcat(exec_3313, "\"");
if ( debug == 1 )
{
printf("%s\n",exec_3313);
strcat(exec_3313, " -debug");
}
system(exec_3313);
}
if ( run_3314 == 1 )
{
char exec_3314[1500] = "";
strcpy(exec_3314, real_exec_script);
strcat(exec_3314, "192.168.1.115 \"");
strcat(exec_3314, shell_3314);
strcat(exec_3314, cmd_post);
strcat(exec_3314, "\"");
if ( debug == 1 )
{
printf("%s\n",exec_3314);
strcat(exec_3314, " -debug");
}
system(exec_3314);
strcpy(exec_3314, real_exec_script);
strcat(exec_3314, "192.168.1.116 \"");
strcat(exec_3314, shell_3314);
strcat(exec_3314, cmd_post);
strcat(exec_3314, "\"");
if ( debug == 1 )
{
printf("%s\n",exec_3314);
strcat(exec_3314, " -debug");
}
system(exec_3314);
}
sleep( INTERVAL );
}
}
由源码可以看出,通过查询information_schema.tables,我们可以获知数据表的最后更新时间。我在这里举的例子是这样的,比如有商 铺信息的表,表名是store,数据库分别是beijing,shanghai,shenzhen…等许多许多个全国不面的城市库。3312端口下的 索引就是商铺的索引,main索引名称如beijing_store,shanghai_store的形式,today索引的名称则如 beijing_store_today,shanghai_store_today的形式,last索引的名称则如 beijing_store_last,shanghai_store_last的形式。 如此规则,3313端口下是二手交易,也是类似beijing_flea,beijing_flea_today,beijing_flea_last的 形式。3314端口下的招聘信息也一样。即是[db_name]_[table_name]为main索引名, [db_name]_[table_name]_today为today索引名,[db_name]_[table_name]_last为last索引 名.
today_indexer的作用就是每隔10分钟,查询information_schema.tables,只要是在10分钟(可以多加十几秒)有更新的,就分端口拼接today索引名称,然后同时向服务端发送重建指令。
现在编译today_indexer
[root@indexclient bin]# gcc -o today_indexer today_indexer.c `/usr/local/mysql/bin/mysql_config --cflags --libs` --std=c99
启用today_indexer
[root@indexclient bin]#nohup ./today_indexer 3312 3313 3314 > /dev/null &
用同样的方法,我们写一个last_indexer程序
last_indexer.c源码
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <mysql.h>
#define INTERVAL 10
int main(int argc, char *argv[] )
{
if ( argv[1] == NULL )
{
printf("Sphinx last Indexer\nAuthor:Keeff <Keeff@bluekee.com>\nUsage:\n/path/to/last_indexer 3312 3313 3314 -debug\n\n");
exit(1);
}
MYSQL *conn;
MYSQL_RES *res;
MYSQL_ROW row;
//数据库配置
char *server = "srcdb";
char *user = "srcdbuser";
char *password = "srcdbpass";
char *database = "mysql";
//debug
int debug = 0;
char *exec_script = "/usr/local/webserver/sphinx/bin/indexer_send";
char cmd_del[] = "|";
char cmd_pre[] = "--rotate ";
char cmd_post[] = "&|";
char *port_3312 = "3312";
char *port_3313 = "3313";
char *port_3314 = "3314";
char cmd_3312[5] = "";
char cmd_3313[5] = "";
char cmd_3314[5] = "";
strcpy(cmd_3312, port_3312);
strcpy(cmd_3313, port_3313);
strcpy(cmd_3345, port_3314);
strcat(cmd_3312, cmd_del);
strcat(cmd_3313, cmd_del);
strcat(cmd_3314, cmd_del);
strcat(cmd_3312, cmd_pre);
strcat(cmd_3313, cmd_pre);
strcat(cmd_3314, cmd_pre);
char real_exec_script[100];
strcpy(real_exec_script, exec_script);
strcat(real_exec_script, " ");
while ( 1 )
{
//指令开关
int run_3312 = 0;
int run_3313 = 0;
int run_3314 = 0;
char shell_3312[1500] = "";
char shell_3313[1500] = "";
char shell_3314[1500] = "";
strcpy(shell_3312, cmd_3312);
strcpy(shell_3313, cmd_3313);
strcpy(shell_3314, cmd_3314);
conn = mysql_init(NULL);
if (!mysql_real_connect(conn, server,user, password, database, 0, NULL, 0))
{
fprintf(stderr, "%s\n", mysql_error(conn));
exit(1);
}
mysql_query(conn,"SET SESSION query_cache_type=off");
mysql_query(conn,"SET NAMES utf8");
if (mysql_query(conn, "SELECT table_schema, table_name FROM information_schema.tables WHERE table_name IN ('store','flea','job') AND unix_timestamp(update_time) > unix_timestamp()-615"))
{
fprintf(stderr, "%s\n", mysql_error(conn));
exit(1);
}
res = mysql_use_result(conn);
while ((row = mysql_fetch_row(res)) != NULL)
{
for(int i=0; i<argc; i++ )
{
if ( argv != NULL )
{
if ( strcmp("-debug",argv) == 0 )
{
debug = 1;
}
if ( strcmp("3312",argv) == 0 )
{
if ( strcmp( "store", row[1] ) == 0 )
{
run_3312 = 1;
strcat(shell_3312,row[0]);
strcat(shell_3312,"_");
strcat(shell_3312,row[1]);
strcat(shell_3312,"_last ");
}
}
if ( strcmp("3313",argv) == 0 )
{
if ( strcmp( "flea", row[1] ) == 0 )
{
run_3313 = 1;
strcat(shell_3313,row[0]);
strcat(shell_3313,"_");
strcat(shell_3313,row[1]);
strcat(shell_3313,"_last ");
}
}
if ( strcmp("3314",argv) == 0 )
{
if ( strcmp( "job", row[1] ) == 0 )
{
run_3314 = 1;
strcat(shell_3314,row[0]);
strcat(shell_3314,"_");
strcat(shell_3314,row[1]);
strcat(shell_3314,"_last ");
}
}
}
}
}
mysql_free_result(res);
mysql_close(conn);
//execute shell
if ( run_3312 == 1 )
{
char exec_3312[1500] = "";
strcpy(exec_3312, real_exec_script);
strcat(exec_3312, "192.168.1.111 \"");
strcat(exec_3312, shell_3312);
strcat(exec_3312, cmd_post);
strcat(exec_3312, "\"");
if ( debug == 1 )
{
printf("%s\n",exec_3312);
strcat(exec_3312, " -debug");
}
system(exec_3312);
strcpy(exec_3312, real_exec_script);
strcat(exec_3312, "192.168.1.112 \"");
strcat(exec_3312, shell_3312);
strcat(exec_3312, cmd_post);
strcat(exec_3312, "\"");
if ( debug == 1 )
{
printf("%s\n",exec_3312);
strcat(exec_3312, " -debug");
}
system(exec_3312);
}
if ( run_3313 == 1 )
{
char exec_3313[1500] = "";
strcpy(exec_3313, real_exec_script);
strcat(exec_3313, "192.168.1.113 \"");
strcat(exec_3313, shell_3313);
strcat(exec_3313, cmd_post);
strcat(exec_3313, "\"");
if ( debug == 1 )
{
printf("%s\n",exec_3313);
strcat(exec_3313, " -debug");
}
system(exec_3313);
strcpy(exec_3313, real_exec_script);
strcat(exec_3313, "192.168.1.114 \"");
strcat(exec_3313, shell_3313);
strcat(exec_3313, cmd_post);
strcat(exec_3313, "\"");
if ( debug == 1 )
{
printf("%s\n",exec_3313);
strcat(exec_3313, " -debug");
}
system(exec_3313);
}
if ( run_3314 == 1 )
{
char exec_3314[1500] = "";
strcpy(exec_3314, real_exec_script);
strcat(exec_3314, "192.168.1.115 \"");
strcat(exec_3314, shell_3314);
strcat(exec_3314, cmd_post);
strcat(exec_3314, "\"");
if ( debug == 1 )
{
printf("%s\n",exec_3314);
strcat(exec_3314, " -debug");
}
system(exec_3314);
strcpy(exec_3314, real_exec_script);
strcat(exec_3314, "192.168.1.116 \"");
strcat(exec_3314, shell_3314);
strcat(exec_3314, cmd_post);
strcat(exec_3314, "\"");
if ( debug == 1 )
{
printf("%s\n",exec_3314);
strcat(exec_3314, " -debug");
}
system(exec_3314);
}
sleep( INTERVAL );
}
}
现在编译last_indexer
[root@indexclient bin]# gcc -o last_indexer last_indexer.c `/usr/local/mysql/bin/mysql_config --cflags --libs` --std=c99
如果需要调试
[root@indexclient bin]#./last_indexer 3312 3313 3314 -debug
这样,如果你的网站数据更新频繁,很快你将看到调试信息
启用last_indexer
[root@indexclient bin]#nohup ./last_indexer 3312 3313 3314 > /dev/null &
六、结束
至此,实时索引架构基本上已经是完成,只需要在查询代码里将三个索引都写上即可。
写得比较仓促,有不对的地方,请朋友们指正。