线上回滚日记 TEST

不断书写才可以成长


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

C++11 新标准的内容

发表于 2018-10-29 | 阅读次数:
字数统计: | 阅读时长 ≈

c++11的新标准

  1. 新类型

    • 新增了long long 和 unsigned long long 两个类型(64位的整型)

    • char32_t and char16_t

    • 原始字符串
  2. 统一的初始化

    1. 扩大了大括号初始化的使用范围,可添加等号也可以不添加,
    2. 列表初始化的时候也可以用在new表达式中
    3. 大括号括起来来调用类的构造函
    1
    2
    3
    Stump s1(3, 15.6); //old style
    Stump s2{5, 43,4}; // C++11
    Stump s3 = {4, 32.1}; //C++11

    好处:

    • 缩窄:防止将数值赋给无法存储它的数值变量,但允许准换为更宽的类型
    • Std::initializer_list 有用该类型修饰构造函数的参数的时候,初始化列表只能用该构造函数。并且这个修饰也可以修饰普通函数的参数,用于接受用大括号修饰的参数列表,并保持内容类型的一致性
  3. 声明

    c++11 提供了多种简化声明的方式

    1. auto

    2. decltype 将变量的类型声明为表达式的类型

      1
      2
      3
      4
      // 构造一个比较的匿名函数
      auto cmp = [](int left, int right) { return (left ^ 1) < (right ^ 1);};
      // 声明 优先级队列的时候, 简化具体化模版类的类型的输入
      std::priority_queue<int, std::vector<int>, decltype(cmp)> q3(cmp);
    3. 返回类型后置

      1
      2
      double f1(double, int); // traditional syntax
      auto f2(double, int) -> double; // new syntax, return type is double

      帮助模板函数指定返回类型,帮助编译器在遇到参数列表后,再去指定根据参数类型指定返回类型。

    4. 模板别名:using =

      对于冗长的标识符,方便的创造别名。

      以前:typedef

      1
      typedef std::vector<std::string>::iterator itType;

      C++11提供的方式:using =

      1
      using itType = std::vector<std::string>::iterator;

      差别在于,新语法可以用于模板的具体化

      1
      2
      template<typename T>
      using arr12 = std::array<T, 12>;
    5. nullptr

      c++11 新增了关键字nullptr,用于表示空指针,这样可以避免以前0即表示空指针,又表示整数常量的问题。

  4. 智能指针

    自动的完成delete的工作。首先引入了auto_ptr,以帮助自动化的完成这些工作,发展过程中,需要一些更加精致的机制,新增了三个智能指针 unique_ptr 、shared_ptr和weak_ptr。当指针过期的时候,期西沟函数将调用delete函数的运行来释放内存,防止内存泄漏。

    1. 使用智能指针

      • 导入memory都文件 #include <memory>
      • 使用智能指针的模板具体化实例指向新生成的对象 unique_ptr<string> ps = new string("test");
      • 不需要delete去删除生成的内存
      • 对于非堆内存是禁止使用智能指针去删除对应的内存的string vacation("what a happy day!!!")
    2. 为什么有三种智能指针

      当相同类型的智能指针相互赋值的时候,一块内存就会被删除多次,所以这样有很多种策略

      • 执行深复制;
      • 建立所有权(ownership)的概念,只有一个指针可以拥有它,这样只有拥有对象的智能指针的构造函数会删除该对象。然赋值操作去转让所有权。转让所有权之后该智能指针就不指向该内存了,就为空了,之后访问就会出现问题,auto_ptr就没有unique_ptr那么的严格;
      • 跟踪和引用特定对象的智能指针数,这里是引用计数,当最后一个指针过期时,才调用delete。
    3. 为什么unique_ptr要优于auto_ptr?

      • 当不合格使用unique_ptr的赋值的时候,会报错,而auto_ptr不会,运行时会发生错误
      • 作为临时右值赋给函数返回的临时变量时,unique_ptr不会报错
      1
      2
      3
      4
      5
      6
      using namespace std;
      unique_ptr<string> pu1(new string " Hi bo!");
      unique_ptr< string> pu2;
      pu2 = pu1; // not allowed;
      unique_ptr<string> pu3;
      pu3 = unique_ptr<string>(new string "Yo!"); // allowed
      • unique_ptr支持使用std::move(),安全的执行智能指针的赋值操作
    4. 如何使用智能指针?

      • 对个指向同一对象的指针 使用shared_ptr
      • 不需要指向同一个对象的指针 使用unique_ptr
      • new分配内存时,才能使用 auto_ptr 和 shared_ptr。 new [] 和 new 分配内存的时候都可以使用 unique_ptr。 当不再内存分配内存时,不能使用智能指针
  5. c++11 抛弃了异常规范的指定,并且添加了关键字noexcept,指出不会引发异常的关键

  6. 作用域内的枚举

    • 不同的实现采用不同的底层类型,之前的底层类型都是整形类型。

    • 枚举名的作用域为枚举定义所属的作用域,所以相同的作用域内的两个枚举,枚举变量名不能是相同的枚举名。新的枚举使用class或struct定义

  7. 对类的修改

    1. 显示转换运算符

      原因:自动类型转换可能引起各种问题,c++引入老人关键词explicit,以静止单参数构造函数导致的自动类型转换

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      class Plebe{
      Plebe(int); //automatic int-to-plebe conversion
      explicit Plebe(double); // required explicit use
      ...
      };

      Plebe a, b;
      a = 5; //implicit conversion, call Plebe(5)
      b = 0.5; // not allowed
      b = Plebe(0.5); // allow explicit conversion

      c++11拓展了explicit的用法,使得可对转换函数做类似的处理

    2. 类内成员的初始化

      在类内定义中初始化成员

      形式:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      class Session{
      int mem1 = 10; // 类定义初始化
      double mem2 {1966.54}; // 大括号形式的类定义初始化
      short mem3;
      public:
      Session(){}
      Session(short s) : mem3(s) {}
      Session(int n, double d, short s) : mem1(n), mem2(d), mem3(s) {}
      }

      好处:使用该初始化方式可以有效的减少代码量

  8. 模板和STL方面的修改

    1. 基于范围的for的循环

    2. 新的STL的容器

      • forward_list 单向链表
      • unordered_map,unordered_set 以及对应的multimap 或者multiset
      • 新增的模板array
    3. valarray升级

    4. 尖括号

      需要和运算符>>区分开,所以声明嵌套的模板时,需要使用空格将尖括号分开

  9. 右值引用

    • 传统的c++引用,现在被称为左值引用,使得标识符关联到左值。(表示数据的表达式,变量名 或解除引用的指针),程序可获取其地址。

    • C++11新增了右值引用,这是使用&&表示的。右值引用关联到右值,即可以出现在赋值表达式右边,但不能对其应用地址运算符的值。右值包括字面常量(c风格字符串除外,它表示地址)、诸如x+y等表达式以及返回值得函数(条件是该函数返回的不是引用):

      1
      2
      3
      4
      5
      int x = 10;
      int y = 23;
      int && r1 = 13;
      int && r2 = x + y;
      double && r3 = std::sqrt(2.0);
    • 引用右值引用的主要目的就是用于移动语义

  10. 移动语义和右值引用

    1. 为何需要移动语义?

      移动语义避免了移动原始数据,而只是修改了记录,由于右值引用是临时变量,右值引用才可以引用该临时能量。该临时能量的值可以直接被窃取而返回,所以减少移动原始数据。

      1
      2
      3
      Useless two = one; //calls copy constructor

      Useless four(one + three); //临时变量作为输入,由于声明了移动构造函数,所以可以直接调用移动语义 calls move constructor
    2. 在之前没有改移动语义的出现,是编译器的智能编译实现的优化

    3. static_cast<>可将对象的类型强制转换为右值,转换为右值后可以使用移动构造函数和移动赋值运算符
    4. 使用移动构造函数和普通的复制构造函数的主要区别是,输入变量是左值还是右值,左值还可以被调用,所以还有用处,而右值一般是临时的,不在被调用的,存储在一个临时的特殊位置,所以一般不会被再次调用,所以可以将其直接转移,减少复制。而static_cast<>可以将左值转换为右值。
    5. C++11 提供了更简单的std::move()的方式,在头文件utility中。
  11. 新的类功能

    1. C++11新增了移动构造函数和移动赋值运算符
    2. 可以使用默认的成员函数: default 或者禁用默认的成员方法: delete

    3. 管理虚方法的标识符: override 和 final

      • override

        1
        2
        3
        virtual void f(char* ch) const override { 
        std::cout << val() << ch << "!\n";
        }

        使用虚说明符override指出您要覆盖一个虚函数:如果声明出现问题,不能覆盖基类的虚函数的话就会产生报错

      • final

        禁止派生类覆盖特定的虚方法,就会使用final虚函数说明符,放在虚函数的末尾

  12. Lambda函数

    1. 比较函数指针、函数符和Lambda函数

      首先将这三者都可以统称为函数对象。举例说明,完成一个函数对象,该函数对像需要返回该整数是否被可以被指定的整数整除:

      1. 函数名:

        1
        2
        bool f3(int x) {return x % 3 == 0;}
        int count3 = count_if(numbers.begin(), numbers.end(), f3); //指定函数对象为函数名
      2. 函数符:

        函数符是一个类对象,重载operator()()方法来实现一些功能。

        好处:可以使用同一个函数符来完成两项的计数的任务

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        class f_mod {
        private:
        int dv;
        public:
        f_mod(int d = 1): dv(d) {}
        bool operator()(int x) {return x % dv == 0};
        }

        f_mod obj(3);

        bool is_div_by_3 = obj(7); // 运行重载之后的operator() () 函数

        int count3 = std::count_if(number.begin(), number.end(), f_mod(3));
      3. Lambda函数

        c++11中对于接受函数对象的函数,可以使用匿名函数定义其参数。

        1
        int count3 = std::count_if(numbers.begin(), numbers.end(), [](int x){return x % 3 == 0;})

        区别:

        • 使用[]替代了函数名;

        • 没有声明返回的类型, (相当于隐性的使用了decltyp得到的类型)

        • 仅当lambda表达式完全又一条语句构成的时候,才可以不指定返回类型,比如上面的情况;否则需要使用新增的返回类型后置的语法

          1
          [](double x)->double{int y = x; return y - x;} // 返回类型后置
        1. 为什么要使用Lambda
          • 距离: 让定义位于使用的地方
          • 简洁: 函数符代码比函数名和lambda代码更加繁琐
          • 效率: 函数地址的方法以为着是非内联的函数,而函数类型和lambda函数不会阻止内联
          • 功能:lambda函数可以访问作用域内的任何动态变量,要捕获要使用的变量,将其名称放入中括号内。
            • 如果只指定了函数名, 如[z],将按值访问变量;
            • 名称前加上&,如[&count], 将按引用的访问count;
            • [&],按引用访问所有的变量
            • [=], 按值访问所有的动态变量
            • 也支持混合使用
  13. 包装器 — 引申出适配器模式

    c++提供对个包装器。这些对象用于给其他编程接口提供更一致或更合适的接口。C++提供不同的包装器模板,bind、mem_fn和reference_wrap以及包装器function。

    1. 包装器functiong和模板的低效性

      • function模板

        1
        std::function<double(char, int)> fdci;

        从调用特征表的角度定义了一个对象,可用于包装调用特征标相同的函数指针、函数对象和lambda表达式。

        1
        2
        function<double(double)> ef1 = dub;
        function<double(double)> ef2 = square;

        可以将function<double(double)>作为函数的形参,让形参f的类型与原始实参相匹配

  14. c++11中的四种类型转换

    1. static_cast<>

      定义: Converts between types using a combination of implicit and user-defined conversions.

      完成编译器允许的隐式类型转换

      基本使用范围:

      1. 基本数据类型之间的转换
      2. 派生类体系的向上转型

      example:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      #include <vector>
      #include <iostream>

      struct B {
      int m = 0;
      void hello() const {
      std::cout << "Hello world, this is B!\n";
      }
      };
      struct D : B {
      void hello() const {
      std::cout << "Hello world, this is D!\n";
      }
      };

      enum class E { ONE = 1, TWO, THREE };
      enum EU { ONE = 1, TWO, THREE };

      int main()
      {
      // 1: initializing conversion
      int n = static_cast<int>(3.14);
      std::cout << "n = " << n << '\n';
      std::vector<int> v = static_cast<std::vector<int>>(10);
      std::cout << "v.size() = " << v.size() << '\n';

      // 2: static downcast
      D d;
      B& br = d; // upcast via implicit conversion
      br.hello();
      D& another_d = static_cast<D&>(br); // downcast
      another_d.hello();

      // 3: lvalue to xvalue
      std::vector<int> v2 = static_cast<std::vector<int>&&>(v);
      std::cout << "after move, v.size() = " << v.size() << '\n';

      // 4: discarded-value expression
      static_cast<void>(v2.size());

      // 5. inverse of implicit conversion
      void* nv = &n;
      int* ni = static_cast<int*>(nv);
      std::cout << "*ni = " << *ni << '\n';

      // 6. array-to-pointer followed by upcast
      D a[10];
      B* dp = static_cast<B*>(a);

      // 7. scoped enum to int or float
      E e = E::ONE;
      int one = static_cast<int>(e);
      std::cout << one << '\n';

      // 8. int to enum, enum to another enum
      E e2 = static_cast<E>(one);
      EU eu = static_cast<EU>(e2);

      // 9. pointer to member upcast
      int D::*pm = &D::m;
      std::cout << br.*static_cast<int B::*>(pm) << '\n';

      // 10. void* to any type
      void* voidp = &e;
      std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
      }
    2. dynamic_cast

      定义:Safely converts pointers and references to classes up, down, and sideways along the inheritance hierarchy.

      用法:dynamic_cast <new_type> (expression)

      If the cast is successful, dynamic_cast returns a value of type new_type.

      If the cast fails and new_type is a reference type,it throws an exception that matches a handler of type std::bad_cast.

    3. const_cast

      定义:对指针或引用去除或者添加const属性

      example:

      1
      2
      3
      4
      5
      6
      7
      const int a= 0;

      int b = const_cast<int>(a);//不对的

      const int *pi = &a;

      int * pii = const_cast<int *>pi;//去除指针中的常量性,也可以添加指针的常量性;
    4. reinterpret_cast

      定义: 无视类型的引用之间的转换

​

Go装饰器模式教程

发表于 2018-10-22 | 阅读次数:
字数统计: | 阅读时长 ≈

对于某类确定的问题,使用装饰器模式是一个完美的解决方案。

理解装饰器模式

Decorators essentially allow you to wrap existing functionality and append or prepend your own custom functionality on top.

装饰器允许你包裹现存的函数功能并且在外层追加自己的函数。

在Go语言中,function被当作基类对象,这就意味着你可以将函数当作变量进行传递。一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
"time"
)

func myFunc() {
fmt.Println("Hello World")
time.Sleep(1*time.Second)
}

func main(){
fmt.Printf("Type: %T\n", myFunc)
}

这表明函数可以被传递,作为调用的实参,接下来扩展一下我们的函数并且添加一个coolFunc()函数,这个函数令一个函数作为它的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
"time"
)

func myFunc() {
fmt.Println("Hello World")
time.Sleep(1*time.Second)
}


func coolFunc(a func()){
a()
}

func main(){
fmt.Printf("Type: %T\n", myFunc)

coolFunc(myFunc)
}

函数作为参数传递给coolFunc函数运行a()

这里有必要添加一个抽象层去包裹myFunc

一个简单的装饰器实例

使用一个简单的实例来给代码实例添加值。添加一些额外的log来观察一下部分函数的开始时间和结束时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
"time"
)

func myFunc() {
fmt.Println("Hello World")
time.Sleep(1 * time.Second)
}

func coolFunc(a func()) {
fmt.Printf("Starting function execution: %s\n", time.Now())
a()
fmt.Printf("End of function execution: %s\n", time.Now())
}

func main() {
fmt.Printf("Type: %T\n", myFunc)
coolFunc(myFunc)
}

现实的例子

这里实现一个简单的http web server 并且装饰我们的节点。这样我们可以验证是否来的请求有特定的header set。

这里我们可以添加一个简单的认证装饰器函数,这个函数可以检查过来的请求头部是否被验证过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main

import (
"fmt"
"log"
"net/http"
)

func isAuthorized(endpoint func(http.ResponseWriter, *http.Request)) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
fmt.Println("Checking to see if Authorized header set...")

if val, ok := r.Header["Authoried"]; ok {
fmt.Println(val)
if val[0] == "true"{
fmt.Println("Header is set! We can serve content!")
endpoint(w, r);
}
}else {
fmt.Println("Not Authorized!!")
fmt.Fprintf(w, "Not Authorized!!")
}
})
}

func homePage(w http.ResponseWriter, r *http.Request) {
fmt.Println("Endpoint Hit: homePage")
fmt.Fprintf(w, "Welcome to the HomePage!")
}

func handleRequests() {
http.HandleFunc("/", isAuthorized(homePage))
// if we want to decotator more function
http.HandleFunc("/new", isAuthrized(newEndPoint))

log.Fatal(http.ListenAndServe(":8081", nil))
}

func main() {
handleRequests()
}

在正式的进入到http的处理文件之前,对处理函数进行装饰,使主处理函数在装饰函数的合理位置运行。

通俗的理解,给处理函数进行了必要的装饰。从代码复用的角度来说,装饰器,将一些可以抽象出来的必要的组件抽象出来,并将所要装饰的函数作为参数传递进去,起到装饰器添加的装饰的作用后,再运行传入的函数

常见面试问题—细节篇

发表于 2018-10-19 | 阅读次数:
字数统计: | 阅读时长 ≈
  1. 什么是RESTful架构:

  (1)每一个URI代表一种资源;

  (2)客户端和服务器之间,传递这种资源的某种表现层;

  (3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现”表现层状态转化”。

  1. 状态转化(State Transfer)

    1. GET用来获取资源
    2. POST用来新建资源(也可以用于更新资源)
    3. PUT用来更新资源
    4. DELETE用来删除资源
  2. 适配器模式

    将一个类的接口,转换成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间

    针对接口编程的具体实现,例如:将类的接口对外的接口

  3. Go的接口是什么

    1. 接口提供了一种方式来说明对象的行为

    2. 接口定义了一组方法,但是这些方法不包含(实现代码):它们没有被实现。接口内也不能包含变量。

      1
      2
      3
      4
      5
      type Namer interface {
      Method1(param_list) return_type
      Method2(param_lsit) return_type
      ...
      }

      接口的名字由方法名加[e]r后缀组成

    3. 类型不需要显示声明它实现了某个接口:接口被隐式的实现。多个类型可以实现同一个接口。

    4. 实现某个接口的类型可以有其他的方法
    5. 一个类型可以实现多个接口
    6. 接口类型可以包含一个实例的引用,改实例的类型实现了此接口(接口是动态类型)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    package main

    import "fmt"

    type Shaper interface {
    Area() float32
    }

    type Square struct {
    side float32
    }

    type (sq *Square) Area() float32 {
    return sq.side * sq.side
    }

    func main(){
    sq1 := new(Square)
    sql.side = 5

    var areaIntf Shaper
    areaIntf = sq1

    // shorter, without separate declaration
    // areaIntf := Shaper(sq1)
    // or even:
    // areaIntf := sq1
    fmt.Printf("The square has area: %f\n", areaIntf.Area())
    }
    1. 接口变量中包含了接受者实例的值和指向对应方法表的指针
    2. 多态的go版本, interface相当于一个函数能力的表格,实现了表格里的函数的实例可以被赋值给改接口的实例,接口实例运行时,就按照表格中记录的接口实现的方法的地址去调用具体的实现。这个和多态的理解是一样的:同一个类型在不同的实例上似乎表现不同的行为
    3. 也可以以一种稍微不同的方式来使用接口这个词:从某个类型的角度来看,它的接口指的是:它的所有的导出方法,只不过没有显示地为这些导出方法额外定一个接口而已
  4. 死锁的必要条件

    1. 互斥
    2. 占有且等待
    3. 不可抢占
    4. 循环等待
  5. 避免死锁的方法

    1. 破坏占有且等待条件 (一次申请所有资源,不然阻塞)
    2. 破坏不可抢占条件 (不能申请资源被阻塞时,自己的资源需要被释放,被抢占)
    3. 破坏循环等待条件 (必须按一定的顺序申请资源)

HBase的部署架构和基础架构

发表于 2018-10-19 | 阅读次数:
字数统计: | 阅读时长 ≈

HBase的来源

海量数据与NoSQL

  1. 关系型数据库的极限

    • 并发的进行插入、编辑以及删除的操作,达到几十或者几百的并发时,单条数据的查询延时会达到分钟的级别
    • 深度优化后的性能依然是高并发系统的主要瓶颈
  2. CAP理论

    1. 原子性
    2. 一致性
    3. 分区容错性

    CAP原理指出三者只能兼顾2点

  3. NoSQL

    1. 放弃某些特性带来了非关系型数据库,对事务性的要求不严格
      • 有些只保证最终一致性
      • 有些数据库在部分宕机的情况下依然运行(同一份数据放到好几个地方)
      • Not Only SQL

HBase怎么来的

  1. Google的BigTable项目,作为Hadoop的一部分;
  2. 分布式的文件系统(HDFS)
  3. HBase采用Key/Value的存储,数据量增大很多,不会影响查询的效率
  4. 列式数据库,把不同的字段部分放到不同的机器上
  5. 带来的问题是,只是存储少量的数据也不会很快
  6. 当单表的数据量很大,并发很高,数据分析的需求很弱可以使用HBase

基本概念

  1. 部署架构

    扩展之后的表结构

    1. 基本结构
      • MasterServer:负责维护表结构信息;
      • RegionServer:实际数据存储
      • 所以当MasterServer挂了之后依然可以查询数据,但是不能新增表了;
    2. 数据存储
      • 数据存储在HDFS上
      • ZooKeeper相当于管家的角色,管理所有的RegionServer的信息,包括具体的数据段存放在哪个RegionServer上
    3. 微观上的细节
      • Region是什么?
        1. 是一段数据的集合
        2. 不能跨服务器,一个RegionServer上有一个或者多个Region
        3. 数据量小的时候一个Region可以包含所有的数据,数据量大的时候会分Region
        4. Region是基于HDFS的,所有的数据的存取操作都调用了HDFS的客户端接口
      • RegionServer是什么?
        1. 是存放Region的容器,直观上是服务器上的一个服务
        2. ZooKeeper获取了RegionServer的地址后,会直接从这里获取数据
      • Master是什么?
        1. Master只负责各种打杂的工作,建表和删表、移动Region,合并等操作。共同特性是需要跨RegionServer。
  2. 存储架构

    存储结构

    1. 最基本的存储单位是列,一个列或者多个列形成一行。HBase的列数据可以不完全的对齐,不同的列也可以存储在不同的机器上。
    2. 每行拥有唯一的Row key来标定这个行的唯一性,每个列有多个版本,多版本的值存储在单元格中。
    3. 若干列可以被归类为一个列族
    4. 行键是什么?
      • rowkey完全由用户指定的唯一的字符串,永远根据rowkey来排序,根据字典排序
      • 单元格是数据存储的最小单元,同一列的不同版本会存储在不同的单元格中
    5. 列族是什么?
      • 由若干列的数据可以组成列族
      • 一个表有几个列族是一开始就指定好的,表的很多属性都是指定在列族上,例如过期时间、数据块缓存以及是否压缩
      • 列的名称前面总带着它所属的列族
      • 相同的列族的会被尽可能的放在一台机器上
      • 设置列族的个数,官方建议越少越好,数据放在一台物理机上依然会加速数据的查询过程,列族太多会影响数据库的性能,而且定的太多也容易出现bug。
    6. 单元格是什么?

      • 多个版本之间存储在不同的单元格,使用版号来进行区分
      • 唯一的数据单元表达式 行键:列族:列:版本号,默认是最新的版本
    7. Region跟行的关系

      • 一个Region就是多个行的集合,Region按照Rowkey字典排序
    8. 与传统的关系型数据库的对比
      • 由于每一行的数据都是离散的
      • 优势可以随时的水平扩展,行的概念抽象起来,通过Rowkey概念链接起来
      • HBase的存储语句都被行列精准的定义出来,相当于每行数据的插入都需要的不停的插入十次。

未命名

发表于 2018-10-18 | 阅读次数:
字数统计: | 阅读时长 ≈
final_report.md

Zhe Work Report

2018.02.28

The report includes :

  1. model comparison tool.
  2. Non-blocking multi node communication in chainer master v3.
  3. Source code in developer01.
  4. Label Device

Model Comparison Tool

  Model comparisontool is a tool to help developer to compare difference between two model from different framework. The tool support Caffe, TF, MXNet, Chainer,pytorch. We mainly compare the variable node shape and functionnode type. We also compare the computation the graph and padding strategy.

  We also develop a web model comparison tool for user. User just need upload their models file and see the visually graph comparison result. Below Section 1 is the design detail of the tool and Section 2 is the Web version Detail, final is the Deploy part.

  1. Design Detail

    • The whole tool based on the architecture as below.

    archtecture

    • In our tool, firstlly loading the graph from different framework. You can find the method to load function responding to different framework as the Table below.
    FrameworkInput FilesFunction fileDetail
    Caffetrain_val.prototxtload_caffe_model.pyBased on the caffe.proto.caffe_pb2 to read model parameters.
    Tensorflowmodel save filesload_tf_model.pyRead computation graph from stored model file. So User should save the graph before.
    Chainermodel class .py filesload_chainer_model.pyGet all functionNode from output.
    MXNet model json fileload_mxnet_model.pyRead json file and parameters.
    SSDSSD modelload_ssd_*.pySSD have special layer.
    • Convert to unified data format and add different pdding strategies, as the Figure below. Later version, we add pw, ph, sh, sw, kh, kw params. source code in rank_multi_port.py.

    representation

    • Graph comparison is a difficult problem. So we transfer graph to an ordered list and find all difference. Source code in rank_multi_port.py.
  2. Web Model Comparison Tool

    For a better User Experience, we develop a website. User just upload the input files and get the visual comparison result and detail parameters. The system combine Django backend and Bootstrap front end. Using D3.js to visualize the model graph. All source code in the web-model-comparison-tool folder.

    comparison

    The website have two pages. In page 1, user upload their needing. Page 2 show the Graph result.

    upload

    result

 

  1. Deploy Method

    Our website verison deploy in developer01 server. The root username modeltools, password: abc110.

    1. ssh to the web server.

       
      xxxxxxxxxx
      ssh modeltools@developer01
    2. cd to work folder.

       
      xxxxxxxxxx
      cd ~/web-model-comparison-tool/model_comparison_tool/
    3. Start the uwsgi server.

       
      xxxxxxxxxx
      uwsgi —socket /tmp2/model_comparison_tool.sock —module model_comparison_tool.wsgi —chmod-socket=777 —uid=www-data —gid=www-data
    4. Restart the nginx service.

       
      xxxxxxxxxx
      sudo /etc/init.d/nginx restart
    5. Now you can browser the site: server local IP:8000 to use our tool.

    If wanna know more, please click on HELP.

     

Non-blocking Multi Node In Chainer Master V3

  In Chainermn master, multi use the blocking to implement multi node trainning. In Chainer, we use data parallelism as multi node practices. The weight update like the figure below.

	data_paralism

Blocking communication, every iteration do allreduce together. But Non-blocking do weight all reduce every layer. So we can hide communication with omputation. Just like below.

Non-blocking

Our code based on the intel chainer master_v3 branch. All the diff in the patch files.

Source code in developer01

  I stored backup files in developer01 server. Model comparison tool is in ~/MCT folder. Non-blocking-chainer is in ./NBC folder. Our tool git also in dl_framework-dl_tools repo.

Device

A Desktop in mine. NUM is: BB26468.

Mysql的基础概念

发表于 2018-10-18 | 阅读次数:
字数统计: | 阅读时长 ≈

存储引擎

  1. 关于count(*)

    知识点:MyISAM会直接存储总行数,InnoDB则不会,需要按行扫描。

    实践:数据量大的表,InnoDB不要轻易select count(*),性能消耗极大。

    常见坑:只有查询全表的总行数,MyISAM才会直接返回结果,当加了where条件后,两种存储引擎的处理方式类似。

    例如:
    t_user(uid, uname, age, sex);

    • uid PK
    • age index

    select count(*) where age<18 and sex=’F’;
    查询未成年少女个数,两种存储引擎的处理方式类似,都需要进行索引扫描。

    启示:不管哪种存储引擎,都要建立好索引。

  2. 关于事务

    知识点:MyISAM不支持事务,InnoDB支持事务。

    实践:事务是选择InnoDB非常诱人的原因之一,它提供了commit,rollback,崩溃修复等能力。在系统异常崩溃时,MyISAM有一定几率造成文件损坏,这是非常烦的。但是,事务也非常耗性能,会影响吞吐量,建议只对一致性要求较高的业务使用复杂事务。
    画外音:Can’t open file ‘XXX.MYI’. 碰到过么?

    小技巧:MyISAM可以通过lock table表锁,来实现类似于事务的东西,但对数据库性能影响较大,强烈不推荐使用。

  3. 关于外键

    知识点:MyISAM不支持外键,InnoDB支持外键。

    实践:不管哪种存储引擎,在数据量大并发量大的情况下,都不应该使用外键,而建议由应用程序保证完整性。外键的一致性是数据库服务器去维护,而交给应用服务器维护要更加快一些。

  4. 关于行锁和表锁

    知识点:MyISAM只支持表锁,InnoDB可以支持行锁。

    分析:
    MyISAM:执行读写SQL语句时,会对表加锁,所以数据量大,并发量高时,性能会急剧下降。
    InnoDB:细粒度行锁,在数据量大,并发量高时,性能比较优异。

    实践:网上常常说,select+insert的业务用MyISAM,因为MyISAM在文件尾部顺序增加记录速度极快。楼主的建议是,绝大部分业务是混合读写,只要数据量和并发量较大,一律使用InnoDB。

    常见坑:
    InnoDB的行锁是实现在索引上的,而不是锁在物理行记录上。潜台词是,如果访问没有命中索引,也无法使用行锁,将要退化为表锁。
    画外音:Oracle的行锁实现机制不同。

    例如:
    t_user(uid, uname, age, sex) innodb;

    • uid PK
    • 无其他索引

    update t_user set age=10 where uid=1;
    命中索引,行锁。

    update t_user set age=10 where uid != 1;
    未命中索引,表锁。

    update t_user set age=10 where name=’shenjian’;
    无索引,表锁。

    启示:InnoDB务必建好索引,否则锁粒度较大,会影响并发。

  5. 对比结论
    在大数据量,高并发量的互联网业务场景下,请使用InnoDB:

    • 行锁,对提高并发帮助很大
    • 事务,对数据一致性帮助很大

    这两个点,是InnoDB最吸引人的地方。

InnoDB的高并发模型

  1. 并发控制

    • 同其他并发模型的控制原理,如果对临界资源进行操作,不采取措施会导致不一致的情况
    • 如何进行并发控制?
      • 锁
      • 数据多版本(mv)
  2. 锁

    • 普通锁
    • 共享锁(读)与排他锁(写)
  3. 数据多版本(进一步的提高并发的性能)

    • 写任务发生时,将数据克隆一份,以版本号区分
    • 写任务操作新克隆的数据,直至提交
    • 并发读任务可以继续读取旧版本的数据,不至于阻塞
  4. redo、undo和回滚段

    1. redo
      • 数据库事务提交后,必须将更新后的数据刷到磁盘上,以保证ACID特性。磁盘随机写性能较低,如果每次都刷盘,会极大影响数据库的吞吐量。优化方式是,将修改行为先写到redo日志里(此时变成了顺序写),再定期将数据刷到磁盘上,这样能极大提高性能。
      • redo日志用于保障,已提交事务的ACID特性
    2. undo
      • 数据库事务未提交时,会将事务修改数据的镜像(即修改前的旧版本)存放到undo日志里,当事务回滚时,或者数据库奔溃时,可以利用undo日志,即旧版本数据,撤销未提交事务对数据库产生的影响。
      • undo日志用于保障,未提交事务不会对数据库的ACID特性产生影响。
    3. 回滚段
      • 存储undo日志的地方,是回滚段。
  5. InnoDB的多版本控制的引擎

    • 多版本并发控制(Multi Version Concurrency Control, MVCC)
    • MVCC就是通过“读取旧版本数据”来降低并发事务的锁冲突,提高任务的并发度

    • 旧版本数据存储在回滚段里

    • InnoDB的内核,会对所有row数据增加三个内部属性:

      (1)DB_TRX_ID,6字节,记录每一行最近一次修改它的事务ID;

      (2)DB_ROLL_PTR,7字节,记录指向回滚段undo日志的指针;

      (3)DB_ROW_ID,6字节,单调递增的行ID;

    • 并发高的核心原因就是读快照,即读回滚段的内容不被写事务阻塞

    • 除非显示的加锁,普通的select语句都是快照读,例如:

      • select * from t where id>2;
    • 这里的显示加锁,非快照读是指:

      select * from t where id>2 lock in share mode;

      select * from t where id>2 for update;

InnoDB中不同的锁结构

  1. 自增锁

    • 特殊的表级锁,专门针对事务插入auto_increment的列
    • 假如不是自增的列 t(id unique PK, name)
      • 这里不会使用自增锁
  2. 共享锁、排他锁

    • 标准的行锁,当行索引成功时,锁对应的行,当索引不成功时,锁退化为表锁
  3. 意向锁

    • InnoDB支持多粒度锁(multiple granularity locking),它允许行级锁与表级锁共存,实际应用中,InnoDB使用的是意向锁。

    • 意向锁是一个表级别的锁

      • 意向共享锁,事务有意向表中某些行加共享锁

      • 意向排他锁,事务有意向对表中的某些行加排他锁

      • 举个例子:

        select … lock in share mode,要设置IS锁;

        select … for update,要设置IX锁;

    • 它会与共享锁/排它锁互斥,其兼容互斥表如下:

      ​ S X

      IS 兼容 互斥

      IX 互斥 互斥

  4. 插入意向锁

    • 多个事务,在同一个索引,同一个范围区间插入记录时,如果插入的位置不冲突,不会阻塞彼此。
  5. 记录锁

    • 记录锁,封锁索引记录,select * from t where id=1 for update; 会在id=1的索引记录上加锁
      • select * from t where id=1; 这个只快照读,并不加锁 ,单纯读不需要加锁,但是事务紧接着会更新数据话,应该要加锁,防止出问题
  6. 间隙锁

    • 封锁索引记录中的间隙

      select * from t

      ​ where id between 8 and 15

      ​ for update;

      会封锁区间,防止其他事务id=10的记录插入,防止幻影数据,导致不可重复读,如果将隔离级别换成读提交,则间隙锁会消失

  7. 临键锁

    • 临键锁会封锁索引记录本身,以及索引记录之前的区间。

数据库索引

  1. 为什么要设计索引?

    • 用于提升数据库的查找速度
  2. 索引结构为什么要设计成树形

    • 对比hash索引和b+树形索引
    • hash树单个查找能力很快,但是大多数的查找情况基于范围的查找,hash查找不适合
  3. 为什么数据库的索引都使用b+树?

    1. 二叉搜索树
      • 数据量大的时候树的高度会比较高,普通的索引
      • 每个节点只存一个数据,磁盘io次数会太多了,聚合在一起,一起传输数据比较多
    2. B树
      • m叉搜索树—树高比较矮
      • 叶子结点和非叶子结点都存储数据
      • 中序遍历访问所有的节点
      • 局部性原理 – 充分提高磁盘io的效率
        • 内存的读写快,磁盘读写慢
        • 磁盘是按页预读
        • 数据读取集中
    3. b+树
      • 非叶子结点不存储数据
      • 叶子结点之间增加了链表
    4. B+树相对B树的更优的特性
      • 范围查找更加快
      • 适合大数据量的磁盘存储
      • B+树可以存储更多的索引
  4. InnoDB的索引

    • InnoDB的索引有两类索引,聚集索引(Clustered Index)与普通索引(Secondary Index)

    • InnoDB的每一个表都会有聚集索引:

      (1)如果表定义了PK,则PK就是聚集索引;

      (2)如果表没有定义PK,则第一个非空unique列是聚集索引;

      (3)否则,InnoDB会创建一个隐藏的row-id作为聚集索引;

    • (1)在索引结构中,非叶子节点存储key,叶子节点存储value;

      (2)聚集索引,叶子节点存储行记录(row);所以,InnoDB索引和记录是存储在一起的,而MyISAM的索引和记录是分开存储的

      (3)普通索引,叶子节点存储了PK的值;

事务的隔离级别

  1. ACID特性

    • atomic 原子性
    • consistent 一致性
    • isolation 隔离性 即事务之间不发生干扰
    • durability 持久性
  2. 脏读,例如读未提交的状态,读到了被人未提交的数据

  3. 不可重读读, 例如另外一个事务在两次查询之间修改了数据, 就是重复读的数据不一样

  4. 幻读,读到了别人插入的数据,或者重读提交的时候,发生了重读,

  5. 四个隔离级别

    • 读未提交
    • 读提交
    • 可重复读
    • 串行化
    • innoDB通过不同的锁策略来实现这些隔离的级别
  6. 可重复读的实现

    1. 普通的select,即快照读,并发性能最高
    2. 加锁的select(in share mode / select … for update), update, delete等语句, 它们的锁,依赖于它们是否在唯一索引(unique index)上使用了唯一的查询条件(unique search condition),或者范围查询条件(range-type search condition):
      • 在唯一索引上使用唯一的查询条件,会使用记录锁(record lock),而不会封锁记录之间的间隔,即不会使用间隙锁(gap lock)与临键锁(next-key lock)
      • 范围查询条件,会使用间隙锁与临键锁,锁住索引记录之间的范围,避免范围间插入记录,以避免产生幻影行记录,以及避免不可重复的读
  7. 读提交

    这是互联网最常用的隔离级别,在RC下:

    1. 普通的读是快照读
    2. 加锁select、update、insect都只会使用记录锁,除了在外键约束和重复性检查时会封锁区间
  8. 事务的开始时间不一样,会不会影响“快照读”的结果呢?

    快照产生的时间是在select 语句时

  9. 在可重复读的隔离级别下,当第一个Read操作发生的时候,Read view就会建立。 在Read Committed隔离级别下,每次发出Read操作,都会建立新的Read view。

Model Comparison Tool

发表于 2018-10-18 | 阅读次数:
字数统计: | 阅读时长 ≈

Model Comparison Tool

  Model comparisontool is a tool to help developer to compare difference between two model from different framework. The tool support Caffe, TF, MXNet, Chainer,pytorch. We mainly compare the variable node shape and functionnode type. We also compare the computation the graph and padding strategy.

  We also develop a web model comparison tool for user. User just need upload their models file and see the visually graph comparison result. Below Section 1 is the design detail of the tool and Section 2 is the Web version Detail, final is the Deploy part.

  1. Design Detail

    • The whole tool based on the architecture as below.

    archtecture

    • In our tool, firstlly loading the graph from different framework. You can find the method to load function responding to different framework as the Table below.

    | Framework | Input Files | Function file | Detail |
    | ———- | ——————— | ——————— | ———————————————————— |
    | Caffe | train_val.prototxt | load_caffe_model.py | Based on the caffe.proto.caffe_pb2 to read model parameters. |
    | Tensorflow | model save files | load_tf_model.py | Read computation graph from stored model file. So User should save the graph before. |
    | Chainer | model class .py files | load_chainer_model.py | Get all functionNode from output. |
    | MXNet | model json file | load_mxnet_model.py | Read json file and parameters. |
    | SSD | SSD model | load_ssd_*.py | SSD have special layer. |

    • Convert to unified data format and add different pdding strategies, as the Figure below. Later version, we add pw, ph, sh, sw, kh, kw params. source code in rank_multi_port.py.

    representation

    • Graph comparison is a difficult problem. So we transfer graph to an ordered list and find all difference. Source code in rank_multi_port.py.

      模型比对使用拓扑排序将图模型有序的排出来

  2. Web Model Comparison Tool

    For a better User Experience, we develop a website. User just upload the input files and get the visual comparison result and detail parameters. The system combine Django backend and Bootstrap front end. Using D3.js to visualize the model graph. All source code in the web-model-comparison-tool folder.

    comparison

    The website have two pages. In page 1, user upload their needing. Page 2 show the Graph result.

    upload

    result

  1. Deploy Method

    Our website verison deploy in developer01 server. The root username modeltools, password: abc110.

    1. ssh to the web server.
    1
    ssh modeltools@developer01
    1. cd to work folder.

      1
      cd ~/web-model-comparison-tool/model_comparison_tool/
    2. Start the uwsgi server.

      1
      uwsgi —socket /tmp2/model_comparison_tool.sock —module model_comparison_tool.wsgi —chmod-socket=777 —uid=www-data —gid=www-data
    3. Restart the nginx service.

      1
      sudo /etc/init.d/nginx restart
    4. Now you can browser the site: server local IP:8000 to use our tool.

    If wanna know more, please click on HELP.

分布式深度学习算法框架的实现

发表于 2018-10-17 | 阅读次数:
字数统计: | 阅读时长 ≈
  1. 分布式深度学习的几种分类

    data_paraliesm

    • 数据并行 — 每台机器有自己的计算模型,权值进行共享

      • 权值同步更新 – 有同步时间的矛盾,但是在收敛性上比较完整

      • 权值异步更新 – 同步时间比较少,但是收敛性的验证不是很完备

        datapara

    • 模型并行

      • 现阶段比较少的模型使用到

      • 不同的feature map 在不同的机器上

      • 或者不同的层在不同的机器上进行计算

  1. Non-blocking的数据并行算法

    Non-blocking

    • forward运算时,y = w*x ,每个interation 需要等待上一个循环计算好的w值,除了第一个iteration。
    • 这里需要注意的问题时,当通信时间过长时,需要考虑weight的等待时间,多一些模型会有影响
    • backward运算时, dx = dy*w,backward计算时,不需要w进行更新的值,所以这里可以起一个线程进行all_reduce(),进行权值的更新。
  2. 实验结果,相对于blocking的算法,训练时间上有20%-40%性能提升;

  3. 对于异常点的监控

    1. 云服务中异常点的监控
    2. 采用异步的同步模型,收敛效果要监控
  4. benchmark

    alexNet 128x3x224x224 177ms

    googlenet 128x3x224x224 687ms

Batch size BS=32 BS=64 BS=128 Comm.
VGG19 960ms 1700ms 3200ms 1300ms
GoogleNet v1 163ms 280ms 520ms 230ms
ResNet50 420ms 800ms 1600ms 340ms

16 commucation node

高性能分布式流控组件

发表于 2018-10-17 | 阅读次数:
字数统计: | 阅读时长 ≈
  1. 分布式流控组件的架构
  2. 令牌桶算法和漏桶算法
  3. 单点频控和分布式频控的区别
  4. 分时上报和全量上报的性能差别
  5. 结合配置中心实现流控性能的动态下发

阿里的分布式流控方案

Sentinel是什么

Sentinel 是面向分布式服务架构的轻量级流量控制框架,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助您保护服务的稳定性。

Sentinel基本概念
  1. 资源

    一个服务或者代码块就是一个资源,概念同RESTful的理解一样,状态可转移 表现层状态转化

  2. 规则

    围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

Sentinel的功能和设计理念
  1. 流量控制

    根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状

  2. 熔断降级

    当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常比例升高的时候,则对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。

    • 熔断手段
      1. 通过并发线程数进行限制,堆积了过多线程后,会拒绝新来的请求
      2. 通过响应时间来对资源进行降级,过了时间窗口进行恢复
  3. 系统负载保护

    系统的入口流量和系统的负载达到一个平衡

Sentinel如何工作的

工作机制:

  • 提供API,来定义需要保护的资源,提供设施对资源进行实时统计和调用链分析
  • 根据预设规则进行流量控制
  • 实时的监控系统

基于Etcd的配置中心搭建

发表于 2018-10-17 | 阅读次数:
字数统计: | 阅读时长 ≈
  1. 项目内容

    基于etcd的搭建配置中心

    • etcd是关键数据的分布式可靠kv存储。主要用于一些基础组件事务性数据的存储,强调数据和事务的一致性和可靠性。
    • 特点
      • 简单,user-facing API(gRPC)
      • 安全,automatic TLS 认证
      • 快速,10000 writes/sec的速率
      • 可靠,Raft分布式算法
  2. 分布式一致性算法

    1. Raft一致性算法

      • 节点的三个状态:跟随者、候选者、领导者。三个之间的转换关系如图。

        raft

      • 跟随者可以接受候选者的vote_rpc,根据版本号和任期号投出。可以接受领导者的心跳包,维持其leader状态。当timeout时间没收到包时,会转变为候选者状态,像其他节点发起投票。

      • 候选者,收到大多数节点的投票,会转变为领导者。在选举过程中发现任期数比自己高的候选者,会转变为跟随者。如果选举超时,会重新发起投票

      • 领导者,发现任期比自己高的节点会变为跟随者。会发送心跳包维持自己的领导地位;

    2. 领导人选举

      • 启动时的随机timeout时间
      • 增加自己的任期号,转变为候选人状态,发送请求投票RPCs
    3. 日志复制

      • 收到客户端请求时,并行发起附加条目RPCs给其他服务器,当这条日志被其他服务器复制后,领导者才会加入到它的状态机中,返还结果给客户端。
      • 领导人处理不一致是通过强制跟随者直接复制自己的日志来解决了。这意味着在跟随者中的冲突的日志条目会被领导人的日志覆盖
    4. 日志压缩

    5. 配置更改

  3. 服务中配置的动态下发的结构

    • etcd_struture
  4. 主要代码结构和细节

    1. 本地缓存啊
    2. 起watch监控对应的配置
    3. 替换本地的配置 多线程同步的问题 读写锁
1
2
3
4
5
type Watcher interface {
AddEntry(key string, data []byte) error
DeleteEntry(key string) error
GetPrefix() string
}
  1. 具体自己提供结构的主要能力
  • 不同的本地配置结构实现该三个接口就可以快速的接入watcher配置的能力
  • watch结构内会初始化 new client, 对于本地缓存的配置。 会先load_conf 一把,然后会开始开启watch对应的配置
  • addEntry 具体配置修改添加本地配置
  • delete 参数本地的配置
  • getPrefix 配置本地prefix的能力
123
Mark Zhan Zha

Mark Zhan Zha

回滚即使成长, 更高的要求是对极致和细节的追求

22 日志
27 标签
GitHub E-Mail
© 2018 Mark Zhan Zha | Site words total count:
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4