Tair缓存系统学习 (数据结构存储系统)

1、Tair 简介 
     Tair 是一个类似于map的key/value结构存储系统(也就是缓存系统),具备标准的特性是:高性能、高扩展、高可靠,也就是传说中的三高产品,支持分布式集群部署。官网说目前支持java和c这两个版本。 
     适用场景是轻量级缓存应用,为小文件和零碎文件、固定数据文件存储的优化。 
2、Tair 存储方式 
     2.1、非持久化 
             非持久化的tair可以看作是一个分布式缓存。 
     2.2、持久化 
             持久化的tair是将数据文件放入到磁盘存储的。持久化的tair有一个非常好的地方就是可以对持久化的文件备份,并且可以备份到不同主机上,通过程序还可以配置成自主备份等非常的灵活。 
             持久化tair中包含了两个默认的存储引擎mdb和fdb,当然tair也非常不错的抽象了持久化层次,也可以采用其他db。 
             mdb是一个高效率的关系型缓存存储数据库和memcached对比来看都存在一样的内存管理方式。当然mdb还支持很多其他特性,比如mdb支持使用share memory ,这使得在重启 tair数据节点进程时不会导致数据丢失,从而使得应用升级更平滑,也不会导致数据命中率有较大的波动。  mdb主要是使用在需要cache的系统中,为了减少系统down的影响,采用共享内存的方式,这样当系统down后,重新启动后数据还存在。 
             fdb 也上一个简单高校的关系型缓存存储数据库,它的查询方式比较有趣是采用树的方式根据数据key的hash值来索引数据,这样增加了数据查找效率。同时索引数据和业务数据分离,尽量把索引的文件保存在内存中,以便减少io开销。    fdb是采用类似Tokyo Cabinet实现方式,但这里为了性能考虑,会把索引和数据分文件存放,同时把索引文件放在内存中(在部署文档中可以看到可以配置大小)。 
3、Tair 负债均衡算法 
     分布式算法是采用的一致性哈希算法。这里一致性哈希主要解决了分布式中的平衡性和分散性、一致性。 
     这个地方也有一个比较好的,对于所有的key,分配到q个桶中,桶是负债均衡和数据迁移的基本单位。congfig server(tair的核心接口之一)根据一定的策略把每个桶指派到不同的data server上。因为数据是按照key做的hash算法。所以可以认为每个q桶的数据基本是平衡的。那么保证桶的分布式均衡性,就保证了数据分布的均衡性; 
4、Tair 可能存在的缺陷 
     自认为保证上面了几点特性时(高性能、高扩展、高可靠等特性),那么势必会消耗其他资源,那么查询效率有时候可能就会被降低。当然这个也是需要跟分布式的机器缓存空间、内存等等因素有关系,当缓存系统规模越大,业务系统规模越大时查询效率可能就会被降低,当然如果这样又有其他方案来解决这个规模大的问题了,这里只是说一下,知道有这么个事就行了。 
     接下来这里还需要特别提醒下的就是tair在分布式时一致性和可靠性是无法同时保证的,这个非tair机制问题,实乃是网络必然会存在问题的,当网络存在动荡时数据一致性很难在短时间内一致,当然如果网络没有问题,tair还是会保持很高的一致性。 
5、Tair 的模块介绍      
     模块目前主要为这三个模块config server 、data server和storage以及plugin、client、common其中还有一个可选择的验证模块invalid server 。 
      config_server: 模块主要包括config_server目录。实现了从配置文件中load进节点的信息,然后根据配置的数据分布的桶的数量和需要建立的数据的备份数,建立一张数据的分布表。该表其实是一个数组,元素为数据存储的节点的信息,长度为桶数乘以数据的备份数。config_server还负责对数据节点故障检测,主要通过心跳来确定数据节点的状态,但数据节点发生变化(比如增加服务器),就会把前面所描述的数据分布表发送给数据节点。同时数据节点通过心跳来上报一些统计信息等数据。 
     data_server: 为数据节点提供服务,主要处理客户端的请求,还支持了数据的复制,根据前面所描述的数据分布表,会把负责的主桶的数据发送到对应的备份桶所在节点。 
      storage:  这里主要实现两种存储引擎,其中mdb目录中实现了内存型的存储引擎,fdb目录中实现持久化的存储引擎。存储引擎的基本接口定义在storage_manager.hpp。 
     plugin  tair: 支持热插拔的插件式服务,可以开发自己的插件,然后通知config_server,需要支持这个插件,让节点进行load。主要的实现场景是性能统计信息。 
      client: 是实现客户端代码。 
      common: 目录提供基础数据结构和组件,packets目录提供了通信协议中各种数据包的实现。基础库主要包括tbsys和tbnet,其中tbsys是主要的数据结构和文件操作的实现,包括排它锁和读写锁实现,对线程的包装,以及配置文件读写和分析等。tbnet是主要实现了单线程的网络读写数据流,采用了epoll的模式。      
6、Tair 的扩展功能 
     除了提供get\put\delete以及批量接口外,还存在一些其他使用功能如:version支持,原子计数器,item支持等。  
     version支持:在tair缓存中每一个数据都包含自己的版本号,版本号在每次更新后都会递增。这个版本号的支持主要目的是由于数据并发导致的更新问题。 
     原子计数器支持:这个支持可以让tair成为一个简单易用的分布式计算。 
     item支持:这个可以实现一个原子分布式的fifo的对别。 
7、Tair 核心接口 
     主要的类名: com.taobao.tair.TairManager  
     常用的方法有:delete\get\invalid\put\lock\hide\getVersion等 
8、缓存的使用 
     在考虑构建高性能框架的时候使用缓存,来提供整体的查询效率和数据的吞吐量,当然计算器科学里面也有这么一句话,所有的问题都可以通过增加一个间接层次来解决,那么在这个思路基础上提出了几点想法 
     在数据库前闲cache并且使用批量添加方法,一次写入多个缓存数据; 
     可以开启多线程序同时写入数据库,用多个线程同时读取cache并写入mysql效率很高; 
     那么如果对只读数据做cache的话那么适合缓存的有,数据很少改变、以读为主的数据比如元数据、配置信息和静态数据; 
     当然缓存使用多了也会带来隐患为缓存分配内存多了,用来服务的内存也就少了,应用层可能就会面临内存不足的压力,所以在考虑使用缓存时需要非常现实的权衡利弊。 
9、业内缓存框架优缺点对比 
     业内轻量级的缓存框架有ehcache/redis/tair一个重量级别的缓存框架memcached,这里对这些缓存框架的优缺点做一个梳理给大家做参考。 
     ehcache  :
     优点:易用性特别强、高性能缓存、版本迭代特别快、缓存策略支持多种、可以通过rmi可插入api实现分布式缓存、具备缓存监听、支持多缓存实例、提供hibernate的缓存实现、支持非持久化和持久化缓存数据。 
     缺点:使用磁盘空间做cache时非常占用磁盘空间,kill掉java进程时不能保证数据安全有可能会导致缓存数据丢失或者数据冲突。 
     适用场景:轻量级应用 
     redis :
     优点:非常丰富的数据结构而且都是原子性操作、高速读写、支持事务、支持命令行输入操作性好 
     缺点:没有自己的内存池在内存分配时存在的碎片会导致性能问题、对内存消耗剧烈(虽然有压缩方法但还是高)、持久化时定时快照每次都是写全量数据,代价有些高、aof追加快照只追加增量数据但写入log特别大。 
     适用场景:轻量级应用 
      memcached :
     优点:协议简单、基于libevent的事件处理、内置内存存储方式、不互相通信的分布式、 
     缺点:数据保存内存中服务进程重启会导致数据丢失、安全性不足是以root权限运行、内存消耗很大影响性能 
     适用场景:分布式应用、数据库前段缓存、服务器间数据共享等 
     不适合场景:不需要分布式的应用、单机服务、轻量级工程 
10、Tair 如何配置server端  
       10.1 安装tair服务 
               10.1.1 下载tair源代码 
                首先安装tair之前需要确认机器上是否安装了 automake autoconfig 和 libtool,使用automake --version命令可以查看,一般情况下机器都会安装的 
               获得底层库 tbsys 和 tbnet的源代码:(svn checkouthttp://code.taobao.org/svn/tb-common-utils/trunk/ tb-common-utils). 
               获得tair源代码:(svn checkout http://code.taobao.org/svn/tair/trunk/ tair). 
               安装boost-devel库,在用rpm管理软件包的os上可以使用rpm -q boost-devel查看是否已安装该库 
               编译安装tbsys和tbnet 
               编译安装tair 
               10.1.2 配置环境变量 
                       先指定环境变量 TBLIB_ROOT 为需要安装的目录. 这个环境变量在后续 tair 的编译安装中仍旧会被使用到. 比如要安装到当前用户的lib目录下, 则指定 export TBLIB_ROOT="~/lib" 
                10.1.3 安装tair  
                      进入common文件夹, 执行build.sh进行安装.  
                      编译安装tair:      
                      进入 tair 目录   
                      运行 bootstrap.sh 运行 configure.  注意, 在运行configue的时候, 可以使用 --with-boost=xxxx 来指定boost的目录. 使用--with-release=yes 来编译release版本.   
                      运行 make 进行编译  
                      运行 make install 进行安装 
        10.2 配置tair服务 
                tair的运行, 至少需要一个 config server 和一个 data server. 推荐使用两个 config server 多个data server的方式. 两个config server有主备之分.  源代码目录中 share 目录下有三个配置文件的样例, 下面会逐个解说. 
              configserver.conf  group.conf 这两个配置文件是config server所需要的. 先看这两个配置文件的配置 
               配置文件 configserver.conf 

[public] 
config_server=x.x.x.x:5198 
config_server=x.x.x.x:5198 
[configserver] 
port=5198 
log_file=logs/config.log 
pid_file=logs/config.pid 
log_level=warn 
group_file=etc/group.conf 
data_dir=data/data 
dev_name=eth0 

              public 下面配置的是两台config server的 ip 和端口. 其中排在前面的是主config server. 这一段信息会出现在每一个配置文件中. 请保持这一段信息的严格一致. 
              configserver下面的内容是本config server的具体配置: 
              port 端口号, 注意 config server会使用该端口做为服务端口, 而使用该端口+1 做为心跳端口           
              log_file 日志文件 
              pid_file  pid文件, 文件中保存当前进程中的pid 
              log_level 日志级别 
              group_file 本config server所管理的 group 的配置文件 
              data_dir   本config server自身数据的存放目录 
              dev_name   所使用的网络设备名 
              注意: 例子中, 所有的路径都配置的是相对路径. 这样实际上限制了程序启动时候的工作目录. 这里当然可以使用绝对路径.  
              注意: 程序本身可以把多个config server 或 data server跑在一台主机上, 只要配置不同的端口号就可以. 但是在配置文件的时候, 他们的数据目录必须分开, 程序不会对自己的数据目录加锁, 所以如果跑在同一主机上的服务, 数据目录配置相同, 程序自己不会发现, 却会发生很多莫名其妙的错误. 多个服务跑在同一台主机上, 一般只是在做功能测试的时候使用. 
              配置文件 group.conf 

#group name 
[group_1] 
# data move is 1 means when some data serve down, the migrating will be start. 
# default value is 0 
_data_move=1 
#_min_data_server_count: when data servers left in a group less than this value, config server will stop serve for this group 
#default value is copy count. 
_min_data_server_count=4 
_copy_count=3 
_bucket_number=1023 
_plugIns_list=libStaticPlugIn.so 
_build_strategy=1 #1 normal 2 rack 
_build_diff_ratio=0.6 #how much difference is allowd between different rack 
# diff_ratio =  |data_sever_count_in_rack1 - data_server_count_in_rack2| / max (data_sever_count_in_rack1, data_server_count_in_rack2) 
# diff_ration must less than _build_diff_ratio 
_pos_mask=65535  # 65535 is 0xffff  this will be used to gernerate rack info. 64 bit serverId & _pos_mask is the rack info, 
_server_list=x.x.x.x:5191 
_server_list=x.x.x.x:5191 
_server_list=x.x.x.x:5191 
_server_list=x.x.x.x:5191 
#quota info 
_areaCapacity_list=1,1124000;   
_areaCapacity_list=2,1124000;  

      每个group配置文件可以配置多个group, 这样一组config server就可以同时服务于多个 group 了. 不同的 group 用group name区分 
     _data_move 当这个配置为1的时候, 如果发生了某个data server宕机, 则系统会尽可能的通过冗余的备份对数据进行迁移. 注意, 如果 copy_count 为大于1的值, 则这个配置无效, 系统总是会发生迁移的. 只有copy_count为1的时候, 该配置才有作用.  
     _min_data_server_count  这个是系统中需要存在的最少data server的个数.  当系统中可正常工作的data server的个数小于这个值的时候, 整个系统会停止服务, 等待人工介入 
     _copy_count  这个表示一条数据在系统中实际存储的份数. 如果tair被用作缓存, 这里一般配置1. 如果被用来做存储, 一般配置为3。 当系统中可工作的data server的数量少于这个值的时候, 系统也会停止工作. 比如 _copy_count 为3, 而系统中只有 2 台data server. 这个时候因为要求一条数据的各个备份必须写到不同的data server上, 所以系统无法完成写入操作, 系统也会停止工作的. 
     _bucket_number  这个是hash桶的个数, 一般要 >> data server的数量(10倍以上). 数据的分布, 负载均衡, 数据的迁移都是以桶为单位的. 
     _plugIns_list  需要加载的插件的动态库名 
     _accept_strategy  默认为0,ds重新连接上cs的时候,需要手动touch group.conf。如果设置成1,则当有ds重新连接会cs的时候,不需要手动touch group.conf。 cs会自动接入该ds。 
     _build_strategy  在分配各个桶到不同的data server上去的时候所采用的策略. 目前提供两种策略. 配置为1 则是负载均衡优先, 分配的时候尽量让各个 data server 的负载均衡. 配置为 2 的时候, 是位置安全优先, 会尽量将一份数据的不同备份分配到不同机架的机器上. 配置为3的时候,如果服务器分布在多个机器上,那么会优先使用位置安全优先,即策略2. 如果服务器只在一个机架上,那么退化成策略1,只按负载分布。 
     _build_diff_ratio 这个值只有当 _build_strategy 为2的时候才有意义. 实际上是用来表示不同的机架上机器差异大小的. 当位置安全优先的时候, 如果某个机架上的机器不断的停止服务, 必然会导致负载的极度不平衡.  当两个机架上机器数量差异达到一定程度的时候, 系统也不再继续工作, 等待人工介入. 
     _pos_mask  机架信息掩码. 程序使用这个值和由ip以及端口生成的64为的id做与操作, 得到的值就认为是位置信息.  比如 当此值是65535的时候 是十六进制 0xffff. 因为ip地址的64位存储的时候采用的是网络字节序, 最前32位是端口号, 后32位是网络字节序的ip地址. 所以0xffff 这个配置, 将认为10.1.1.1 和 10.2.1.1 是不同的机架. 
     _areaCapacity_list  这是每一个area的配额信息. 这里的单位是 byte. 需要注意的是, 该信息是某个 area 能够使用的所有空间的大小. 举个具体例子:当copy_count为3 共有5个data server的时候, 每个data server上, 该area实际能使用的空间是这个值/(3 * 5). 因为fdb使用mdb作为内部的缓存, 这个值的大小也决定了缓存的效率. 
                   data server的配置文件 

[public] 
config_server=172.23.16.225:5198 
config_server=172.23.16.226:5198 
[tairserver] 
storage_engine=mdb 
mdb_type=mdb_shm 
mdb_shm_path=/mdb_shm_path01 
#tairserver listen port 
port=5191 
heartbeat_port=6191 
process_thread_num=16 
slab_mem_size=22528 
log_file=logs/server.log 
pid_file=logs/server.pid 
log_level=warn 
dev_name=bond0 
ulog_dir=fdb/ulog 
ulog_file_number=3 
ulog_file_size=64 
check_expired_hour_range=2-4 
check_slab_hour_range=5-7 
[fdb] 
# in 
# MB 
index_mmap_size=30 
cache_size=2048 
bucket_size=10223 
free_block_pool_size=8 
data_dir=fdb/data 
fdb_name=tair_fdb 

                   下面解释一下data server的配置文件: 

public: 部分不再解说
storage_engine: 这个可以配置成fdb或者mdb.分别表示是使用内存存储数据(mdb)还是使用磁盘(fdb).
mdb_type: 这个是兼容以前版本用的,现在都配成mdb_shm就可以了
mdb_shm_path: 这个是用作映射共享内存的文件.
port: data server的工作端口
heartbeat_port: data server的心跳端口
process_thread_num: 工作线程数.实际上启动的线程会比这个数值多, 因为有一些后台线程.真正处理请求的线程数量是这里配置的.
slab_mem_size: 所占用的内存数量.这个值以M为单位, 如果是mdb, 则是mdb能存放的数据量, 如果是fdb, 此值无意义
ulog_dir: 发生迁移的时候, 日志文件的文件目录
ulog_file_number: 用来循环使用的log文件数目
ulog_file_size: 每个日志文件的大小, 单位是M
check_expired_hour_range: 清理超时数据的时间段.在这个时间段内, 会运行一个后台进程来清理mdb中的超时数据.一般配置在系统较空闲的时候
check_slab_hour_range: 对slap做平衡的时间段.一般配置在系统较空闲的时候
index_mmap_size: fdb中索引文件映射到内存的大小, 单位是M
cache_size: fdb中用作缓存的共享内存大小, 单位是M
bucket_size: fdb在存储数据的时候, 也是一个hash算法, 这儿就是hash桶的数目
free_block_pool_size: 这个用来存放fdb中的空闲位置, 便于重用空间
data_dir: fdb的数据文件目录
fdb_name: fdb数据文件名

        10.3 运行前准备 
                因为系统使用共享内存作为数据存储的空间(mdb)或者缓存空间(fdb), 所以需要先更改配置, 使得程序能够使用足够的共享内存.  scripts 目录下有一个脚本set_shm.sh 是用来做这些修改的, 这个脚本需要root权限来运行. 
        10.4 如何启动集群          
               在完成安装配置之后, 可以启动集群了.  启动的时候需要先启动data server 然后启动cofnig server.  如果是为已有的集群添加dataserver则可以先启动dataserver进程然后再修改gruop.conf,如果你先修改group.conf再启动进程,那么需要执行touch group.conf;在scripts目录下有一个脚本 tair.sh 可以用来帮助启动 tair.sh start_ds 用来启动data server.   tair.sh start_cs 用来启动config server.  这个脚本比较简单, 它要求配置文件放在固定位置, 采用固定名称.  使用者可以通过执行安装目录下的bin下的 tair_server (data server) 和 tair_cfg_svr(config server) 来启动集群. 
11 Tair client端(应用中使用) 
        11.1 mavn 配置下载jar包 
               版本号pom文件配置    

              <!--  Tair MC--> 
               <dependency> 
                 <groupId>com.taobao.tair</groupId> 
                 <artifactId>tair-mc-client</artifactId> 
                 <version>1.0.4.18</version> 
               </dependency> 
               <dependency> 
                    <groupId>com.taobao.tair</groupId> 
                    <artifactId>tair-client</artifactId> 
                    <version>2.3.4.49</version> 
               </dependency> 
               <dependency> 
                 <groupId>com.taobao.memberprofile</groupId> 
                 <artifactId>tag-tair</artifactId> 
                 <version>0.8</version> 
               </dependency> 

               工程jar配置 

               <dependency> 
                 <groupId>com.taobao.memberprofile</groupId> 
                 <artifactId>tag-tair</artifactId> 
               </dependency> 
               <!--  Tair MC--> 
               <dependency> 
                      <groupId>com.taobao.tair</groupId> 
                      <artifactId>tair-mc-client</artifactId> 
               </dependency> 
               <dependency> 
                     <groupId>com.taobao.tair</groupId> 
                     <artifactId>tair-client</artifactId> 
               </dependency> 

        11.2 添加Tair代理 

               设计Tair代理初衷、可以有多个不同的对象实例,以应对不同tair对象,不同namespace的情况,如果要增加共享变量就需要注意正确的使用方式,下图就是所有访问请求都是通过TairProxy请求到TairManager ,所以针对不同业务的都可以调用对外封装的Manager。

TairProxy部分实现代码 
put方法:            

    public boolean put(PrefixKey prefix, String key, Serializable value, int expiredTime) {
        ResultCode result = null;
        if (expiredTime < 1) { 
            // 做不同的处理 
            result = tairManager.put(namespace, prefix.getPrefix() + key, value);
        } else {
            result = tairManager.put(namespace, prefix.getPrefix() + key, value, 0, expiredTime);
        }
        if (result.isSuccess() && result.getCode() == 0) return true;
        log.error("put tair false! namespace:" + namespace + ",prefix:" + prefix + ",key:" + key + "value:" + value + ",exp:" + expiredTime);
        return false;
    }

get方法:

    public Object get(PrefixKey prefix, String key) {
        Result result = tairManager.get(namespace, prefix.getPrefix() + key);
        if (result.isSuccess() && result.getRc().getCode() == 0) {
            DataEntry value = result.getValue();
            if (null != value) {
                return value.getValue();
            }
        }
        return null;
    }

invaild方法:

public boolean invalid(PrefixKey prefix, String key) {
    ResultCode invalid = tairManager.invalid(namespace, prefix.getPrefix() + key);
    if (null != invalid && invalid.getCode() == 0) {
      return true;
    }
    log.error("invalid tair false! namespace:" + namespace + ",prefix:" + prefix + ",key:" + key);
    return false;
  }

           TairManager可以根据需要注入mdb和ldb等等。namespace不同的spring配置文件如下 spring-tair.xml配置文件,同时根据不同的manger需要在这里声明。 

${libra2center_tair_mdb_configid}true
${libra2center_tair_market2_configID}true
${libra2center_tair_mcclient_configID}true
${libra2center_tair_market2_configID}true
${libra2center_tair_ldb_configid}true

 log4j.xml配置 : .....
 还有需要配置taircongfigid,这个根据业务需要自己配置地方

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章