原文:http://www.linuxidc.com/Linux/2013-08/89105.htm

1. GlusterFS概述

GlusterFS是一个开源的分布式文件系统,具有强大的Scale-Out横向扩展能力,通过扩展能够支持数PB存储容量和处理数千客户端。GlusterFS借助TCP/IP或InfiniBand RDMA网络将物理分布的存储资源聚集在一起,使用单一全局命名空间来管理数据。GlusterFS基于可堆叠的用户空间设计,可为各种不同的数据负载提供优异的性能。

GlusterFS支持运行在任何标准IP网络上标准应用程序的标准客户端,用户可以在全局统一的命名空间中使用Glusterfs/NFS/CIFS等标准协议来访问应用数据。GlusterFS使得用户可摆脱原有的独立、高成本的封闭存储系统,能够利用普通廉价的存储设备来部署可集中管理、横向扩展、虚拟化的存储池,存储容量可扩展至TB/PB级。Glusterfs的深入剖析请参考”GlusterFS集群文件系统研究”一文 http://www.linuxidc.com/Linux/2011-12/50579.htm 。GlusterFS主要特征如下:

1) 扩展性和高性能
2) 高可用性
3) 全局统一命名空间
4) 弹性哈希算法
5) 弹性卷管理
6) 基于标准协议

2. Xlator工作原理

GlusterFS采用模块化、堆栈式的架构,可通过灵活的配置支持高度定制化的应用环境,比如大文件存储、海量小文件存储、云存储、多传输协议应用等。每个功能以模块形式实现,然后以积木方式进行简单的组合,即可实现复杂的功能。比如,Replicate模块可实现RAID1,Stripe模块可实现RAID0,通过两者的组合可实现RAID10和RAID01,同时获得高性能和高可靠性。

GlusterFS堆栈式设计思想源自GNU/Hurd微内核操作系统,具有很强的系统扩展能力,系统设计实现复杂性降低很多,基本功能模块的堆栈式组合就可以实现强大的功能。基本模块称为Translator,它是GlusterFS提供的一种强大文件系统功能扩展机制,借助这种良好定义的接口可以高效简便地扩展文件系统的功能。

GlusterFS中所有的功能都通过Translator机制实现,服务端与客户端模块接口是兼容的,同一个translator可同时在两边加载。每个translator都是SO动态库,运行时根据配置动态加载。每个模块实现特定基本功能,比如Cluster, Storage, Performance, Protocol,Features等,基本简单的模块可以通过堆栈式的组合来实现复杂的功能,Translator可以把对外部系统的访问转换成目标系统的适当调用。大部分模块都运行在客户端,比如合成器、I/O调度器和性能优化等,服务端相对简单许多。客户端和存储服务器均有自己的存储栈,构成了一棵Translator功能树,应用了若干模块。模块化和堆栈式的架构设计,极大降低了系统设计复杂性,简化了系统的实现、升级以及系统维护。


Gluster卷Translator栈图

GlusterFS概念中,由一系列translator构成的完整功能栈称之为Volume(如上图所示),分配给一个volume的本地文件系统称为brick,被至少一个translator处理过的brick称为subvolume。FUSE模块位于客户端,POSIX模块位于服务器端,它们通常是volume中首个或最后一个模块,依赖于访问数据流的方向。中间部分会再加入其他功能的模块,构成一个完整的volume,这些模块通过一张图(graph)有机结合在一起。这是一种多层设计,运行时通过有序地向上或向下调用相邻模块接口来传递消息,调用关系由每个模块根据自身功能和translator图来决定。由translator实现的卷的完整数据流,如下图所示。


GlusterFS数据流

3. Xlator结构和相关API

Xlator是高度模块化的组件,具有良好定义的内部结构,包括结构体和接口函数原型定义。因此,要实现一个xlator,必须严格按照定义来实现,具体讲就是要实现xlator.h中定义的xlator_fops、xlator_cbks、init、fini、volume_options等结构体中的参数和函数指针,描述如下:

struct xlator_fops {
        fop_lookup_t        lookup;
        fop_stat_t          stat;
        fop_fstat_t          fstat;
        fop_truncate_t      truncate;
        fop_ftruncate_t      ftruncate;
        fop_access_t        access;
        fop_readlink_t      readlink;
        fop_mknod_t          mknod;
        fop_mkdir_t          mkdir;
        fop_unlink_t        unlink;
        fop_rmdir_t          rmdir;
        fop_symlink_t        symlink;
        fop_rename_t        rename;
        fop_link_t          link;
        fop_create_t        create;
        fop_open_t          open;
        fop_readv_t          readv;
        fop_writev_t        writev;
        fop_flush_t          flush;
        fop_fsync_t          fsync;
        fop_opendir_t        opendir;
        fop_readdir_t        readdir;
        fop_readdirp_t      readdirp;
        fop_fsyncdir_t      fsyncdir;
        fop_statfs_t        statfs;
        fop_setxattr_t      setxattr;
        fop_getxattr_t      getxattr;
        fop_fsetxattr_t      fsetxattr;
        fop_fgetxattr_t      fgetxattr;
        fop_removexattr_t    removexattr;
        fop_lk_t            lk;
        fop_inodelk_t        inodelk;
        fop_finodelk_t      finodelk;
        fop_entrylk_t        entrylk;
        fop_fentrylk_t      fentrylk;
        fop_rchecksum_t      rchecksum;
        fop_xattrop_t        xattrop;
        fop_fxattrop_t      fxattrop;
        fop_setattr_t        setattr;
        fop_fsetattr_t      fsetattr;
        fop_getspec_t        getspec;

/* these entries are used for a typechecking hack in STACK_WIND _only_ */
        fop_lookup_cbk_t        lookup_cbk;
        fop_stat_cbk_t          stat_cbk;
        fop_fstat_cbk_t          fstat_cbk;
        fop_truncate_cbk_t      truncate_cbk;
        fop_ftruncate_cbk_t      ftruncate_cbk;
        fop_access_cbk_t        access_cbk;
        fop_readlink_cbk_t      readlink_cbk;
        fop_mknod_cbk_t          mknod_cbk;
        fop_mkdir_cbk_t          mkdir_cbk;
        fop_unlink_cbk_t        unlink_cbk;
        fop_rmdir_cbk_t          rmdir_cbk;
        fop_symlink_cbk_t        symlink_cbk;
        fop_rename_cbk_t        rename_cbk;
        fop_link_cbk_t          link_cbk;
        fop_create_cbk_t        create_cbk;
        fop_open_cbk_t          open_cbk;
        fop_readv_cbk_t          readv_cbk;
        fop_writev_cbk_t        writev_cbk;
        fop_flush_cbk_t          flush_cbk;
        fop_fsync_cbk_t          fsync_cbk;
        fop_opendir_cbk_t        opendir_cbk;
        fop_readdir_cbk_t        readdir_cbk;
        fop_readdirp_cbk_t      readdirp_cbk;
        fop_fsyncdir_cbk_t      fsyncdir_cbk;
        fop_statfs_cbk_t        statfs_cbk;
        fop_setxattr_cbk_t      setxattr_cbk;
        fop_getxattr_cbk_t      getxattr_cbk;
        fop_fsetxattr_cbk_t      fsetxattr_cbk;
        fop_fgetxattr_cbk_t      fgetxattr_cbk;
        fop_removexattr_cbk_t    removexattr_cbk;
        fop_lk_cbk_t            lk_cbk;
        fop_inodelk_cbk_t        inodelk_cbk;
        fop_finodelk_cbk_t      finodelk_cbk;
        fop_entrylk_cbk_t        entrylk_cbk;
        fop_fentrylk_cbk_t      fentrylk_cbk;
        fop_rchecksum_cbk_t      rchecksum_cbk;
        fop_xattrop_cbk_t        xattrop_cbk;
        fop_fxattrop_cbk_t      fxattrop_cbk;
        fop_setattr_cbk_t        setattr_cbk;
        fop_fsetattr_cbk_t      fsetattr_cbk;
        fop_getspec_cbk_t        getspec_cbk;
};

struct xlator_cbks {
        cbk_forget_t    forget;
        cbk_release_t  release;
        cbk_release_t  releasedir;
};

void            (*fini) (xlator_t *this);
int32_t          (*init) (xlator_t *this);

typedef struct volume_options {
        char                *key[ZR_VOLUME_MAX_NUM_KEY];
        /* different key, same meaning */
        volume_option_type_t type;
        int64_t              min;  /* 0 means no range */
        int64_t              max;  /* 0 means no range */
        char                *value[ZR_OPTION_MAX_ARRAY_SIZE];
        /* If specified, will check for one of
          the value from this array */
        char                *default_value;
        char                *description; /* about the key */
} volume_option_t;

xlator_fops和xlator_cbks结构体中的函数指针在xlator.h中都有严格的明确定义,实现时必须完全遵从。其中,xlator_fops是Linux中file_operations, inode_operations和super_operatioins的组合。另外,以上结构体和函数指针名分别确定为fops, cbks, init, fini, options,不可更改。因为xlator最终以SO动态库形式提供给glusterfs主体程序使用,需要使用统一确定的名称来加载和定位xlator中函数指针和变量。Init, fini分别用于xlator加载和卸载时的处理工作,这个对于每个xlator的个性化私有数据处理非常有用。如果xlator模板提供的接口和参数无法满足需求,可以有效利用这两个接口进行处理。值得一提的是,xlator并不一定要实现以上全部的函数指针和变量,可以仅实现特定相关的部分,其它的部分会在运行时自动填入默认的值,并直接传递给下一个translator, 同时指定回调函数,回调函数传回之前translator的结果。

Translator采用异步和回调函数的实现机制,这意味着处理特定请求的代码必须被分为两个部分:调用函数和回调函数。一个xlator的函数调用下一个translator的函数,然后无阻塞的返回。当调用下一个translator的函数时,回调函数可能会立即被调用,也可能稍后在一个不同的线程上被调用。在两种情况下,回调函数都不会像同步函数那样获取其上下文。GlusterFS提供了几种方式用于在调用函数及其回调函数间保存和传递上下文,但是必须xlator自行处理而不能完全依赖协议栈。

Translator的回调机制主要采用了STACK_WIND和STACK_UNWIND。当xlator fops某个函数被调用,表示接受到一个请求,使用frame stack来表示。Fops函数中执行相应操作,然后可把该请求使用STACK_WIND传递给下一个或多个translator。当完成一个请求而不再需要调用下一个translator,或者当任务完成从回调函数中回到上一个translator时需要调用STACK_UNWIND。实际上,最好使用STACK_UNWIND_STRICT,它可以用来指定那类请求你已经完成了。相关宏在stack.h中定义,原型如下:

#define STACK_WIND(frame, rfn, obj, fn, params ...)
#define STACK_WIND_COOKIE(frame, rfn, cky, obj, fn,params ...)
#define STACK_UNWIND(frame, params ...)
#define STACK_UNWIND_STRICT(op, frame, params ...)

其中用到的参数如下: 
Frame:stack frame表示请求
Rfn::回调函数,当下一个translator完成时会调用该函数
Obj::正在控制的translator对象
Fn:从下一个translator的fops table中指定要调用的translator函数
Params:任何其他被调用函数的参数(比如,inodes, fd, offset, data buffer)
Cky:cookie,这是一个opaque指针
Op:操作类型,用来检查附加的参数符合函数的期望

每个translator-stack frame都有一个local指针,用来储存该translator特定的上下文,这是在调用和回调函数间存储上下文的主要机制。当stack销毁时,每个frame的local如果不为NULL都会被传递给GF_FREE,但是不会执行其他的清理工作。如果local结构体包含指针或引用其他对象,就需要仔细处理这些。因此比较理想的情况是,内存和其它资源能在stack被销毁前被释放,不要完全依赖自动的GFS_GFREE。最为妥当的做法是定义特定translator的销毁函数,并在STACK_UNWIND返回前手工调用。

Xlator大部分的调用函数和回调函数以文件描述符(fd_t)或inode(inode_t)作为参数。通常translator需要存储一些自有的上下文,这些上下文独立于单个请求的生命周期。比如,DHT存储目录对应的布局映射(layout map)和某inode最后可知的位置。Glusterfs提供了一系列的函数用于存储此类上下文。在每种情况下,第二个参数是一个指向translator对象的指针,需要存储的数据与其相关,存储的值是一个unsigned的64位整数。这些函数返回0代表成功,在_get和_del函数中使用引用参数而不是返回值。

inode_ctx_put (inode, xlator, value)
inode_ctx_get (inode, xlator, &value)
inode_ctx_del (inode, xlator, &value)
fd_ctx_set (fd, xlator, value)
fd_ctx_get (fd, xlator, &value)
fd_ctx_del (fd, xlator, &value)

传递给调用函数和回调函数的inode_t或fd_t指针只是借来(borrowed)引用。如果希望该对象稍后还存在,最好调用inode_ref或fd_ref增加一个持久引用,并且当引用不再需要时调用inode_unref或fd_unref。 
另外一个常用的类型是dict_t,它是一个通用的排序字典或hash-map的数据结构,可用来存放任意类型值并以字符串为键值。例如,存储的值可以是任意大小的有符号或无符号整数,字符串,或二进制。字符串和二进制需要被标记在不需要时由glusterfs函数释放,或由glibc释放或根本不释放。Dict_t*和*data_t对象都是引用计数的,只有当引用数为0时被释放。同inodes和文件描述符一样,如果希望通过参数接受的dict_t持久存在,必须调用_ref和_unref处理器生命周期。字典并不是仅用于调用和回调函数,也可用于传递不同的模块选项,包括translator初始化的选项。事实上,目前translator的init函数主要用于解析字典中的选项。向translatro中添加一个选项,需要在translator的options数组中添加一个实体。每一个选项可以是boolean,整数,字符串,路径,translator名称,和其它一些自定义类型。如果是字符串,可以指定有效值。解析后的选项和其它的信息可以存放在xlator_t结构体中的private内。

Translators中大部分的logging是通过gf_log函数实现,其参数包括字符串(通常是this->name)、log等级、格式化串以及格式化的其他参数。日志等级分为GF_LOG_ERROR, GF_LOG_WARNING , GF_LOG_LOG和GF_LOG_DEBUG。Xlator可以封装gfs_log自定义相关宏,或采用现有等级,这样translator的日志在运行时就能被输出。设计xlator时,可以添加一个translator 日志等级选项,也可以实现一个特定的xattr调用用于传递新值。

4. 构造新Xlator

这里我们通过构造一个称为NULL的Xlator来梳理构造新xlator的基本方法。NULL Xlator本身不实现具体的功能,仅作为类似Proxy的中转,用于演示构造xlator的结构和方法。NULL Xlator实现包括四个文件,null.h, null.c, null_fops.h, null_fops.c,其中null_fops.h, null_fops.c与defaults.h, defaults.c完全相同。Null.h内容如下:

#ifndef __NULL_H__
#define __NULL_H__

#ifndef _CONFIG_H
#define _CONFIG_H
#include "config.h"
#endif
#include "mem-types.h"

typedef struct {
        xlator_t *target;
} null_private_t;

enum gf_null_mem_types_ {
        gf_null_mt_priv_t = gf_common_mt_end + 1,
        gf_null_mt_end
};

#endif /* __NULL_H__ */

其中,自定义了私有数据结构体null_private_t和内部数据类型。Null.c内容如下:

#include <ctype.h>
#include <sys/uio.h>

#ifndef _CONFIG_H
#define _CONFIG_H
#include "config.h"
#endif

#include "glusterfs.h"
#include "call-stub.h"
#include "defaults.h"
#include "logging.h"
#include "xlator.h"

#include "null.h"
#include "null_fops.h"
int32_t
init (xlator_t *this)
{
        xlator_t *tgt_xl = NULL;
        null_private_t *priv = NULL;

if (!this->children || this->children->next) {
                gf_log (this->name, GF_LOG_ERROR,
                        "FATAL: null should have exactly one child");
                return -1;
        }

priv = GF_CALLOC (1, sizeof (null_private_t), gf_null_mt_priv_t);
        if (!priv)
                return -1;

/* Init priv here */
        priv->target = tgt_xl;

gf_log (this->name, GF_LOG_DEBUG, "null xlator loaded");
        return 0;
}
void
fini (xlator_t *this)
{
        null_private_t *priv = this->private;

if (!priv)
                return;
        this->private = NULL;
        GF_FREE (priv);

return;
}
struct xlator_fops fops = {
        .lookup        = null_lookup,
        .stat          = null_stat,
        .fstat          = null_fstat,
        .truncate      = null_truncate,
        .ftruncate      = null_ftruncate,
        .access        = null_access,
        .readlink      = null_readlink,
        .mknod          = null_mknod,
        .mkdir          = null_mkdir,
        .unlink        = null_unlink,
        .rmdir          = null_rmdir,
        .symlink        = null_symlink,
        .rename        = null_rename,
        .link          = null_link,
        .create        = null_create,
        .open          = null_open,
        .readv          = null_readv,
        .writev        = null_writev,
        .flush          = null_flush,
        .fsync          = null_fsync,
        .opendir        = null_opendir,
        .readdir        = null_readdir,
        .readdirp      = null_readdirp,
        .fsyncdir      = null_fsyncdir,
        .statfs        = null_statfs,
        .setxattr      = null_setxattr,
        .getxattr      = null_getxattr,
        .fsetxattr      = null_fsetxattr,
        .fgetxattr      = null_fgetxattr,
        .removexattr    = null_removexattr,
        .lk            = null_lk,
        .inodelk        = null_inodelk,
        .finodelk      = null_finodelk,
        .entrylk        = null_entrylk,
        .fentrylk      = null_fentrylk,
        .rchecksum      = null_rchecksum,
        .xattrop        = null_xattrop,
        .fxattrop      = null_fxattrop,
        .setattr        = null_setattr,
        .fsetattr      = null_fsetattr,
        .getspec        = null_getspec,
};

struct xlator_cbks cbks = {
        .forget = null_forget,
        .release = null_release,
        .releasedir = null_releasedir,
};

struct volume_options options[] = {
        { .key  = {NULL} },
};

这其中主要实现了上面提到的init和fini函数,fops调用函数指针和cbks回调函数指针,以及卷参数选项options。一个新xlator的代码基本框架就是如此,这里因为没有实现具体功能,各种结构体、变量和函数实现都相对非常简单。如果要实现特定功能的xlator,可以以此为模块进行扩展。Glusterfs源码中xlator都是很好的例子,但有些很复杂,不适合初学者,可以先从简单的rot-13, read-only, bypass, negative-lookup等xlator开始研究,然后构造出自己所需功能的xlator。

5. 编译新Xlator

设计并编码实现新Xlator后,我们需要将其编译成SO形式的动态库,提供给Glusterfs使用。编译过程中,需要使用到glusterfs其他部分的相关代码,要求设置较为复杂的编译环境。这里我们编写了Makefile文件设置环境并进行编译,内容如下:

# Change these to match your source code.
TARGET  = null.so
OBJECTS = null.o null_fops.o

# Change these to match your environment.
GLFS_SRC  = /home/liuag/glusterfs-3.2.5
GLFS_VERS = 3.2.5
GLFS_LIB  = /opt/glusterfs/3.2.5/lib64/
HOST_OS  = GF_LINUX_HOST_OS

# You shouldn't need to change anything below here.

CFLAGS  = -fPIC -Wall -O2 \
          -DHAVE_CONFIG_H -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -D$(HOST_OS) \
          -I$(GLFS_SRC) -I$(GLFS_SRC)/libglusterfs/src \
          -I$(GLFS_SRC)/contrib/uuid -I.
LDFLAGS = -shared -nostartfiles -L$(GLFS_LIB) -lglusterfs -lpthread

$(TARGET): $(OBJECTS)
        $(CC) $(CFLAGS) $(OBJECTS) $(LDFLAGS) -o $(TARGET)

install: $(TARGET)
        cp $(TARGET) $(GLFS_LIB)/glusterfs/$(GLFS_VERS)/xlator/null

clean:
        rm -f $(TARGET) $(OBJECTS)

将Makefile设置成与自己相匹配的编译环境,然后直接make即可生成null.so动态库,make install安装null新xlator。至此,一个新xlator就构造成功了,之后我们就可以使用它了。

6. 测试新Xlator

最激动人心的时刻终于到来了,现在我们可以通过修改volume配置文件来添加并测试新构造的null xlator。这个xlator可以工作在客户端或服务器端,可以修改相应卷配置文件或fuse卷配置文件来实现。下面以服务器端加载为例,局部卷配置修改如下:

volume test-posix
    type storage/posix
    option directory /data/test-1
end-volume

volume test-null
    type null/null
    subvolumes test-posix
end-volume

volume test-access-control
    type features/access-control
    subvolumes test-null
end-volume
… …

OK,现在重启glusterd服务,然后mount这个卷,就可以测试null xlator功能了。当然,你可能什么功能都测试不出来,因为我们什么功能都没实现。

设计新Xlator扩展GlusterFS[转]的更多相关文章

  1. Code Complete 读后总结和新的扩展阅读计划

    Code Complete 读后总结和新的扩展阅读计划 用了一年时间终于将代码大全读完了,在这里做一个简单的总结,并安排下一阶段的扩展阅读计划. 1.选择代码大全作为我程序员职业入门的第一本书,我认为 ...

  2. atitit.TokenService v3 qb1  token服务模块的设计 新特性.docx

    atitit.TokenService v3 qb1  token服务模块的设计 新特性.docx 1.1. V3 新特性1 1.2. V2 新特性1 2. Token的归类1 3. Token的用途 ...

  3. 《C++设计新思维》Command设计模式读后感

    原文内容提领: 本书第5章标题为泛化仿函数,我认为本章真正讲述的内容可以总结出一句话! 如何利用C++老标准实现C++11新标准类似std::function提供的功能. std::function简 ...

  4. 《C++设计新思维》勘误,附C++14新解法

    勘误: 原书(中文版)3.13节,65-69页中GenScatterHierarchy以及FieldHelper均存在问题,当TypeList中类型有重复时,无法通过编译(原因在于“二义性基类”). ...

  5. atitit.jQuery Validate验证框架详解与ati Validate 设计新特性

    atitit.jQuery Validate验证框架详解与ati Validate 设计新特性 1. AtiValidate的目标1 2. 默的认校验规则1 2.1. 使用方式 1.metadata用 ...

  6. 为 GlusterFS 设计新的xlator (编译及调用过程分析)

    GlusterFS 是一个开源的网络分布式文件系统,前一阵子看了一点GlusterFS(Gluster)的代码,修改了部分代码,具体是增加了一个定制的xlator,简单记录一下. Gluster与xl ...

  7. 2019年UX设计新趋势

    UX设计总是在不断变化中.最近短短两年的时间里,我们已经看到,很多地方都大规模采用颠覆性技术,比如语音用户界面,混合现实和智能家居设备.设计这些体验的实际过程可能保持不变,但新技术的出现引发了新的行为 ...

  8. 如何利用phpize在生产环境中为php添加新的扩展php-bcmath

    在日常的开发当中,随着开发的功能越来越复杂.对运行环境的要求也就随着需求的变化需要不断地更新和变化.一个在线的生产系统不可能一开始就满足了所有的运行依赖,因此动态地添加依赖就显得比较必要了.如果你的应 ...

  9. JS框架设计之对象扩展一种子模块

    对象扩展 说完了,对象的创建(框架的命名空间的创建)以及如何解决多库之间的命名空间冲突问题之后,接下来,就是要扩展我们的对象,来对框架进行扩展,我们需要一种新功能,将新添加的功能整合到我们定义的对象中 ...

随机推荐

  1. 记一次kali和win8.1的双系统修复!!

    简要情况: 原来电脑存在的系统:win7和kali. 后来的系统:win8.1和原本的kali 情况描述:在我装完win8.1后就直接启动到win8.1了没有了grub2的选择启动界面,但是我还是想要 ...

  2. dirty cow exp

    公司搞底层的改了一下,说做到了几个不死机 /* * (un)comment correct payload first (x86 or x64)! * * $ gcc cowroot.c -o cow ...

  3. delphi 程序输出文件夹存放位置

  4. 安装Genymotion与集成eclipse,最后有集成android studio

    本安装过程从不用到VPN 一切国内网络都可以解决. 首先下载Genymotion,网址  https://www.genymotion.com/account/login/ 首先需要注册,我使用163 ...

  5. ajax加php实现简单的投票效果

    废话少说,作为一个前端猿,首先上前端的代码. 1.上html代码: <!DOCTYPE html> <html> <head lang="en"> ...

  6. iOS根据网络图片的size大小设置UIImageView的大小

    有时候在设置UIImageView的大小时候需要根据UIimage的长宽比来自动设置,不让图片原比例失真. 如果是从本地获取到的图片,[UIImage imageNamed:@"" ...

  7. [SQL基础教程] 2-2 算数运算符和比较运算符

    [SQL基础教程] 2-2 算数运算符和比较运算符 算数运算符 四则运算 运算符 含义 + - * / SELECT col_1*2 AS col_new FROM table; 注意 所有包含NUL ...

  8. ElasticSearch(6)-结构化查询

    引用:ElasticSearch权威指南 一.请求体查询 请求体查询 简单查询语句(lite)是一种有效的命令行_adhoc_查询.但是,如果你想要善用搜索,你必须使用请求体查询(request bo ...

  9. 通过一个表的id同时查询多个表的数据

    'select c.字段名,x.字段名 as 改为新的显示名,x.字段名 from 表名1 b,表名2 c,表名3 x where b.字段id=' . $id . ' and b.`字段id`=c. ...

  10. ****K - Alien's Organ

    K - Alien's Organ Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Sub ...