一、背景

Hadoop设计之初借鉴GFS/MapReduce的思想:移动计算的成本远小于移动数据的成本。所以调度通常会尽可能将计算移动到拥有数据的节点上,在作业执行过程中,从HDFS角度看,计算和数据通常是同一个DataNode节点,即存在大量的本地读写。

但是HDFS最初实现时,并没有区分本地读和远程读,二者的实现方式完全一样,都是先由DataNode读取数据,然后通过DFSClient与DataNode之间的Socket管道进行数据交互。这样的实现方式很显然由于经过DataNode中转对数据读性能有一定的影响。

社区很早也关注到了这一问题,先后提出过两种方案来提升性能,分别是HDFS-347HDFS-2246

HDFS-2246是比较直接和自然的想法,既然DFSClient和DataNode在同一个节点上,当DFSClient对数据发出读请求后,DataNode提供给DFSClient包括文件路径,偏移量和长度的三元组(path,offset,length),DFSClient拿到这些信息后直接从文件系统读取数据,从而绕过DataNode避免一次数据中转的过程。但是这个方案存在两个问题,首先,HDFS需要为所有用户配置白名单,赋予其可读权限,当增加新用户需要更新白名单,维护不方便;其次,当为用户赋权后,意味着用户拥有了访问所有数据的权限,相当于超级用户,从而导致数据存在安全漏洞。

HDFS-347使用UNIX提供的Unix Domain Socket进程通讯机制实现了安全的本地短路读取。DFSClient向DataNode请求数据时,DataNode打开块文件和元数据文件,通过Unix Domain Socket将对应的文件描述符传给DFSClient,而不再是路径、偏移量和长度等三元组。文件描述符是只读的,DFSClient不能随意修改接收到的文件。同时由于DFSClient自身无法访问块所在的目录,也就不能访问未授权数据。

虽然本地短路读在性能上有了明显的提升,但是从全集群看,依然存在几个性能问题:
(1)DFSClient向DataNode发起数据读请求后,DataNode在OS Buffer对数据会进行Cache,但是数据Cache的分布情况并没有显式暴漏给上层,对任务调度透明,造成Cache浪费。比如同一Block多个副本可能被Cache在多个存储这些副本的DataNode OS Buffer,造成内存资源浪费。
(2)由于Cache的分布对任务调度透明,一些低优先级任务的读请求有可能将高优先级任务正在使用的数据从Cache中淘汰出去,造成数据必须从磁盘读,增加读数据的开销从而影响任务的完成时间,甚至影响到关键生产任务SLA。

针对这些问题,社区在2013年提出集中式缓存方案(Centralized cache management)HDFS-4949,由NameNode对DataNode的Cache进行统一集中管理,并将缓存接口显式暴漏给上层应用,该功能在2.3.0发布。这个功能对于提升HDFS读性能和上层应用的执行效率与实时性有很大帮助。

集中式缓存方案的主要优势:
(1)用户可以指定常用数据或者高优先级任务对应的数据常驻内存,避免被淘汰到磁盘。例如在数据仓库应用中事实表会频繁与其他表JOIN,如果将这些事实表常驻内存,当DataNode内存使用紧张的时候也不会把这些数据淘汰出去,可以很好的实现了对于关键生产任务的SLA保障;
(2)由NameNode统一进行缓存的集中管理,DFSClient根据Block被Cache分布情况调度任务,尽可能实现本地内存读,减少资源浪费;
(3)明显提升读性能。当DFSClient要读取的数据被Cache在同一DataNode时,可以通过ZeroCopy直接从内存读,略过磁盘IO和checksum校验等环节,从而提升读性能;
(4)由于NameNode统一管理并作为元数据的一部分进行持久化处理,即使DataNode节点出现宕机,Block移动,集群重启,Cache不会受到影响。

二、部署与使用

2.1 部署

集群开启HDFS集中式缓存特性非常简单,虽然HDFS本身为集中式缓存在NameNode/DataNode端均提供了多个配置参数,但是大多不是必须配置项,最核心的配置项是DataNode侧一个参数。

hdfs-site.xmlhdfs-default.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<property>
<name>dfs.datanode.max.locked.memory</name>
<value>0</value>
<description>
The amount of memory in bytes to use for caching of block replicas in
memory on the datanode. The datanode's maximum locked memory soft ulimit
(RLIMIT_MEMLOCK) must be set to at least this value, else the datanode
will abort on startup.
By default, this parameter is set to 0, which disables in-memory caching.
If the native libraries are not available to the DataNode, this
configuration has no effect.
</description>
</property>

如配置项描述所述,该配置的默认值为0,表示集中式缓存特性处于关闭状态,选择适当的值打开该特性。

开启集中式缓存特性需要注意两个前提:
(1)DataNode的native库必须可用;因为集中式缓存特性通过系统调用mmap/mlock实现,DataNode需要通过native库支持完成系统调用,否则会导致该特性不生效。
(2)系统memlock至少与配置值相同;因为集中式缓存特性通过系统调用mmap/mlock实现,所以系统最大锁定内存空间需要至少与DataNode配置的锁定空间大小相同,否则会导致DataNode进行启动失败。

此外,HDFS还包括了其他可选的配置项:

hdfs-site.xmlhdfs-default.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<property>
<name>dfs.namenode.path.based.cache.refresh.interval.ms</name>
<value>30000</value>
<description>The amount of milliseconds between subsequent path cache rescans. Path cache rescans are when we calculate which blocks should be cached, and on what datanodes. By default, this parameter is set to 30 seconds.</description>
</property>
<property>
<name>
dfs.namenode.path.based.cache.block.map.allocation.percent
</name>
<value>0.25</value>
<description>The percentage of the Java heap which we will allocate to the cached blocks map. The cached blocks map is a hash map which uses chained hashing. Smaller maps may be accessed more slowly if the number of cached blocks is large; larger maps will consume more memory.</description>
</property>
<property>
<name>dfs.cachereport.intervalMsec</name>
<value>10000</value>
<description>Determines cache reporting interval in milliseconds. After this amount of time, the DataNode sends a full report of its cache state to the NameNode. The NameNode uses the cache report to update its map of cached blocks to DataNode locations. This configuration has no effect if in-memory caching has been disabled by setting dfs.datanode.max.locked.memory to 0 (which is the default). If the native libraries are not available to the DataNode, this configuration has no effect.</description>
</property>
property>
<name>dfs.datanode.fsdatasetcache.max.threads.per.volume</name>
<value>4</value>
<description>The maximum number of threads per volume to use for caching new data on the datanode. These threads consume both I/O and CPU. This can affect normal datanode operations.</description>
</property>

2.2 使用

HDFS集中式缓存对数据读写接口并没有影响,正常调用已缓存数据的读写即可使用缓存特性。 为了便于数据管理,HDFS通过CacheAdmin对外暴露了一系列缓存管理的接口,其中CLI接口如下。

CacheAdmin

1
2
3
4
5
6
7
8
9
10
11
Usage: bin/hdfs cacheadmin [COMMAND]
[-addDirective -path <path> -pool <pool-name> [-force] [-replication <replication>] [-ttl <time-to-live>]]
[-modifyDirective -id <id> [-path <path>] [-force] [-replication <replication>] [-pool <pool-name>] [-ttl <time-to-live>]]
[-listDirectives [-stats] [-path <path>] [-pool <pool>] [-id <id>]
[-removeDirective <id>]
[-removeDirectives -path <path>]
[-addPool <name> [-owner <owner>] [-group <group>] [-mode <mode>] [-limit <limit>] [-maxTtl <maxTtl>]
[-modifyPool <name> [-owner <owner>] [-group <group>] [-mode <mode>] [-limit <limit>] [-maxTtl <maxTtl>]]
[-removePool <name>]
[-listPools [-stats] [<name>]]
[-help <command-name>]

CacheAdmin主要对用户暴露的是对缓存数据/缓存池(在系统架构及原理进行详细解释)的增删改查功能,另外还提供了相关的缓存策略,供用户灵活使用。
如这里期望能够缓存数据仓库中被频繁访问的user表数据:

CacheAdmin CLI

1
2
$HADOOP_HOME/bin/hdfs cacheadmin -addPool factPool -owner hadoop-user -group hadoop-user -mod 777 -limit 1024000000 -ttl 2d
$HADOOP_HOME/bin/hdfs cacheadmin -addDirective -path /user/hive/warehouse/dw.db/user -pool factPool -force -replication 3 -ttl 1d

首先新建名称为factPool的缓存池,并赋予相关的用户组及权限等信息,另外限制该缓存池可以缓存的最大空间及缓存数据的最大TTL等;
然后将user表数据加入到缓存池factPool进行缓存,并指定缓存时间为1天,缓存3个副本;
之后当有读user表数据的请求过来后即可调度到缓存节点上从内存直接读取,从而提升读性能。其它CLI的用法可类比这里不再一一罗列。

2.3 适用场景

当前内存相比HDD成本还比较高,另外对于Hadoop集群,节点的内存大部分是分配给YARN供计算使用,所以剩余的内存资源其实非常有限,能够提供给HDFS集中式缓存使用的部分更少,为了使有限的资源发挥出最好的效率,这里提供几点建议:
(1)数据仓库中存在一部分事实表被频繁访问,与其他事实表/维度表JOIN,将访问频率较高的部分事实表进行缓存,可以提高数据生产的效率;
(2)根据局部性原理,最近写入的数据最容易被访问到,从数据仓库应用来看,每天有大量报表统计任务,需要读取前一天数据做分析,事实上大量表都是按天进行分区,可以把符合要求的热点分区数据做缓存处理,过期后清理缓存,也能大幅提升生产和统计效率;
(3)资源数据,当前存在非常多的计算框架依赖JAR/SO等一些公共资源,传统的做法是将这些资源数据写入到HDFS,通过Distributed Cache进行全局共享,也便于管理,如Spark/Tez/Hive/Kafaka等使用到的公共JAR包。如果将这部分资源数据进行长期缓存,可以优化JVM初始化时间,进而提升效率;
(4)其他;

三、系统架构及原理

3.1 架构

设计文档中定义集中式缓存机制(Centralized cache management):

An explicit caching mechanism that allows users to specify paths to be cached by HDFS.

其中包含了若干具体的目标:

Strong semantics for cached paths
Exposing cache state to framework schedulers
Defer cache policy decisions to higher­ level frameworks
Backwards compatibility
Management, debugging, metrics
Security
Quotas

为了实现上述目标,首先引入两个重要的概念:CacheDirective,CachePool。其中CacheDirective定义了缓存基本单元,本质上是文件系统的目录或文件与具体缓存策略等属性的集合;为了便于灵活管理,将属性类似的一组CacheDirective组成缓存池(CachePool),在缓存池CachePool上可以进行权限、Quota、缓存策略和统计信息等灵活控制。
在具体展开集中式缓存的系统架构和原理前,首先梳理对CacheDirective缓存的详细流程,具体如图1:
(1)用户通过调用客户端接口向NameNode发起对CacheDirective(Directory/File)缓存请求;
(2)NameNode接收到缓存请求后,将CacheDirective转换成需要缓存的Block集合,并根据一定的策略并将其加入到缓存队列中;
(3)NameNode接收到DataNode心跳后,将缓存Block的指令下发到DataNode;
(4)DataNode接收到NameNode发下的缓存指令后根据实际情况触发Native JNI进行系统调用,对Block数据进行实际缓存;
(5)此后DataNode定期(默认10s)向NameNode进行缓存汇报,更新当前节点的缓存状态;
(6)上层调度尽可能将任务调度到数据所在的DataNode,当客户端进行读数据请求时,通过DFSClient直接从内存进行ZeroCopy,从而显著提升性能;


图1 HDFS集中式缓存管理流程图

HDFS集中式缓存的架构如图2所示,这里主要涉及到NameNode和DataNode两侧的管理和实现,NameNode对数据缓存的统一集中管理,并根据策略调度合适的DataNode对具体的数据进行数据的缓存和置换,DataNode根据NameNode的指令执行对数据的实际缓存和置换。


图2 HDFS集中式缓存架构图

3.2 DataNode

DataNode是执行缓存和置换的具体执行者,具体来说即cacheBlock和uncacheBlock调用。FsDatasetImpl#FsDatasetCache类是该操作的执行入口。

Manages caching for an FsDatasetImpl by using the mmap(2) and mlock(2) system calls to lock blocks into memory. Block checksums are verified upon entry into the cache.

FsDatasetCache的核心是称为mappableBlockMap的HashMap,用于维护当前缓存的Block集合,其中Key为标记该Block的ExtendedBlockId,为了能够实现与Federation的兼容,在blockid的基础上增加了blockpoolid,这样多个blockpool的block不会因为blockid相同产生冲突;Value是具体的缓存数据及当前的缓存状态。

FsDatasetCache#mappableBlockMap

1
private final HashMap<ExtendedBlockId, Value> mappableBlockMap = new HashMap<ExtendedBlockId, Value>();

mappableBlockMap更新只有FsdatasetCache提供的两个具体的函数入口:

FsDatasetCache.java

1
2
synchronized void cacheBlock(long blockId, String bpid, String blockFileName, long length, long genstamp, Executor volumeExecutor)
synchronized void uncacheBlock(String bpid, long blockId)

可以看出,对mappableBlockMap的并发控制实际上放在了cacheBlock和uncacheBlock两个方法上,虽然锁粒度比较大,但是并不会对并发读写带来影响。原因是:cacheBlock在系统调用前构造空Value结构加入mappableBlockMap中,此时该Value维护的Block状态是CACHING,之后将真正缓存数据的任务加入异步任务CachingTask去完成,所以锁很快会被释放,当处于CACHING状态的Block被访问的时候会退化到从HDD访问,异步任务CachingTask完成数据缓存后将其状态置为CACHED;uncacheBlock是同样原理。所以,虽然锁的粒度比较大,但是并不会阻塞后续的数据缓存任务,也不会对数据读写带来额外的开销。

顺着自底向上的思路,再来看触发cacheBlock和uncacheBlock的场景,通过函数调用关系容易看到缓存和置换的触发场景都比较简单。
(1)cacheBlock:唯一的入口是从ANN(HA Active NameNode)下发的指令;

(2)uncacheBlock:与cacheBlock不同,uncacheBlock存在三个入口:append,invalidate和uncache。其中uncache也是来自ANN下发的指令;append和invalidate触发uncacheBlock的原因是:append会导致数据发生变化,缓存失效需要清理后重新缓存,invalidate来自删除操作,缓存同样失效需要清理。

DataNode的处理逻辑比较简单,到这里整个实现的主路径基本梳理完成。

3.3 NameNode

相比DataNode的实现逻辑,NameNode侧要复杂的多,缓存管理继承了NameNode一贯的模块化思路,通过CacheManager实现了整个集中式缓存在管理端的复杂处理逻辑。
CacheManager通过几个关键数据结构组织对数据缓存的实现。

CacheManager.javagithub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Cache directives, sorted by ID.
*/
private final TreeMap<Long, CacheDirective> directivesById = new TreeMap<Long, CacheDirective>();
/**
* Cache directives, sorted by path
*/
private final TreeMap<String, List<CacheDirective>> directivesByPath = new TreeMap<String, List<CacheDirective>>();
/**
* Cache pools, sorted by name.
*/
private final TreeMap<String, CachePool> cachePools = new TreeMap<String, CachePool>();
/**
* All cached blocks.
*/
private final GSet<CachedBlock, CachedBlock> cachedBlocks;
private CacheReplicationMonitor monitor;

通过上述的几个核心集合类数据结构,很容易实现用户层对缓存数据/缓存池的增删改查功能调用的支持。

虽然上述的几个核心集合类数据结构能够容易支持用户对相关接口的调用,但是从整个集中式缓存全局来看,并没有将用户调用接口对目录/文件的缓存同DataNode实际数据缓存建立起有效连接。CacheReplicationMonitor发挥的即是这种作用,CacheReplicationMonitor使得缓存接口调用、核心数据结构以及数据缓存真正有序流动起来。

如果对BlockManager#ReplicationMonitor比较熟悉的话,可以发现CacheReplicationMonitor的工作模式几乎从ReplicationMonitor复制而来,CacheReplicationMonitor本质上也是一个定时线程,与ReplicationMonitor稍微不同的是,CacheReplicationMonitor除了定时触发外用户的缓存调用也会触发。其核心是其rescan方法,具体来看主要做三个具体工作:
(1)resetStatistics:对CacheManager从CacheDirective和CachePool两个维度对统计信息进行更新;
(2)rescanCacheDirectives:顺序扫描CacheManager#directivesById数据结构(与CacheManager#directivesByPath实际上等价),检查哪些CacheDirective(目录或文件)需要进行cache处理,一旦发现有文件需要进行缓存,立即将该文件的所有Block加入到CacheReplicationMonitor#cachedBlocks(GSet<CachedBlock, CachedBlock> cachedBlocks)中,后续工作由rescanCachedBlockMap接着进行;
(3)rescanCachedBlockMap:顺序扫描CacheReplicationMonitor#cachedBlocks的所有CacheBlock,由于CacheBlock也有副本个数的概念,rescanCachedBlockMap在扫描的过程中会发现实际缓存的Block副本数与预设的缓存副本有差异,比如新增缓存请求/节点宕机/心跳异常/节点下线等等导致Cache Block副本数与期望值之间产生差异,所以需要CacheReplicationMonitor进行周期检查和修复,根据差异多少关系将对应的CacheBlock加入到DatanodeManager管理的对应DatanodeDescriptor#{pendingCached,pendingUncached}数据结构中,待对应的DataNode心跳过来后将其转化成对应的执行指令下发给DataNode实际执行。关于心跳与指令下发的细节已经在之前文章中多处提到,这里不再展开。

这里还遗留一个问题,由于Block与CacheBlock均存在多副本的关系,如何选择具体的DataNode执行缓存或置换。
(1)uncacheBlock:uncacheBlock选择对应的DataNode其实比较简单,顺序遍历所有已经缓存了该Block的DataNode,为其准备uncacheBlock指令,直到缓存副本达到预期即可;
(2)cacheBlock:cacheBlock稍微复杂,先从Block副本所在的所有DataNode集合中排除DecommissionInProgress/Decommissioned/CorruptBlock所在DataNode/已经缓存了该Block的DataNode之后,在剩下的DataNode集合中随机进行选择即可。

DataNode实际执行完成NameNode下发的cacheBlock/uncacheBlock指令后,在下次cacheReport(默认时间间隔10s)汇报给NameNode,CacheManager根据汇报情况对缓存执行情况对CacheManager#cachedBlocks进行更新。

至此,NameNode端的集中式缓存逻辑形成了合理有效的闭环,基本实现了设计目标。

3.3 DFSClient

客户端本身的读数据逻辑基本没有变化,与传统的读模式区别在于,当客户端向DataNode发出REQUEST_SHORT_CIRCUIT_FDS请求到达DataNode后,DataNode会首先判断数据是否被缓存,如果数据已经缓存,将缓存信息返回到客户端后续直接进行内存数据的ZeroCopy;如果数据没有缓存采用传统方式进行ShortCircuit-Read。

四、总结

通过前述分析,可以看到HDFS集中式缓存优势非常明显:
1、显著提升数据读性能;
2、提升集群内存利用率;

虽然优势明显,但是HDFS集中式缓存目前还有一些不足:
1、内存置换策略(LRU/LFU/etc.)尚不支持,如果内存资源不足,缓存会失败;
2、集中式缓存与Balancer之间还不能很好的兼容,大量的Block迁移会造成频繁内存数据交换;
3、缓存能力仅对读有效,对写来说其实存在副作用,尤其是Append;
4、与Federation的兼容非常不友好;

总之,内存模式在存储和计算的多个方向上已经成为业界广泛关注的方向和趋势,Hadoop社区也在投入精力持续在内存上发力,从集中式缓存来看,收益非常明显,但是还存在一些小问题,在实际应用过程中还需要优化和改进。

#创建缓存组,默认为cache_data
hdfs cacheadmin -addPool cache_data -mode 0777
Successfully added cache pool cache_data.
#生成一个1GB大小的文件
dd if=/dev/zero of=/tmp/test.zero bs=1M count=1024
+0 records in
+0 records out
24 bytes (1.1 GB) copied, 1.25545 s, 855 MB/s
hdfs dfs -put /tmp/test.zero /data
#生成缓存指令
hdfs cacheadmin -addDirective -path /data -pool cache_data -ttl 1d
Added cache directive 1
#显示缓存池的信息
hdfs cacheadmin -listPools -stats cache_data
Found 1 result.
NAME OWNER GROUP MODE LIMIT MAXTTL BYTES_NEEDED BYTES_CACHED BYTES_OVERLIMIT FILES_NEEDED FILES_CACHED
cache_data hdfs hdfs rwxrwxrwx unlimited never 1073741824 1073741824 0 1 1
#统计信息,显示EXP Date
hdfs cacheadmin -listDirectives -path /data

Found 1 entry
ID POOL REPL EXPIRY PATH
cache_data 1 2014-12-22T15:38:31+0800 /data
#删除缓存指令和缓存池
hdfs cacheadmin -removeDirectives -path /data
Removed cache directive 1
Removed every cache directive with path /data
hdfs cacheadmin -removePool cache_data
Successfully removed cache pool cache_data.

HDFS中的集中缓存管理详解的更多相关文章

  1. 跟我一起学WCF(8)——WCF中Session、实例管理详解

    一.引言 由前面几篇博文我们知道,WCF是微软基于SOA建立的一套在分布式环境中各个相对独立的应用进行交流(Communication)的框架,它实现了最新的基于WS-*规范.按照SOA的原则,相对独 ...

  2. 大数据Hadoop核心架构HDFS+MapReduce+Hbase+Hive内部机理详解

    微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于 2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer,目前致力于分享这几年的学习经验. ...

  3. Hadoop核心架构HDFS+MapReduce+Hbase+Hive内部机理详解

    转自:http://blog.csdn.net/iamdll/article/details/20998035 分类: 分布式 2014-03-11 10:31 156人阅读 评论(0) 收藏 举报 ...

  4. .Net 缓存依赖详解

    缓存命名空间的讲解流程 16.1  System.Web.Caching简介 本节从缓存命名空间的总体简介和组成结构入手,从整体上对System.Web.Caching进行概述. 16.1.1  Sy ...

  5. php中的PDO函数库详解

    PHP中的PDO函数库详解 PDO是一个“数据库访问抽象层”,作用是统一各种数据库的访问接口,与mysql和mysqli的函数库相比,PDO让跨数据库的使用更具有亲和力:与ADODB和MDB2相比,P ...

  6. hibernate缓存机制详解

    hiberante面试题—hibernate缓存机制详解   这是面试中经常问到的一个问题,可以按照我的思路回答,准你回答得很完美.首先说下Hibernate缓存的作用(即为什么要用缓存机制),然后再 ...

  7. 浏览器 HTTP 协议缓存机制详解

    最近在准备优化日志请求时遇到了一些令人疑惑的问题,比如为什么响应头里出现了两个 cache control.为什么明明设置了 no cache 却还是发请求,为什么多次访问时有时请求里带了 etag, ...

  8. Oracle权限管理详解

    Oracle权限管理详解 转载--CzmMiao的博客生活 Oracle 权限 权限允许用户访问属于其它用户的对象或执行程序,ORACLE系统提供三种权限:Object 对象级.System 系统级. ...

  9. Redis for Windows(C#缓存)配置文件详解

    Redis for Windows(C#缓存)配置文件详解   前言 在上一篇文章中主要介绍了Redis在Windows平台下的下载安装和简单使用http://www.cnblogs.com/aehy ...

随机推荐

  1. SPSS中文版安装

    我这里安装的是64位的 双击安装包 选择您的安装目录 在这里等几分钟 重要!安装完毕后将图中选项勾去掉! 这个时候输入一下授权码 9DNCAF2O3QVDV7FBIO696OO6GWLNXZPPRYT ...

  2. Java程序---多数字求和

    题目: 编写一个程序,此程序从命令行接收多个数字,求和之后输出结果. 设计思想: 1.记录要输入的数字的个数n 2.建立一个长度为n的数组存储输入的数字 3.累加求和并输出结果 注:此程序中应用了Sc ...

  3. 一、Html5基础讲解以及五个标签

    什么是html?html是用来描述网页的一种语言html指超文本标记语言html不是编程语言,是一种标记语言 HTML基础标签 Head.body html标题 <h1>…<h6&g ...

  4. mysql定时备份

    1.创建备份DB的脚本 python版本的脚本 db_backup.py import os password='df' os.system('mysqldump -uroot -p{} fwss & ...

  5. 图解RabbitMQ

    花点时间重新把rabbitMQ的整体流程粗略的梳理了一遍,便于理解.总体来说,消息产生者->queue->消息消费者可以图解如下: 文字解说:消息产生者 producter 和 consu ...

  6. python学习之----导航树

    findAll 函数通过标签的名称和属性来查找标签 .但是如果你需要通过标签在文档中的位 置来查找标签,该怎么办?这就是导航树(Navigating Trees)的作用.在第1 章里,我们 看过用单一 ...

  7. jsfiddle将demo设置为public公开的

    jsfiddle的demo虽然可以通过链接分享给所有人,但是进入个人主页是没有的,需要将项目设置为公开public的 根据提示,打开demo项目页==>左侧菜单==>填写标题和描述==&g ...

  8. 学习excel的使用技巧一空格替换为0

    问题1  把excel表格中的空格  填充为0 方法1 选中CDE列    CRTL+F 查找空 替换为0 方法2 选中CDE列 CRTL+G 打开定位  点击条件定位  选择空值 点击确定  然后在 ...

  9. py库: Selenium (自动化测试)

    http://blog.csdn.net/liujingqiu/article/details/50458553 http://www.cnblogs.com/zhaof/p/6953241.html ...

  10. laravel 的 intervention-image 图像处理笔记

    安装: https://blog.csdn.net/beyond__devil/article/details/62230610 需求: PHP >= 5.4 Fileinfo 扩展 GD库 & ...