精华内容
下载资源
问答
  • Pig源码分析: 逻辑执行计划优化

    千次阅读 2014-04-13 21:52:30
    本文分析的是逻辑执行计划优化的代码结构,具体每种Rule的实现不做分析。

    Whole View

    本文分析的是逻辑执行计划优化的代码结构,具体每种Rule的实现不做分析。

    看本文之前最好参考之前那篇逻辑执行计划模型的文章。



    Architecture

    几个关键类/接口的关系:


    每个关键类/接口的实现和继承结构在下面各节展开。


    Optimizer

    PlanOptimizer是抽象类,主要和Rule、PlanTransformListener、OperatorPlan打交道。

    public abstract class PlanOptimizer {
     
        protected List<Set<Rule>> ruleSets;
        protected OperatorPlan plan;
        protected List<PlanTransformListener> listeners;
        protected int maxIter;

    它接受一个OperatorPlan,即Operators的DAG模型,在optimize()方法里,遍历ruleSet,得到几批Rules,即Set<Rule>。对于每批Rules,调用每个rule.match(plan)来处理传入的OperatorPlan,返回一个匹配成功的List<OperatorPlan> matches,对这些match的plans进行进一步处理。首先获得rule的transformer,然后进行transformer的check()和transform()操作。如果需要Listener操作的,还会遍历listeners,让每个PlanTransformListener监听到transformer进行的transform操作,transformer的reportChanges()方法可以返回他transform操作修改的部分。

    代码如下:

        public void optimize() throws FrontendException {
    
            for (Set<Rule> rs : ruleSets) {
                boolean sawMatch = false;
                int numIterations = 0;
                do {
                    sawMatch = false;
                    for (Rule rule : rs) {
                        List<OperatorPlan> matches = rule.match(plan);
                        if (matches != null) {
                            Transformer transformer = rule.getNewTransformer();
                            for (OperatorPlan m : matches) {
                                try {
                                    if (transformer.check(m)) {
                                        sawMatch = true;
                                        transformer.transform(m);
                                        if (!rule.isSkipListener()) {
                                            for(PlanTransformListener l: listeners) {
                                                l.transformed(plan, transformer.reportChanges());
                                            }
                                        }
                                    }
                                } catch (Exception e) {
                                    StringBuffer message = new StringBuffer("Error processing rule " + rule.name);
                                    if (!rule.isMandatory()) {
                                        message.append(". Try -t " + rule.name);
                                    }
                                    throw new FrontendException(message.toString(), 2000, e);
                                }
                            }
                        }
                    }
                } while(sawMatch && ++numIterations < maxIter);
            }
        }

    实现类:



    LogicalPlanOptimizer

    LogicalPlanOptimizer类是PlanOptimizer的子类

     

    默认加载两个Listener:


    Listener的这两个实现在PlanTransformerListener一节具体展开讲述。

     

    初始化的时候会buildRuleSets(),把需要添加的Rule都生成出来,然后校对该Rule是否被强制加入,或被turn off,从而选择性地放入优化规则。以下列举了所有候选的优化规则,Rule是顺序执行的:



    Transformer

    Transformer是抽象类,有三个方法需要子类实现:

    check()方法,利用pattern来匹配plan里符合的operator集合,返回match的operator集

    transform()方法,具体实施对tree的转换操作

    reportChanges()方法,报告tree的哪部分被transform操作过了(只包括被修改了的或增加了的,不包括删除的node),目的是为了让Listener得知,从而可以修改schema或annotation等等。

     

    继承结构如下:



    PlanTransformerListener

    PlanTransformListener监听一个plan被修改后会触发。

    举例:

    当一个Rule把一次join里的Filter步骤提前到join操作之间做,那么过滤部分的input schema很可能需要改变,此时一个schema listener就会被触发并执行。

     

    PlanTransformListener是一个接口,需要实现一个方法:

    public void transformed(OperatorPlan fp, OperatorPlan tp) throws FrontendException;

    下面具体介绍两个实现类


    ProjectionPatcher

    作用是在映射操作中修补引用信息

    有两个内部静态类



    SchemaPatcher

    使用于逻辑执行计划优化过程,plantransform了之后修补schema信息


    Rule

    public abstract class Rule {
        protected String name = null;
        protected OperatorPlan pattern;
        transient protected OperatorPlan currentPlan;
    private transient Set<Operator> matchedNodes = 
    new HashSet<Operator>();
        private boolean mandatory;
        private boolean skipListener = false;
    

    Rule已经把 match(OperatorPlan plan)方法的逻辑实现好了。

    子类需要实现的是buildPattern()方法,来制定各自的”模式”,即pattern变量。

    子类还需要实现getNewTransformer()方法来实例化一个transformer,transformer的check()和transform()方法会进一步处理rule匹配的operators。

     

    Rulematch()的用途是确保plan的所有子plan都满足该rule的pattern。

    实现逻辑比较繁杂。


    Rule继承结构



    具体每个Rule不分析了。



    全文完 :)






    展开全文
  • Spark SQL 物理执行计划各操作实现

    万次阅读 2014-04-01 18:44:45
    Catalyst作为一个实现无关的查询优化框架,在优化后的逻辑执行计划到真正的物理执行计划这部分只提供了接口,没有提供像Analyzer和Optimizer那样的实现。 本文介绍的是Spark SQL组件各个物理执行计划的操作实现。把...

    SparkStrategy: logical to physical

    Catalyst作为一个实现无关的查询优化框架,在优化后的逻辑执行计划到真正的物理执行计划这部分只提供了接口,没有提供像Analyzer和Optimizer那样的实现。

    本文介绍的是Spark SQL组件各个物理执行计划的操作实现。把优化后的逻辑执行计划映射到物理执行操作类这部分由SparkStrategies类实现,内部基于Catalyst提供的Strategy接口,实现了一些策略,用于分辨logicalPlan子类并替换为合适的SparkPlan子类。


    SparkPlan继承体系如下。接下里会具体介绍其子类的实现。



    SparkPlan

    主要三部分:LeafNode、UnaryNode、BinaryNode

    各自的实现类:



    提供四个需要子类重载的方法

      // TODO: Move to `DistributedPlan`
      /** Specifies how data is partitioned across different nodes in the cluster. */
      def outputPartitioning: Partitioning = UnknownPartitioning(0) // TODO: WRONG WIDTH!
      /** Specifies any partition requirements on the input data for this operator. */
      def requiredChildDistribution: Seq[Distribution] =
        Seq.fill(children.size)(UnspecifiedDistribution)
    
      def execute(): RDD[Row]
      def executeCollect(): Array[Row] = execute().collect()
    

    Distribution和Partitioning类用于表示数据分布情况。有以下几类,可以望文生义。


    LeafNode


    ExistingRdd

    先介绍下Row和GenericRow的概念。

    Row是一行output对应的数据,提供getXXX(i: Int)方法

    trait Row extends Seq[Any] with Serializable

    支持数据类型包括Int, Long, Double, Float, Boolean, Short, Byte, String。支持按序数(ordinal)读取某一个列的值。读取前需要做isNullAt(i: Int)的判断。

    对应的有一个MutableRow类,提供setXXX(i: Int, value: Any)方法。可以修改(set)某序数上的值


    GenericRow是Row的一种方便实现,存的是一个数组

    class GenericRow(protected[catalyst] val values: Array[Any]) extends Row

    所以对应的取值操作和判断是否为空操作会转化为数组上的定位取值操作。

    它也有一个对应的GenericMutableRow类,可以修改(set)值。


    ExistingRdd用于把绑定了case class的rdd的数据,转变为RDD[Row],同时反射提取出case class的属性(output)。转化过程的单例类和伴生对象如下:

    object ExistingRdd {
      def convertToCatalyst(a: Any): Any = a match {
        case s: Seq[Any] => s.map(convertToCatalyst)
        case p: Product => new GenericRow(p.productIterator.map(convertToCatalyst).toArray)
        case other => other
      }
      // 把RDD[A]映射成为RDD[Row],map A中每一行数据
      def productToRowRdd[A <: Product](data: RDD[A]): RDD[Row] = {
        // TODO: Reuse the row, don't use map on the product iterator.  Maybe code gen?
        data.map(r => new GenericRow(r.productIterator.map(convertToCatalyst).toArray): Row)
      }
    
      def fromProductRdd[A <: Product : TypeTag](productRdd: RDD[A]) = {
        ExistingRdd(ScalaReflection.attributesFor[A], productToRowRdd(productRdd))
      }
    }
    
    case class ExistingRdd(output: Seq[Attribute], rdd: RDD[Row]) extends LeafNode {
      def execute() = rdd
    }
    

    UnaryNode


    Aggregate

    隐式转换声明,针对本地分区的RDD,扩充了一些操作

    /* Implicit conversions */
    import org.apache.spark.rdd.PartitionLocalRDDFunctions._
    

    Groups input data by`groupingExpressions` and computes the `aggregateExpressions` for each group.

    @param child theinput data source.

    case class Aggregate(
        partial: Boolean,
        groupingExpressions: Seq[Expression],
        aggregateExpressions: Seq[NamedExpression],
        child: SparkPlan)(@transient sc: SparkContext)
    

    在初始化的时候,partial这个参数用来标志本次Aggregate操作只在本地做,还是要去到符合groupExpression的其他partition上都做。该判断逻辑如下:

    override def requiredChildDistribution =
        if (partial) { // true, 未知的分布
          UnspecifiedDistribution :: Nil
    } else {
      // 如果为空,则分布情况是全部的tuple在一个single partition里
          if (groupingExpressions == Nil) { 
            AllTuples :: Nil
    	  // 否则是集群分布的,分布规则来自groupExpressions
          } else {
            ClusteredDistribution(groupingExpressions) :: Nil
          }
        }
    

    最重要的execute()方法:
    def execute() = attachTree(this, "execute") {
      // 这里进行了一次隐式转换,生成了PartitionLocalRDDFunctions
      val grouped = child.execute().mapPartitions { iter =>
        val buildGrouping = new Projection(groupingExpressions)
        iter.map(row => (buildGrouping(row), row.copy()))
      }.groupByKeyLocally()  // 这里生成的结果是RDD[(K, Seq[V])]
    
      val result = grouped.map { case (group, rows) =>
    // 这一步会把aggregateExpressions对应到具体的spark方法都找出来
    // 具体做法是遍历aggregateExpressions,各自newInstance
        val aggImplementations = createAggregateImplementations()
    
        // Pull out all the functions so we can feed each row into them.
        val aggFunctions = aggImplementations.flatMap(_ collect { case f: AggregateFunction => f })
    
        rows.foreach { row =>
          aggFunctions.foreach(_.update(row))
        }
        buildRow(aggImplementations.map(_.apply(group)))
      }
    
      // TODO: THIS BREAKS PIPELINING, DOUBLE COMPUTES THE ANSWER, AND USES TOO MUCH MEMORY...
      if (groupingExpressions.isEmpty && result.count == 0) {
        // When there is no output to the Aggregate operator, we still output an empty row.
        val aggImplementations = createAggregateImplementations()
        sc.makeRDD(buildRow(aggImplementations.map(_.apply(null))) :: Nil)
      } else {
        result
      }
    }
    

    AggregateExpression继承体系如下,这部分代码在Catalyst expressions包的aggregates.scala里:


    他的第一类实现AggregateFunction,带一个update(input: Row)操作。子类的update操作是实际对Row执行变化。


    DebugNode

    DebugNode是把传进来child SparkPlan调用execute()执行,然后把结果childRdd逐个输出查看

    case class DebugNode(child: SparkPlan) extends UnaryNode

    Exchange

    case class Exchange(newPartitioning: Partitioning, child: SparkPlan) extends UnaryNode

    为某个SparkPlan,实施新的分区策略。
    execute()方法:
    def execute() = attachTree(this , "execute") {
        newPartitioning match {
          case HashPartitioning(expressions, numPartitions) =>
            // 把expression作用到rdd每个partition的每个row上
            val rdd = child.execute().mapPartitions { iter =>
              val hashExpressions = new MutableProjection(expressions)
              val mutablePair = new MutablePair[Row, Row]() // 相当于Tuple2
              iter.map(r => mutablePair.update(hashExpressions(r), r))
            }
            val part = new HashPartitioner(numPartitions)
            // 生成ShuffledRDD
            val shuffled = new ShuffledRDD[Row, Row, MutablePair[Row, Row]](rdd, part)
            shuffled.setSerializer(new SparkSqlSerializer(new SparkConf(false)))
            shuffled.map(_._2) // 输出Tuple2里的第二个值
    
          case RangePartitioning(sortingExpressions, numPartitions) =>
            // TODO: RangePartitioner should take an Ordering.
            implicit val ordering = new RowOrdering(sortingExpressions)
    
            val rdd = child.execute().mapPartitions { iter =>
              val mutablePair = new MutablePair[Row, Null](null, null)
              iter.map(row => mutablePair.update(row, null))
            }
            val part = new RangePartitioner(numPartitions, rdd, ascending = true)
            val shuffled = new ShuffledRDD[Row, Null, MutablePair[Row, Null]](rdd, part)
            shuffled.setSerializer(new SparkSqlSerializer(new SparkConf(false)))
            shuffled.map(_._1)
    
          case SinglePartition =>
            child.execute().coalesce(1, shuffle = true)
    
          case _ => sys.error(s"Exchange not implemented for $newPartitioning")
          // TODO: Handle BroadcastPartitioning.
        }
      }
    

    Filter

    case class Filter(condition: Expression, child: SparkPlan) extends UnaryNode
    
    def execute() = child.execute().mapPartitions { iter =>
      iter.filter(condition.apply(_).asInstanceOf[Boolean])
    }
    

    Generate

    case class Generate(
        generator: Generator,
        join: Boolean,
        outer: Boolean,
        child: SparkPlan)
      extends UnaryNode
    

    首先,Generator是表达式的子类,继承结构如下


    Generator的作用是把input的row处理后输出0个或多个rows,makeOutput()的策略由子类实现。

    Explode类做法是将输入的input array里的每一个value(可能是ArrayType,可能是MapType),变成一个GenericRow(Array(v)),输出就是一个


    回到Generate操作,

    join布尔值用于指定最后输出的结果是否要和输入的原tuple显示做join

    outer布尔值只有在join为true的时候才生效,且outer为true的时候,每个input的row都至少会被作为一次output


    总体上,Generate操作类似FP里的flatMap操作

      def execute() = {
        if (join) {
          child.execute().mapPartitions { iter =>
            val nullValues = Seq.fill(generator.output.size)(Literal(null))
            // Used to produce rows with no matches when outer = true.
            val outerProjection =
              new Projection(child.output ++ nullValues, child.output)
    
            val joinProjection =
              new Projection(child.output ++ generator.output, child.output ++ generator.output)
            val joinedRow = new JoinedRow
    
            iter.flatMap {row =>
              val outputRows = generator(row)
              if (outer && outputRows.isEmpty) {
                outerProjection(row) :: Nil
              } else {
                outputRows.map(or => joinProjection(joinedRow(row, or)))
              }
            }
          }
        } else {
          child.execute().mapPartitions(iter => iter.flatMap(generator))
        }
      }
    

    Project

    case class Project(projectList: Seq[NamedExpression], child: SparkPlan) extends UnaryNode

    project的执行:

      def execute() = child.execute().mapPartitions { iter =>
        @transient val reusableProjection = new MutableProjection(projectList)
        iter.map(reusableProjection)
      }
    

    MutableProjection类是Row => Row的继承类,它构造的时候接收一个Seq[Expression],还允许接收一个inputSchema: Seq[Attribute]。MutableProjection用于根据表达式(和Schema,如果有Schema的话)把Row映射成新的Row,改变内部的column。


    Sample

    case class Sample(fraction: Double, withReplacement: Boolean, seed: Int, child: SparkPlan)  extends UnaryNode
    
    def execute() = child.execute().sample(withReplacement, fraction, seed)
    

    RDD的sample操作:

      def sample(withReplacement: Boolean, fraction: Double, seed: Int): RDD[T] = {
        require(fraction >= 0.0, "Invalid fraction value: " + fraction)
        if (withReplacement) {
          new PartitionwiseSampledRDD[T, T](this, new PoissonSampler[T](fraction), seed)
        } else {
          new PartitionwiseSampledRDD[T, T](this, new BernoulliSampler[T](fraction), seed)
        }
      }
    

    生成的PartitionwiseSampledRDD会在RDD的每个partition都选取样本

    PossionSampler和BernoulliSampler是RandomSampler的两种实现。


    Sort

    case class Sort(
        sortOrder: Seq[SortOrder],
        global: Boolean,
        child: SparkPlan)
      extends UnaryNode
    

    对分布有要求:

    override def requiredChildDistribution =
      if (global) OrderedDistribution(sortOrder) :: Nil 
    else UnspecifiedDistribution :: Nil
    

    SortOrder类是UnaryExpression的实现,定义了tuple排序的策略(递增或递减)。该类只是为child expression们声明了排序策略。之所以继承Expression,是为了能影响到子树。

    case class SortOrder(child: Expression, direction: SortDirection) extends UnaryExpression

    // RowOrdering继承Ordering[Row]
    @transient
      lazy val ordering = new RowOrdering(sortOrder)
    
      def execute() = attachTree(this, "sort") {
        // TODO: Optimize sorting operation?
        child.execute()
          .mapPartitions(iterator => iterator.map(_.copy()).toArray.sorted(ordering).iterator,
            preservesPartitioning = true)
      }
    

    有一次隐式转换过程,.sorted是array自带的一个方法,因为ordering是RowOrdering类,该类继承Ordering[T],是scala.math.Ordering[T]类。


    StopAfter

    case class StopAfter(limit: Int, child: SparkPlan)(@transient sc: SparkContext) extends UnaryNode

    StopAfter实质上是一次limit操作

      override def executeCollect() = child.execute().map(_.copy()).take(limit)
      def execute() = sc.makeRDD(executeCollect(), 1) // 设置并行度为1
    

    makeRDD实质上调用的是new ParallelCollectionRDD[T]的操作,此处的seq为tabke()返回的Array[T],而numSlices为1:

    /** Distribute a local Scala collection to form an RDD. */
      def parallelize[T: ClassTag](seq: Seq[T], numSlices: Int = defaultParallelism): RDD[T] = {
        new ParallelCollectionRDD[T](this, seq, numSlices, Map[Int, Seq[String]]())
      }
    

    TopK

    case class TopK(limit: Int, sortOrder: Seq[SortOrder], child: SparkPlan)
    (@transient sc: SparkContext) extends UnaryNode
    

    可以把TopK理解为类似Sort和StopAfter的结合,

      @transient
      lazy val ordering = new RowOrdering(sortOrder)
    
      override def executeCollect() = child.execute().map(_.copy()).takeOrdered(limit)(ordering)
      def execute() = sc.makeRDD(executeCollect(), 1)
    

    takeOrdered(num)(sorting)实际触发的是RDD的top()()操作
     def top(num: Int)(implicit ord: Ordering[T]): Array[T] = {
        mapPartitions { items =>
          val queue = new BoundedPriorityQueue[T](num)
          queue ++= items
          Iterator.single(queue)
        }.reduce { (queue1, queue2) =>
          queue1 ++= queue2
          queue1
        }.toArray.sorted(ord.reverse)
      }
    

    BoundedPriorityQueue是Spark util包里的一个数据结构,包装了PriorityQueue,他的优化点在于限制了优先队列的大小,比如在添加元素的时候,如果超出size了,就会进行对堆进行比较和替换。适合TopK的场景。

    所以每个partition在排序前,只会产生一个num大小的BPQ(最后只需要选Top num个),合并之后才做真正的排序,最后选出前num个。


    BinaryNode


    BroadcastNestedLoopJoin

    case class BroadcastNestedLoopJoin(
        streamed: SparkPlan, broadcast: SparkPlan, joinType: JoinType, condition: Option[Expression])
        (@transient sc: SparkContext)
      extends BinaryNode
    

    比较复杂的一次join操作,操作如下,
      def execute() = {
        // 先将需要广播的SparkPlan执行后进行一次broadcast操作
        val broadcastedRelation = 
        sc.broadcast(broadcast.execute().map(_.copy()).collect().toIndexedSeq)
    
        val streamedPlusMatches = streamed.execute().mapPartitions { streamedIter =>
          val matchedRows = new mutable.ArrayBuffer[Row]
          val includedBroadcastTuples =  
            new mutable.BitSet(broadcastedRelation.value.size)
          val joinedRow = new JoinedRow
          
          streamedIter.foreach { streamedRow =>
            var i = 0
            var matched = false
    
            while (i < broadcastedRelation.value.size) {
              // TODO: One bitset per partition instead of per row.
              val broadcastedRow = broadcastedRelation.value(i)
              if (boundCondition(joinedRow(streamedRow, broadcastedRow)).asInstanceOf[Boolean]) {
                matchedRows += buildRow(streamedRow ++ broadcastedRow)
                matched = true
                includedBroadcastTuples += i
              }
              i += 1
            }
    
            if (!matched && (joinType == LeftOuter || joinType == FullOuter)) {
              matchedRows += buildRow(streamedRow ++ Array.fill(right.output.size)(null))
            }
          }
          Iterator((matchedRows, includedBroadcastTuples))
        }
    
        val includedBroadcastTuples = streamedPlusMatches.map(_._2)
        val allIncludedBroadcastTuples =
          if (includedBroadcastTuples.count == 0) {
            new scala.collection.mutable.BitSet(broadcastedRelation.value.size)
          } else {
            streamedPlusMatches.map(_._2).reduce(_ ++ _)
          }
    
        val rightOuterMatches: Seq[Row] =
          if (joinType == RightOuter || joinType == FullOuter) {
            broadcastedRelation.value.zipWithIndex.filter {
              case (row, i) => !allIncludedBroadcastTuples.contains(i)
            }.map {
              // TODO: Use projection.
              case (row, _) => buildRow(Vector.fill(left.output.size)(null) ++ row)
            }
          } else {
            Vector()
          }
    
        // TODO: Breaks lineage.
        sc.union(
          streamedPlusMatches.flatMap(_._1), sc.makeRDD(rightOuterMatches))
      }
    

    CartesianProduct

    case class CartesianProduct(left: SparkPlan, right: SparkPlan) extends BinaryNode

    调用的是RDD的笛卡尔积操作,

    def execute() = 
      left.execute().map(_.copy()).cartesian(right.execute().map(_.copy())).map {
        case (l: Row, r: Row) => buildRow(l ++ r)
      }
    

    SparkEquiInnerJoin

    case class SparkEquiInnerJoin(
        leftKeys: Seq[Expression],
        rightKeys: Seq[Expression],
        left: SparkPlan,
        right: SparkPlan) extends BinaryNode
    

    该join操作适用于left和right两部分partition一样大且提供各自keys的情况。

    基本上看代码就可以了,没有什么可以说明的,做local join的时候借助的是PartitionLocalRDDFunctions里的方法。

      def execute() = attachTree(this, "execute") {
        val leftWithKeys = left.execute().mapPartitions { iter =>
          val generateLeftKeys = new Projection(leftKeys, left.output) // 传入了Schema
          iter.map(row => (generateLeftKeys(row), row.copy()))
        }
    
        val rightWithKeys = right.execute().mapPartitions { iter =>
          val generateRightKeys = new Projection(rightKeys, right.output)
          iter.map(row => (generateRightKeys(row), row.copy()))
        }
    
        // Do the join.
        // joinLocally是PartitionLocalRDDFunctions的方法
        val joined = filterNulls(leftWithKeys).joinLocally(filterNulls(rightWithKeys))
        // Drop join keys and merge input tuples.
        joined.map { case (_, (leftTuple, rightTuple)) => buildRow(leftTuple ++ rightTuple) }
      }
    
      /**
       * Filters any rows where the any of the join keys is null, ensuring three-valued
       * logic for the equi-join conditions.
       */
      protected def filterNulls(rdd: RDD[(Row, Row)]) =
        rdd.filter {
          case (key: Seq[_], _) => !key.exists(_ == null)
        }
    

    PartitionLocalRDDFunctions方法如下,该操作并不引入shuffle操作。两个RDD的partition数目需要相等。

      def joinLocally[W](other: RDD[(K, W)]): RDD[(K, (V, W))] = {
        cogroupLocally(other).flatMapValues {
          case (vs, ws) => for (v <- vs.iterator; w <- ws.iterator) yield (v, w)
        }
      }
    

    Other

    Union

    该操作直接继承SparkPlan

    case class Union(children: Seq[SparkPlan])(@transient sc: SparkContext) extends SparkPlan

    用传入的SparkPlan集合各自的RDD执行结果生成一个UnionRDD

     def execute() = sc.union(children.map(_.execute()))




    全文完 :)


    展开全文
  • 如何做一个可执行计划

    千次阅读 2017-04-01 16:46:41
    你知道最严谨的德国人新年计划执行率有多高么?实话告诉你,基本上能坚持自己计划三个月的不超过50%。这个数字在我看来其实一点也不惊讶,放在以前的我身上可能一个月都无法坚持。原因当然有很多,懒、拖延症等等...

      你知道最严谨的德国人新年计划的执行率有多高么?实话告诉你,基本上能坚持自己计划三个月的不超过50%。这个数字在我看来其实一点也不惊讶,放在以前的我身上可能一个月都无法坚持。原因当然有很多,懒、拖延症等等。

      不过更多可能是自己的计划充斥着虚拟的的想法,比如想要赚更多钱,想要自己每天努力学习等等。这些看起来和大部分人的想法都差不多,其实越是这种比较空的目标越是难以实现的,因为它缺乏了衡量是否完成的标准——数值。

      在去年,我重新设置了自己的计划,并用这一年的时间去实践了它,至少目前来看,完成的还算可以,我就以我自身的例子来分享给各位如何制定一个可执行的新年计划。

      一、计划应该是数字化的。

      在我去年的计划中,基本上有一半左右的计划是数字化的,比如读多少本书,减肥多少斤,看多少部电影等等。为什么我们要将自己的计划数字化?因为只有数字才是一个看得到的点,比如当你写减肥的时候应该有一个明确的目标就是要瘦多少斤,这样你才可以在计划中分配这个斤数(数值)比如你要看多少本书,平均下来每月、每周要看多少本。这样的话你心里就会有个估值方便你去完成。只有数字化的计划才是最方便执行的计划。

      以我为例子,我当时计划的是年后要减肥20斤,实际上最后瘦了50多斤吧。当时我期望的是一个月可以瘦5~8斤,这样3~4个月可以完成,实际上由于我基数比较大,所以速度快了些,通过100天瘦了50斤。

      二、大部分计划应该是可以受自己控制的

      新年计划应该是可以受自己控制的计划,因为从根本上讲这个是你自己的诉求,如果是别人要求你做的,可能并不适合放在你的新年计划中(当然有些必须的计划比如好好上班、不逃课这种也大可不写);基本只有出自你自身意愿的才比较容易受控制,因为你发自内心的想要去做这件事的时候,完成起来相对容易。

      三、设定一个能让你提高、让你更好但又是你不愿意做的事。

      当然这个计划应该是能让你变得更好,是对你有用的;抑或是工作或学习中需要你去完成的计划(比如通过四六级考试、考证)。这种目标并非是出自你自己的意愿,而只是学习或者工作中所必须完成的。这时虽然有种挑战自己的意味,但是完成后的喜悦感会更加强烈,且对自己以后做其他事情更加有毅力。毕竟没有谁能靠不愿意就不做这样过一辈子。

      在心理学上有一个词叫舒适区(Comfort zone),意思就是人会在这种状态或模式中感到舒适。舒适区有正面和负面两种影响,正面不说了,负面影响的话就是会让人固步自封、懒惰且安于现状,不会主动地付出太多的努力,无法突破自己。但是当我们“突破舒适区”,主动寻求改变,谋求发展的时候,我们会发现更加广阔的天地。(这也是设定这个“非舒适区”目标的原因。)

      

      四、计划目标需要调剂。

      也可以理解为需要一种制衡,当你把所有计划都列为加薪、升值、考研、过四六级这种目标的时候,即使是一个外人都会觉得看起来有点喘不过气。有野心是好的,保持合适的野心会持续激发一个人的激情,只是过渡的野心容易造成失败,这个时候你就需要在你的计划中增加调剂用以中和这份计划。

      你可以增加一些与你爱好有关的计划目标,比如喜欢旅游,可以列几个目的地或者陌生的城市,喜欢音乐可以试着去学习一个乐器,去培养一两个兴趣爱好可以让你在累得时候找到一个舒缓自己情绪的避风港。

      

      五、你的计划应该是可追踪的。

      应该为自己的目标打上时间戳,合理的运用现在的工具,比如各种计划列表类的软件(evernote、onenote等等),把自己的年度计划细分到月,在每月开始前几天把看下自己下个月应该做些什么,细分下进度,这会让你的计划看起来更加明朗,你会清楚的明白什么时候该做什么。

      然后就是要建立一种追踪机制,你需要一个方法,知道自己的计划实施到了哪一部,那些任务完成了,那些正在进行,那些遇到了阻碍,这些是十分必要的,最常见的方法就是使用上一段中你设定好的计划列表。

      以我减肥为例我会每天记录我自己的体重,比如长时间发现体重没有变化,我会判定为平台期并且去寻找方式方法去突破他。具体可以看下图吧

      

      六、大部分计划应当是行动计划

      比如你想写一手好字,可以改为每周练习书写三次,每次30分钟;你想要锻炼身体,可以写为每周运动三次,每次40分钟(写出具体运动更好),毕竟计划中应当这样才方便去完成,当然你想要一个新手机也可以当做你的新年计划,不过严肃的来讲,这个只是你的愿望清单,并非计划罢了;他可以放进你的计划中,只是愿望清单不要太多才好(物欲哈哈)。

      七、设定一个与家人相关的目标。

      现在这个社会,我们或许真的忙于工作、学习、恋爱,忽视了家人——特别是父母。对于父母来讲,我们做的往往做的不够多,我妈曾经说过我一句话,“你上学的时候一给家里打电话80%是因为该要生活费了”;现在想想还真的是羞愧;工作后因为一些观念上的原因与父母还是时有争吵,所以希望大家可以设定一个与家人有关的计划或目标,可以是带父母去旅游一次或者是去看一次电影,也可以是每周给家里去个电话等等,谢谢。

      好了,基本上我觉得这些应该可以帮助大家写一个好的新年计划了,不过在你要执行之前,在给大家几个建议:

      1、让你的家人或者好友知道你的计划,将这个计划公之于众会方便你有一种被监督感,家人或者朋友既可以对你的计划提出自己的意见建议,也可以对你计划的实施祈祷监督作用。这么做绝对有好处,毕竟最爱面子的中国人是不会让自己下不来台的(笑)。

      2、计划目标不要列的过多或过少,毕竟是年度计划,需要你用一年的时间来完成,但是毕竟有一些目标是在同时进行的,并不像短期目标逐一攻破这么简单;过多的目标会让你压力增大,容易让人心生厌倦且放弃执行。所以一个合适的量是相对重要的,我的建议是,至少每天、每周、每月都要留有一些让自己休息放松的时间。

      3、切记要跟踪自己的计划,每周、月拿出一些时间来回顾下自己计划完成度,如果有问题或者提前完成了,方便及时进行修改。

      4、请告诉自己并做出承诺,“我一定要努力完成目标!”

    展开全文
  •  可还是与真实,具体环境有差别! 其实反正都要线上操作,可以安排业务不忙时段,做实验! 也可以加班,下班时候做线上实验! 另外再说下 像上面问题都是人为问题,而不是技术. 如果你没做实验,遇到问题自然...

    因为做系统切割,把数据库导入到另外一台好机器上. 原本在远程操作实验了几回. 解决了大部分问题.并且撰写了实施步骤. 

    停机时间领导安排了,我负责的是最后一块DB工作.从最后一小时前7分钟开始干活,

    活干得很顺畅,晚上速度比白天快,7分钟导出,3分钟传送,15分钟导入,5分分析模式. 10分钟做分区和移数据. 再建索引!

    一切OK!

    共用了40分钟啊.

    最后突发想 把访问分区表的SQL语句 放进去看看执行计划. 这是以前保留下来的. 放上去一看报某个字段没有!

    原来是最近前几天 开发人员上线,对该表增加了个时间字段. 而脚本是insert into table (........) select ....... from  table_old 指定字段方式,而新增的字段可以为空.所以在插入的时候没有报错!

    他们在闹闹,说该这样,那样做.

    我闭目隔音耳棉一塞!

    决定重做 

    因为 方案不清楚,数据可能丢失,时间剩余30分钟了.

    再建个分区表把新字段放进去,建唯一约束,插入数据. 改表名,删索引,建索引!

    展开全文
  • 如果你的企业实施了专门的PM系统,那么这些工作应该是各个执行人员分头反馈,但是如果用Project,那么就得劳驾项目经理同志去挨个手动填写了,哈哈苦B的项目经理! 计划监控有许多种方式,针对不通过的项目角色,...
  • 通过分析SQL语句的执行计划优化SQL(总结) FROM:http://www.blogjava.net/shanben/archive/2008/07/07/213150.html   做DBA快7年了,中间感悟很多。在DBA的日常工作中,调整个别性能较差的SQL语句时一项富有...
  • 项目实施漫谈-实施计划先行

    千次阅读 2011-11-08 16:55:21
    项目实施漫谈的这些文章本来计划是一个系列的,用来记录自己三年项目实施的一些经验和总结。结果刚写了两篇就出国留学,被岔开了,以至于一年过去了,都没有时间来回顾项目实施的得失与点点滴滴。最近刚刚回国,人人...
  • 通过分析SQL语句的执行计划优化SQL

    千次阅读 2006-04-04 15:05:00
    前言 本文档主要介绍与SQL调整有关的内容,内容涉及多个方面:SQL语句执行的过程、ORACLE优化器,表之间的关联,如何得到SQL执行计划,如何分 析执行计划等内容,从而由浅到深的方式了解SQL优化的过程,使大家逐步...
  • 但是,当这种构想进入具体计划时,再全部委托乐观派就很危险。因为乐观派的动力容易失控、陷入莽撞,或误入歧途。 这时就要委托性格谨慎、深思熟虑、对事物善于观察的人当副手,事先设想到所有的风险,慎重细致地...
  • 执行计划:   第1章 性能调整综述 第2章 有效的应用设计 第3章 SQL语句处理的过程 第4章 ORACLE的优化器 第5章 ORACLE的执行计划  访问路径(方法) -- access path  表之间的连接
  • 如果你的企业实施了专门的PM系统,那么这些工作应该是各个执行人员分头反馈,但是如果用Project,那么就得劳驾项目经理同志去挨个手动填写了,哈哈苦B的项目经理! 计划监控有许多种方式,针对不通过的项目角色,...
  • CRM 实施计划和准备的8个步骤!

    千次阅读 2018-08-17 15:55:40
    若要在CRM中获得成功,您可以做出的最佳投资之一就是拥有一个明确的计划。正如您不会在没有蓝图的情况下建造房屋一样,您不会在没有计划的情况下开始CRM。计划可以帮助您与其他人沟通,按照正确的顺序来工作,确认...
  • 计划做出来不是用来看的,而是要执行计划!跟踪计划执行的难度和工作量比起做计划要高出好多倍。计划跟踪并不是对照进度计划,按时间检查每个人的任务完成情况这么简单,本文将为你分享:1.建立便捷的项目组内沟通...
  • 我们会常说“计划赶不上变化”,我们写出来的计划往往只有一个版本,写...项目计划是保证我们做正确事情的重要手段,我们先来看看项目计划应该有什么内容,下一篇介绍如何写出这个计划,再下一篇是如何执行和跟踪计划
  • 计划不是摆设,上篇我们说了计划应该有什么内容,本篇将会分享如何写出实用的计划。...4.制定可执行可检查的进度计划;5.细化近期计划,定下远期计划大节点;6.让项目组各成员详细计划自己的工作;7.持续更新计划
  • 有个朋友决心要减肥,每天吃很少,晚上甚至断食,坚持了两个星期就见效果了,瘦了将近10斤,我夸他有决心有执行力,可又过了一周,再见到他,又反弹回来了。 我们经常会希望自己有所改变,能往更好的方向发展,...
  • 在大项目中,实施顾问主要负责什么具体工作?现在分工细了,大项目都是由多专业人组成团队进行工作:1、咨询顾问:流程梳理、诊断、优化、理念\需求引导输送获得老板和管理层认可2、项目经理:计划、分配组织、协调...
  • IT项目管理是项目管理,包括9大知识领域(项目综合、范围、时间、成本、质量、人力资源、沟通、风险和采购管理)以及启动、计划实施、控制和收尾等过程组成。
  • 不过现在来看还是有启发意义的,虽然笔法有些稚嫩实施分为这几个阶段:1字典准备,系统参数配置2客户化3使用培训4做报表做运行监控5升级更新版本这几部分都挺费时间。为什么?1字典准备,系统参数配置没有字典准备和...
  • 应急计划中同时规定风险触发因素,即出现何种征兆时执行应急计划。 弹回计划 备用应对计划,在主应对计划不起作用的情况下使用。 权变措施 针对已发生的风险而紧急采取的、原来未计划的应对措施 权变措施针对...
  • PMP项目管理13个计划

    千次阅读 2019-06-14 09:57:50
    1、变更管理计划 所属过程:制定项目管理计划 含义:定义管理项目变更的过程,用来明确如何对变更进行监控。为管理变更控制过程提供指导,记录变更控制委员会的情况。 内容:当项目需要变更的时候,如何进行变更。 2...
  • 因为计划,我们可以更好的执行和落地。我现在养成了很多的个人工作计划习惯,每天晚上会提前计划下明天的工作,每周末会总结及计划下下周的工作,每个月度和年度都会总结及计划下下一月度及年度的工作。我觉得正是...
  • 如何提高ERP项目实施执行

    千次阅读 2005-08-11 09:51:00
    如何提高ERP项目实施执行力:http://blog.itpub.net/post/2130/35772如何提高ERP项目实施执行力发表人:hpj168 | 发表时间: 2005年七月21日, 21:45据不完全统计,我国目前已有近千家企业购买了MRP-II/ERP软件,...
  • 实施CMMI具体要做什么——点评

    千次阅读 2010-04-26 13:23:00
    实施CMMI具体要做什么——点评在MSN项目管理群里,一位很资深的IT工作人员推荐她的博客,我粗看了一下确实不错,准备每篇阅读一下,当然也希望通过点评一下来提高一下自己。该文章是实施CMM/CMMI中具体要做什么?...
  • 今天小弟就跟大家聊一聊如何管理智能座舱测试系统以及管理过程中具体实现的细节。 我个人大致给智能座舱测试系统分为四个部分: 前期:人员的招聘以及相关计划的排版和环境的搭建 中期:测试进度的掌控以及供应商的...
  • Oracle 执行语句历史查询—测试和实施人员必备技能 author:润明 2012-2-7 QQ:226399587  http://blog.csdn.net/runming918  今晚真的有点无聊,工作上好几天都没啥事情了,但还是不得不每天在办公室‘装忙’耗...
  • 每个项目都应该有测试部门,测试就应该有测试计划,但应该先知道测试计划内容都应该有哪些,该怎么写?(本人初步认为内容应该有以下几点,下面的测试计划是一个简单的例子) 1. 概述 1.1 编写目的 1.2 项目背景 ...
  • 权变措施 弹回计划 应急计划的区别

    千次阅读 2018-11-20 16:10:21
    考完PMP认证已经快半年了,好久没有看PMBOK了,好多知识点都忘记了,今天网上看到了权变措施、弹回计划、应急计划区别的文章,自己重新学习了下,在这里也和大家进行下分享。 什么是权变措施 权变措施是未经计划的...
  • 挨踢项目求生法则(8)——计划

    千次阅读 2014-01-22 15:27:54
    计划赶不上变化,计划还要不要写呢?项目工期限死,估算有什么价值呢?只有项目经理紧张项目,其他人是打工心态,怎样办呢?PMP的知识能搭救项目吗?如何才能做出一个按期交付的完美计划呢?所有问题,将在这一篇中...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 148,357
精华内容 59,342
关键字:

具体执行计划实施计划