Region Compact请求是在Region MemStore Flush之后被触发的:

boolean shouldCompact = region.flushcache();

// We just want to check the size
boolean shouldSplit = region.checkSplit() != null; if (shouldSplit) {
this.server.compactSplitThread.requestSplit(region);
} else if (shouldCompact) {
server.compactSplitThread.requestCompaction(region, getName());
} server.getMetrics().addFlush(region.getRecentFlushInfo());

region.flushcache方法负责该Region实例的Flush操作,该操作的返回值shouldCompact如果为true,则表示该Region达到了Region Compact的要求,但此时并不一定会触发Compact请求,因为shouldCompact之后会有shouldSplit的判断,只有当shouldSplit为false且shouldCompact为true时才会触发Compact请求。

注:某一Region的Flush、Split、Compact是分别在不同的线程中进行的,三者之间的协调会在后续详细介绍,在此仅仅关注Compact的触发条件,以及Compact进行时如何选取StoreFile进行合并即可。

Region Compact触发(即shouldCompact为true)的判断条件是该Region中各个Store中的StoreFile数目:

/**
* See if there's too much store files in this store
*
* @return true if number of store files is greater than the number defined
* in minFilesToCompact
*/
public boolean needsCompaction() {
return (storefiles.size() - filesCompacting.size()) > minFilesToCompact;
}

在某一Region做Flush操作时,内部是逐个对该Region的多个Store依次进行Flush的,每一个Store Flush完成之后,就会调用上述代码进行判断:

storefiles:表示Store Flush之后,StoreFile的总数目;

filesCompacting:表示Store中正处于Compact状态的StoreFile的数目,即表示该Store中的某些StoreFile已被选择进行Compact,这次判断需要忽略这些StoreFile;

minFilesToCompact:表示进行Store Compact时Store中的StoreFile数目(不包含已被选择进行Compact的StoreFile)所需要达到的下限值,它的值是由以下代码计算而来的:

// By default, compact if storefile.count >= minFilesToCompact
this.minFilesToCompact = Math.max(2, conf.getInt(
"hbase.hstore.compaction.min",
/* old name */conf
.getInt("hbase.hstore.compactionThreshold", 3)));

注意:hbase.hstore.compactionThreshold是一个旧的配置项,当配置项中出现hbase.hstore.compaction.min时,它将失效。

由上所示可知,shouldCompact为true的前提条件是当前Region中的某个Store中的StoreFile数目需要达到下限要求(需要去除那些已处于合并状态的StoreFile),如果满足条件,则通过requestCompaction发出具体的Compact请求,requestCompaction有多个重载方法,最终执行流程会被导向下述这些重载方法:

@Override
public synchronized List<CompactionRequest> requestCompaction(
final HRegion r, final String why, int pri,
final List<CompactionRequest> requests) throws IOException {
List<CompactionRequest> ret; // not a special compaction request, so make out own list
if (requests == null) {
ret = new ArrayList<CompactionRequest>(r.getStores().size()); for (Store s : r.getStores().values()) {
ret.add(requestCompaction(r, s, why, pri, null));
}
} else {
ret = new ArrayList<CompactionRequest>(requests.size()); for (CompactionRequest request : requests) {
ret.add(requestCompaction(r, request.getStore(), why, pri,
request));
}
} return ret;
}

可以看出,实际的Compact请求也是以Store为单位发出的,如下代码所示:

@Override
public synchronized CompactionRequest requestCompaction(final HRegion r,
final Store s, final String why, int priority,
CompactionRequest request) throws IOException {
if (this.server.isStopped()) {
return null;
} CompactionRequest cr = s.requestCompaction(priority, request); if (cr != null) {
cr.setServer(server); if (priority != Store.NO_PRIORITY) {
cr.setPriority(priority);
} ThreadPoolExecutor pool = s.throttleCompaction(cr.getSize()) ? largeCompactions
: smallCompactions; pool.execute(cr); if (LOG.isDebugEnabled()) {
String type = (pool == smallCompactions) ? "Small " : "Large "; LOG.debug(type
+ "Compaction requested: "
+ cr
+ (why != null && !why.isEmpty() ? "; Because: " + why
: "") + "; " + this);
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Not compacting " + r.getRegionNameAsString()
+ " because compaction request was cancelled");
}
} return cr;
}

首先会根据Store的状态形成一个CompactionRequest对象(实现了Runnable接口,所谓的请求实际上就是一个Runnable对象,以线程的形式执行),然后根据当前Store需要进行Compact操作的StoreFile的总大小(不是文件数目)判断是何种类型的Compact(LargeCompaction或者SmallCompaction),从而将CompactionRequest提交至不同的线程池中执行。

LargeCompaction与SmallCompaction的选择过程如下:

boolean throttleCompaction(long compactionSize) {
long throttlePoint = conf.getLong(
"hbase.regionserver.thread.compaction.throttle", 2
* this.minFilesToCompact
* this.region.memstoreFlushSize); return compactionSize > throttlePoint;
}

至此,CompactRequest请求已被提交至相应的线程池中,根据线程池的执行策略,在适当的时机被执行。

CompactionRequest

如何根据Store的状态生成相应的CompactionRequest对象,即选择Store中的哪些StoreFile进行Compact,该过程是由Store requestCompaction方法完成的,下面对该方法的核心代码进行讲述。

代码主要是在一个同步代码块中完成的,

synchronized (filesCompacting) {
......
}

随着MemStore的不断Flush操作,可能会造成同一个Region的多次Compact Request,为了避免同一StoreFile出现在多个Compact中,所以需要对filesCompacting(表示已被选取进行Compact的StoreFile)进行互斥访问。

接下来的所有代码都是出现在上述同步代码块中的。

// candidates = all storefiles not already in compaction queue
List<StoreFile> candidates = Lists.newArrayList(storefiles);

选取Store中的所有StoreFile作为备选对象,storefiles是依据StoreFile.Comparators.FLUSH_TIME进行排序的,此时candidates以及后续的filesCompacting也是有序的(older –> newer)。

if (!filesCompacting.isEmpty()) {
// exclude all files older than the newest file we're
// currently
// compacting. this allows us to preserve contiguity
// (HBASE-2856)
StoreFile last = filesCompacting
.get(filesCompacting.size() - 1); int idx = candidates.indexOf(last); Preconditions.checkArgument(idx != -1); candidates.subList(0, idx + 1).clear();
}

如果filesCompacting不为空,则需要从candidates中移除一些StoreFile:

(1)选中filesCompacting中的最后一个StoreFile保存至变量last中,即filesCompacting中FlushTime最新的那个StoreFile;

(2)移除candidates中所有FlushTime比last旧的StoreFile。

这样做的目的主要是为了Compact操作的持续性,即每次尽可以多的合并新产生的StoreFile,不要重复去Compact那些已经被合并过或正在进行Compact的StoreFile。

boolean override = false;

if (region.getCoprocessorHost() != null) {
override = region.getCoprocessorHost().preCompactSelection(
this, candidates, request);
} CompactSelection filesToCompact; if (override) {
// coprocessor is overriding normal file selection
filesToCompact = new CompactSelection(conf, candidates);
} else {
filesToCompact = compactSelection(candidates, priority);
} if (region.getCoprocessorHost() != null) {
region.getCoprocessorHost().postCompactSelection(
this,
ImmutableList.copyOf(filesToCompact
.getFilesToCompact()), request);
}

这里不讨论Coprocessor对Compact的影响,所以override为false,执行compactSelection算法(详细算法描述见后)进行选取,选取结果会被保存至filesToCompact中。

// no files to compact
if (filesToCompact.getFilesToCompact().isEmpty()) {
return null;
}

经过相应算法选取之后,如果选取结果为空,则表示不需要进行Compact,返回null即可。

// basic sanity check: do not try to compact the same StoreFile
// twice.
if (!Collections.disjoint(filesCompacting,
filesToCompact.getFilesToCompact())) {
// TODO: change this from an IAE to LOG.error after
// sufficient testing
Preconditions.checkArgument(false, "%s overlaps with %s",
filesToCompact, filesCompacting);
}

这是一个对选取结果进行核验的工作,主要是为了保证选取结果不会出现在filesCompacting中,即不会对同一个StoreFile进行两次Compact操作。

filesCompacting.addAll(filesToCompact.getFilesToCompact());

Collections.sort(filesCompacting,
StoreFile.Comparators.FLUSH_TIME);

将选取结果添加至filesCompacting中,并对更新后的filesCompacting依据FlushTime重新排序。

// major compaction iff all StoreFiles are included
boolean isMajor = (filesToCompact.getFilesToCompact().size() == this.storefiles
.size()); if (isMajor) {
// since we're enqueuing a major, update the compaction wait
// interval
this.forceMajor = false;
}

如果选取结果包含该Store中所有的StoreFile,则表示我们应该进行一个Major Compaction,并取消Force Major Compaction。

// everything went better than expected. create a compaction
// request
int pri = getCompactPriority(priority);

计算本次Compact的优先级:

/**
* @return The priority that this store should have in the compaction queue
* @param priority
*/
public int getCompactPriority(int priority) {
// If this is a user-requested compaction, leave this at the highest
// priority
if (priority == PRIORITY_USER) {
return PRIORITY_USER;
} else {
return this.blockingStoreFileCount - this.storefiles.size();
}
}

如果Compact Request是由用户发起的,则需要给该Compaction赋于最高的优先级PRIORITY_USER(1),否则需要进行差值计算得出,其中blockingStoreFileCount来源于如下代码:

this.blockingStoreFileCount = conf.getInt(
"hbase.hstore.blockingStoreFiles", 7);

以下代码开始生成具体的CompactionRequest的对象,在我们讲述的流程中request为null,所以通过相应的构造函数创建对象即可。

// not a special compaction request, so we need to make one
if (request == null) {
request = new CompactionRequest(region, this,
filesToCompact, isMajor, pri);
} else {
// update the request with what the system thinks the
// request should be
// its up to the request if it wants to listen
request.setSelection(filesToCompact); request.setIsMajor(isMajor); request.setPriority(pri);
}

代码至此完成了一个CompactRequest的形成。

 

CompactSelection

该算法由方法compactSelection实现:

/**
* Algorithm to choose which files to compact
*
* Configuration knobs:
*
* "hbase.hstore.compaction.ratio" normal case: minor compact when file <=
* sum(smaller_files) * ratio
*
* "hbase.hstore.compaction.min.size" unconditionally compact individual
* files below this size
*
* "hbase.hstore.compaction.max.size" never compact individual files above
* this size (unless splitting)
*
* "hbase.hstore.compaction.min" min files needed to minor compact
*
* "hbase.hstore.compaction.max" max files to compact at once (avoids OOM)
*
* @param candidates
* candidate files, ordered from oldest to newest
* @return subset copy of candidate list that meets compaction criteria
* @throws IOException
*/
CompactSelection compactSelection(List<StoreFile> candidates, int priority)
throws IOException {
......
}

方法开始处有如下注释:

// ASSUMPTION!!! filesCompacting is locked when calling this function

/*
* normal skew:
*
* older ----> newer _ | | _ | | | | _ --|-|- |-|-
* |-|---_-------_------- minCompactSize | | | | | | | | _ | | | | | | |
* | | | | | | | | | | | | | | | | | | |
*/

该算法的选取主要从旧到新依次进行,minCompactSize 的应用见后面代码所示。

CompactSelection compactSelection = new CompactSelection(conf,
candidates);

创建一个CompactSelection对象,在构造方法中主要是初始化了一些参数。

boolean forcemajor = this.forceMajor && filesCompacting.isEmpty();

根据forceMajor(在compactSelection方法调用之前已经计算出该值)与filesCompacting的状态计算forcemajor的值。如果forecemajor为false则执行下述代码:

if (!forcemajor) {
// Delete the expired store files before the compaction selection.
if (conf.getBoolean("hbase.store.delete.expired.storefile", true)
&& (ttl != Long.MAX_VALUE)
&& (this.scanInfo.minVersions == 0)) {
CompactSelection expiredSelection = compactSelection
.selectExpiredStoreFilesToCompact(EnvironmentEdgeManager
.currentTimeMillis() - this.ttl); // If there is any expired store files, delete them by
// compaction.
if (expiredSelection != null) {
return expiredSelection;
}
} // do not compact old files above a configurable threshold
// save all references. we MUST compact them
int pos = 0; while (pos < compactSelection.getFilesToCompact().size()
&& compactSelection.getFilesToCompact().get(pos)
.getReader().length() > maxCompactSize
&& !compactSelection.getFilesToCompact().get(pos)
.isReference()) {
++pos;
} if (pos != 0) {
compactSelection.clearSubList(0, pos);
}
}

其中

// Delete the expired store files before the compaction selection.
if (conf.getBoolean("hbase.store.delete.expired.storefile", true)
&& (ttl != Long.MAX_VALUE)
&& (this.scanInfo.minVersions == 0)) {
CompactSelection expiredSelection = compactSelection
.selectExpiredStoreFilesToCompact(EnvironmentEdgeManager
.currentTimeMillis() - this.ttl); // If there is any expired store files, delete them by
// compaction.
if (expiredSelection != null) {
return expiredSelection;
}
}

这部分代码主要用于删除那些过期的StoreFile,如果存在有过期的StoreFile,则算法的选取结果即为这些过期的StoreFile,不再需要进行进一步的选取,在此先忽略这一步。

// do not compact old files above a configurable threshold
// save all references. we MUST compact them
int pos = 0; while (pos < compactSelection.getFilesToCompact().size()
&& compactSelection.getFilesToCompact().get(pos)
.getReader().length() > maxCompactSize
&& !compactSelection.getFilesToCompact().get(pos)
.isReference()) {
++pos;
} if (pos != 0) {
compactSelection.clearSubList(0, pos);
}

compactSelection.getFilesToCompact()的返回值即为candidates,该代码的作用是从前到后淘汰一些文件大小超过配置大小的StoreFile,直至在此过程中遇到下述两种情况:

(1)某StoreFile的文件大小小于或等于maxCompactSize(hbase.hstore.compaction.max.size);

(2)某StoreFile的文件类型为Reference(该文件类型在Split的过程中产生)。

if (compactSelection.getFilesToCompact().isEmpty()) {
LOG.debug(this.getHRegionInfo().getEncodedName() + " - " + this
+ ": no store files to compact"); compactSelection.emptyFileList(); return compactSelection;
}

如果经过前一步的淘汰过程之后,compactSelection.getFilesToCompact()的返回结果为空,则表示没有相应的StoreFile可以进行Compact,选取结束,直接返回即可。

// Force a major compaction if this is a user-requested major
// compaction,
// or if we do not have too many files to compact and this was requested
// as a major compaction
boolean majorcompaction = (forcemajor && priority == PRIORITY_USER)
|| (forcemajor || isMajorCompaction(compactSelection
.getFilesToCompact()))
&& (compactSelection.getFilesToCompact().size() < this.maxFilesToCompact);

majorcompaction为true,需要达到下述两个条件之一:

(1)用户主动请求进行Major Compaction;

(2)forcemajor为true或者满足系统主动进行Major Compaction的条件(主要根据StoreFile时间戳进行判断),而且此时待选取的StoreFile数目没有超过临界值(maxFilesToCompact:hbase.hstore.compaction.max)。

关于用户主动请求或系统主动进行Major Compaction的情况,后续再进行讨论。

接下来的代码会根据条件的不同出现两个大的代码块,分别进行讲述。

if (!majorcompaction
&& !hasReferences(compactSelection.getFilesToCompact())) {
......
} else {
......
}

(1)如果majorcompaction为false且剩余的待选取StoreFile中不包含Reference类型的文件,则执行如下代码:

// we're doing a minor compaction, let's see what files are
// applicable
int start = 0; double r = compactSelection.getCompactSelectionRatio();

r的值即为hbase.hstore.compaction.ratio,不考虑配置hbase.offpeak.start.hour、hbase.offpeak.end.hour情况。

// remove bulk import files that request to be excluded from minors
compactSelection.getFilesToCompact().removeAll(
Collections2.filter(compactSelection.getFilesToCompact(),
new Predicate<StoreFile>() {
public boolean apply(StoreFile input) {
return input.excludeFromMinorCompaction();
}
}));

在待选取的StoreFile中移除那些已经被设置从Minor Compaction中移除的StoreFile。

// skip selection algorithm if we don't have enough files
if (compactSelection.getFilesToCompact().size() < this.minFilesToCompact) {
if (LOG.isDebugEnabled()) {
LOG.debug("Not compacting files because we only have "
+ compactSelection.getFilesToCompact().size()
+ " files ready for compaction. Need "
+ this.minFilesToCompact + " to initiate.");
} compactSelection.emptyFileList(); return compactSelection;
}

如果此时我们选取的StoreFile数目小于minFilesToCompact(配置时注意该值不包含等于的情况),则本次Compact的操作取消,直接返回即可,其中minFilesToCompact的计算代码如下:

// By default, compact if storefile.count >= minFilesToCompact
this.minFilesToCompact = Math.max(2, conf.getInt(
"hbase.hstore.compaction.min",
/* old name */conf
.getInt("hbase.hstore.compactionThreshold", 3)));

继续进行选取算法。

// get store file sizes for incremental compacting selection.
int countOfFiles = compactSelection.getFilesToCompact().size(); long[] fileSizes = new long[countOfFiles]; long[] sumSize = new long[countOfFiles]; for (int i = countOfFiles - 1; i >= 0; --i) {
StoreFile file = compactSelection.getFilesToCompact().get(i); fileSizes[i] = file.getReader().length(); // calculate the sum of fileSizes[i,i+maxFilesToCompact-1) for
// algo
int tooFar = i + this.maxFilesToCompact - 1; sumSize[i] = fileSizes[i]
+ ((i + 1 < countOfFiles) ? sumSize[i + 1] : 0)
- ((tooFar < countOfFiles) ? fileSizes[tooFar] : 0);
}

上述代码实际是做一些准备工作,主要生成了两个重要的数组:fileSizes、sumSize。

fileSizes:依次保存着各个StoreFile的文件大小;

sumSize:对应fileSizes,依次保存着fileSizes[i,i+maxFilesToCompact-1)之和,即sumSize[i]中保存着compactSelection.getFilesToCompact()中第i个StoreFile(包含)之后最多maxFilesToCompact个StoreFile的总大小。

根据这两个数组,继续选举算法:

/*
* Start at the oldest file and stop when you find the first file
* that meets compaction criteria:
*
* (1) a recently-flushed, small file (i.e. <= minCompactSize) OR
* (2) within the compactRatio of sum(newer_files)
*
* Given normal skew, any newer files will also meet this criteria
*
* Additional Note: If fileSizes.size() >> maxFilesToCompact, we
* will recurse on compact(). Consider the oldest files first to
* avoid a situation where we always compact [end-threshold,end).
* Then, the last file becomes an aggregate of the previous
* compactions.
*/
while (countOfFiles - start >= this.minFilesToCompact
&& fileSizes[start] > Math.max(minCompactSize,
(long) (sumSize[start + 1] * r))) {
++start;
} int end = Math.min(countOfFiles, start + this.maxFilesToCompact); long totalSize = fileSizes[start]
+ ((start + 1 < countOfFiles) ? sumSize[start + 1] : 0); compactSelection = compactSelection.getSubList(start, end);

从前到后(根据FlushTime从旧到新)依次淘汰相应的StoreFile,直至遇到某一StoreFile的大小满足条件:

fileSizes[start] <= Math.max(minCompactSize, (long) (sumSize[start + 1] * r))

minCompactSize:hbase.hstore.compaction.min.size

理解其中的一种情况:fileSizes[start] > (long) (sumSize[start + 1] * r) > minCompactSize,它表示当前StoreFile的大小比它之后maxFilesToCompact范围内的所有StoreFile大小之和还要大,我们认为当前StoreFile太大,应淘汰掉,优先合并它之后那些较小的StoreFile。

根据注释可知,HBase希望最近Flush、Small的StoreFile文件大小满足下述两个条件之一:

(1)小于等于minCompactSize;

(2)小于等于the compactRatio of sum(newer_files)。

上述代码最后会更新compactSelection对象的状态。

// if we don't have enough files to compact, just wait
if (compactSelection.getFilesToCompact().size() < this.minFilesToCompact) {
if (LOG.isDebugEnabled()) {
LOG.debug("Skipped compaction of " + this + ". Only "
+ (end - start) + " file(s) of size "
+ StringUtils.humanReadableInt(totalSize)
+ " have met compaction criteria.");
} compactSelection.emptyFileList(); return compactSelection;
}

如果此时没有足够的StoreFile进行Compact(StoreFile数目小于minFilesToCompact),则跳过这次Compact,直接返回。

(2)如果majorcompaction为true或者待选取的StoreFile中包含Reference文件,则执行如下代码:

if (majorcompaction) {
if (compactSelection.getFilesToCompact().size() > this.maxFilesToCompact) {
LOG.debug("Warning, compacting more than "
+ this.maxFilesToCompact
+ " files, probably because of a user-requested major compaction"); if (priority != PRIORITY_USER) {
LOG.error("Compacting more than max files on a non user-requested compaction");
}
}
} else if (compactSelection.getFilesToCompact().size() > this.maxFilesToCompact) {
// all files included in this compaction, up to max
int pastMax = compactSelection.getFilesToCompact().size()
- this.maxFilesToCompact; compactSelection.getFilesToCompact().subList(0, pastMax)
.clear();
}

如果majorcompaction为true,仅需要在选取进行Compact的StoreFile数目大于maxFilesToCompact(hbase.hstore.compaction.max)时,打印一些信息即可;

如果majorcompaction为false,且在选取进行Compact的StoreFile数目大于maxFilesToCompact(hbase.hstore.compaction.max)时,移除掉后面多余的StoreFile。

return compactSelection;

至此,选举算法结束。

HBase Compact的更多相关文章

  1. Hbase源码之 compact源码(二)

    compact一中介绍了HBASE compact的调度流程,本篇文章主要介绍实际进行compact的过程.先从上文中的chore中接入,在HRegionserver中的compactChecker ...

  2. hbase源码之 compact源码(一)

    hbase compact流程较多,这里分章节介绍,首先介绍compact在regionserver中的调用流程,并不会涉及真正的compact读取合并文件的流程,后续介绍. 在regionserve ...

  3. hbase 学习笔记二----shell

          Hbase 是一个分布式的.面向列的开源数据库,其实现是建立在google 的bigTable 理论之上,并基于hadoop HDFS文件系统.     Hbase不同于一般的关系型数据库 ...

  4. HBase:Shell

    HBase shell commands As told in HBase introduction, HBase provides Extensible jruby-based (JIRB) she ...

  5. HBASE SHELL 命令使用

    HBASE SHELL命令的使用 在hbase shell客户端有许多的操作命令,今天回顾并且总结一二,希望和广大读者共同进步,并且悉心聆听你们的意见.在此的hbase版本是:HBase 1.2.0- ...

  6. HBase详细概述

    原文地址:https://blog.csdn.net/u010270403/article/details/51648462 本文首先简单介绍了HBase,然后重点讲述了HBase的高并发和实时处理数 ...

  7. Hbase记录-Hbase介绍

    Hbase是什么 HBase是一种构建在HDFS之上的分布式.面向列的存储系统,适用于实时读写.随机访问超大规模数据的集群. HBase的特点 大:一个表可以有上亿行,上百万列. 面向列:面向列表(簇 ...

  8. HBase原理和架构

    HBase是什么 HBase在生态体系中的位置 HBase vs HDFS HBase表的特点 HBase是真正的分布式存储,存储级别达到TB级别,而才传统数据库就不是真正的分布式了,传统数据库在底层 ...

  9. HBase操作(Shell与Java API)

    版权声明:本文为博主原创文章,未经博主允许不得转载.     转: http://blog.csdn.net/u013980127/article/details/52443155 下面代码在Hado ...

随机推荐

  1. [PWA] 5. Hijacking one type of request

    Previously we saw how to Hijacking all the reqest, but this is not useful. So now we want to see how ...

  2. Protocol buffer序列化及其在微信蓝牙协议中的应用

    Protocol buffer是Google出品的一种轻便高效的结构化数据存储格式,可对结构化数据进行序列化,并具有语言无关.平台无关等特点,在通信协议和数据存储等领域已经得到广泛的应用.目前其已经提 ...

  3. Linux 内核的文件 Cache 管理机制介绍-ibm

    https://www.ibm.com/developerworks/cn/linux/l-cache/ 1 前言 自从诞生以来,Linux 就被不断完善和普及,目前它已经成为主流通用操作系统之一,使 ...

  4. winserver2008下创建计划任务注意点

    winserver2008下创建任务计划注意点: 1.建立独立用户,可以给其赋予administrator权限 2.起始于(可选):要填写exe文件所在路径 3.设置成“不管用户是否登录都运行”,同时 ...

  5. Fedora14下首次搭建Samba服务器遇到的一些问题

    SMB(Server Messages Block,信息服务块)是一种在局域网上共享文件和打印机的通信协议.而Samba则是在Linux和Unix系统上实现SMB协议的一个免费软件,由服务器及客户端程 ...

  6. POJ 1228 Grandpa's Estate(凸包)

    Grandpa's Estate Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 11289   Accepted: 3117 ...

  7. 使用CSS、JS的几种方法

    引用CSS到html网页里的4种方法 1.行内式 在标记的style属性中设定CSS样式,这种方式本质上没有体现出CSS的优势,因此不推荐使用     <div style="widt ...

  8. poj 2679 Adventurous Driving(SPFA 负环)

    /* - - 这题做了一天.....粗心害死人啊 题目描述恶心 数据更恶心... 先处理一下能走的边 能走的点(到这建边从终点跑一下.) 然后就是SPFA了 注意负环的判断 */ #include&l ...

  9. C#之—委托

    (1)定义委托:(百度百科样例,只有写了才有收获) namespace Entrust { public delegate void GreetingDelegate(string name); // ...

  10. (转)ecshop 后台商品分类添加图片的功能

    转之--http://blog.sina.com.cn/s/blog_4696b3760100n5ee.html   1 .首先找到数据表 ecs_category (商品分类表) 添加一 cat_i ...