大数据相关概念

处理

  • 数据清理
  • 数据集成
    • 将不同来源于格式的数据逻辑上或物理上进行集成的过程

分析

  • 联邦分析
    • 一种数据科学方法实践,用于分析存储在用户本地设备中的原始数据,本地计算然后汇总

查询

  • 联邦查询
    • 对多个不同数据源进行检索进行查询

概念

  • 数据孤岛
    • 物理性
      • 数据在不同部门相互独立存储,独立维护,彼此间相互孤立
    • 逻辑性
      • 不同部门站在自己角度定义数据,使得数据被赋予不同含义,加大了跨部门数据合作的沟通成本
  • 数据仓库,Data Warehouse,DWH
    • 是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合,用于支持管理决策和信息的全局共享,主要处理历史的、结构化的数据
  • 数据集市
    • 数据仓库的特殊形式,正如数据仓库,数据集市也包含对操作数据的快照,便于用户基于历史趋势与经验进行战略决策。两者关键的区别在于数据集市的创建是在有具体的、预先定义好了的对被选数据分组并配置的需求基础之上的。配置数据集市强调对相关信息的易连接性
    • 通俗讲,数据是专门针对特定用户/团队处理后的,以提高数据易用性
  • 数据湖
    • 数据湖是一个存储企业各种各样原始数据的大型仓库,其中的数据可供存取、处理、分析和传输
    • 可以包括结构化数据(关系数据库数据)、半结构化数据(json,xml等)、非结构化数据(电子邮件,文档)、二进制数据(音视频等)
  • 联机分析处理,Online Analytical Processing,OLAP
  • 联机事务处理,Online Transaction Processing,OLTP
  • 数据库管理系统,Database Management System,DBMS

文章

参考

云原生

定义

云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式API。

这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。

技术

不可变基础设施

在传统可变服务器基础架构中,服务器在创建之后会不断更新和修改,包括软件环境、配置等,在这种架构中存在配置漂移、雪花服务器等问题。配置漂移 是指对操作系统的更改不被记录,独特和可变的配置,无法/很难在其他机器复现。雪花服务器 存在配置漂移问题的服务器,很难被复制。凤凰服务器 对配置进行版本管理,减少或解决配置漂移问题。

在不可变基础架构/设施中,对配置进行版本控制,服务器一但部署,不会再手动更改。如需更新配置、软件环境,通过部署版本化的配置解决,或通过部署打包的公共镜像。这种方式,有更高的一致性、可靠性及可预测的部署过程。

参考

c++ - debug, asan

AddressSanitizer (ASan) C/C++ 程序的内存错误检测器,可以检查如下错误。

TL;TR

1
2
3
4
5
# CMAKE_CXX_FLAGS
-g3 -fno-omit-frame-pointer -fno-common -fsanitize=undefined -fsanitize=address -fsanitize-recover=address

# 环境变量
export ASAN_OPTIONS=symbolize=true:halt_on_error=false:abort_on_error=false:disable_coredump=false:unmap_shadow_on_exit=true:disable_core=false:sleep_before_dying=15:fast_unwind_on_fatal=1:log_path=asan.log

指定 Asan Library 路径

1
export LD_LIBRARY_PATH=/path/to/libasan.so.x

注意

  • 部分 IDE 集成分析工具,ASAN_OPTIONS 可能被覆盖,比如 CLion,需要在设置中设置 ASAN_OPTIONS(AddressSanitizer)。

image-20241014111225039

使用

编译选项

1
2
3
4
5
6
-fsanitize=address						# 开启内存越界检测
-fsanitize-recover=address # 内存出错后继续运行, 需配合运行选项 halt_on_error=0
-fno-stack-protector # 去使能栈溢出保护
-fno-omit-frame-pointer # 去使能栈溢出保护
-fno-var-tracking # 默认选项为-fvar-tracking,会导致运行非常慢
-g1 # 表示最小调试信息,通常debug版本用-g即-g2

示例

1
2
ASAN_CFLAGS += -fsanitize=address -fsanitize-recover=address
ASAN_CFLAGS += -fno-stack-protector -fno-omit-frame-pointer -fno-var-tracking -g1

链接选项

1
ASAN_LDFLAGS += -fsanitize=address -g1 # 如果使用gcc链接,此处可忽略

运行选项

ASAN_OPTIONS是Address-Sanitizier的运行选项环境变量。

1
2
3
4
5
6
7
8
9
10
11
halt_on_error=0/1 					# 检测内存错误后继续运行
abort_on_error=0/1 # 遇到错误后调用 abort() 而不是 _exit()
detect_leaks=0/1 # 使能内存泄露检测
malloc_context_size=15 # 内存错误发生时,显示的调用栈层数为15
log_path=asan.log # 内存检查问题日志存放文件路径
suppressions=$SUPP_FILE # 屏蔽打印某些内存错误
symbolize=0/1 # 启用符号化,将错误地址翻译成代码行号
disable_coredump=0/1 # 禁用 core dump
disable_core=0/1 # 禁用 core dump
unmap_shadow_on_exit=1
sleep_before_dying=60

更多

示例

1
2
3
4
5
6
7
# 1
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer
export ASAN_OPTIONS=halt_on_error=0:use_sigaltstack=0:detect_leaks=1:malloc_context_size=15:log_path=/tmp/asan.log:suppressions=$SUPP_FILE

# 2
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer
export ASAN_OPTIONS=symbolize=true:halt_on_error=false:abort_on_error=false:disable_coredump=false:unmap_shadow_on_exit=true:disable_core=false:sleep_before_dying=15:log_path=asan_log

cmake 链接

1
2
3
4
#asan 链接sys
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
SET(CMAKE_C_FLAGS_ASAN "-O2 -g -fsanitize=address -fno-omit-frame-pointer" CACHE STRING "Flags used by the C compiler during asan builds." FORCE)
SET(CMAKE_C_FLAGS "-O2 -g -fsanitize=address -fno-omit-frame-pointer -lstdc++ -lasan" CACHE STRING "Flags used by the C compiler during asan builds." FORCE)

Debug

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
==1867==ERROR: AddressSanitizer: heap-use-after-free on address 0x7f69c0e59823 at pc 0x000001f1f50c bp 0x7f69c02624e0 sp 0x7f69c02624d0
...
0x7f69c0e59823 is located 35 bytes inside of 8388608-byte region [0x7f69c0e59800,0x7f69c1659800)
freed by thread T14 here:
...
previously allocated by thread T14 here:
...
SUMMARY: AddressSanitizer: heap-use-after-free /data/zhenkai.sun/ranker/cmake-build-debug/CMakeUnzipPackages/mongo-c-driver-1.19.1/src/libbson/src/bson/bson.c:1993 in bson_init_static
Shadow bytes around the buggy address:
0x0fedb81c32b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fedb81c32c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fedb81c32d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fedb81c32e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fedb81c32f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0fedb81c3300: fd fd fd fd[fd]fd fd fd fd fd fd fd fd fd fd fd
0x0fedb81c3310: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0fedb81c3320: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0fedb81c3330: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0fedb81c3340: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0fedb81c3350: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==1867==ABORTING

c++ 手册

定义变量

基础操作

字符串

c++

1
2
// 查找
size_type idx = str.find(s); // 未查到: (idx == std::string::npos)

容器

顺序容器

  • [x] 可变数组 vector
  • [x] 双向链表 list
  • [ ] 单向链表 forward_list
  • [ ] 双端队列 deque
  • [ ] 固定数组 array

关联容器

  • [ ] 集合 set、multiset
  • [ ] 映射 map、multimap

容器适配器

  • [ ] 栈 stack
  • [ ] 队列 queue
  • [ ] 优先队列 priority_queue

可变容量数组 vector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <vector>
// 初始化
std::vector<T> vec;
std::vector<T> vec(5, 0); // 长度 5, 元素值
std::vector<T> vec = {1, 2, 3, 4, 5};
std::vector<T> vec(arr, arr + sizeof(arr) / sizeof(arr[0]));
std::vector<T> vec(container.begin(), container.end());
// 插入
vec.push_back(element);
vec.emplace_back(element);
vec.insert(iterator, element);
// 访问元素
vec.at(index);
vec[index];
vec.front();
vec.back();
// 删除元素
vec.pop_back();
vec.erase(iterator);
vec.erase(start_iterator, end_iterator);
// 大小和容量
vec.resize();

双向链表 list

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
#include <list>
// 初始化
std::list<T> lst;
std::list<T> lst = {1, 2, 3, 4, 5};
std::list<T> lst(container.begin(), container.end());
// 插入
lst.push_back(element);
lst.push_front(element);
lst.insert(it, element);
lst.insert(it, n, element); // 在 it 插入 n 个 element
lst.insert(it, begin, end); // 在 it 插入迭代器 [begin, end) 内元素
// 赋值 (清空元素并重置为指定元素)
assign(begin, end);
assign(n, element);
// 访问
l.begin(); // 返回迭代器
l.end(); // 返回迭代器
l.front(); // 返回元素
l.back(); // 返回元素
// 删除
erase(begin, end);
erase(it);
remove(element);
clear();
pop_back();
pop_front();

单向链表 forward_list

1
2
3
4
5
6
7
8
9
10
11
12
#include <forward_list>
// 初始化
std::forward_list<int> fl;
std::forward_list<int> fl {1, 2, 3};
std::forward_list<int> fl(n, element);
std::forward_list<int> fl(begin, end);
// 插入
fl.push_front(element);
fl.insert_after(iter, element);
// 赋值 (清空元素并重置为指定元素)
// 访问
// 删除

unordered_set

1
2
3
4
5
6
7
8
9
// 修改
insert(Key&&)
emplace(Args&&)
erase(const Key&)
merge()
// 查找
iterator find(Key)
size_type count(Key)
bool contains(Key)

基础知识

虚函数

  • 运行时动态绑定
  • 基类使用虚函数表(vtable)存储每个虚函数的地址

c++基础

[toc]

组织

文件

文件

文件名后缀 说明
.c c 源文件
.h c 同文件
.cpp c++ 源文件
.hpp c++ 头文件
.hh c++ 头文件
.hxx c++ 头文件
.h++ c++ 头文件

原则

  • 通常的,.h.c 对应 c 代码,.hpp.cpp 对应 c++ 代码
    • 除此之外的后缀,多是项目规范,保持一惯性即可
  • 保持 .h 文件是 c 兼容的代码(不包含 c++ 代码)
  • 如果想编写 c 和 c++ 的混合代码
    • 可以在 .hpp 中使用 extern "C" 来实现
  • 不要使用 .H.C 后缀,因为部分文件系统不区分大小写,比如 windows,macos 在格式化分区时也有不区分大小写的选项

输入输出

标准输入输出包含:

  • cin
  • cout
  • cerr
  • clog
    • 带缓冲区,常用于写日志数据
1
2
3
4
5
6
// 持续输入
while (cin >> num)
sum += num;

// 读取到类对象
cin >> Item;

基础

变量

初始化

C++ 支持两种初始化方式,复制初始化和直接初始化。

1
2
int val(1024);   // 直接初始化;效率更高
int val = 1024; // 复制初始化

对于内置类型来说,复制初始化和直接初始化几乎没有差别。函数体外定义的内置变量都初始化成0,函数体内定义的内置变量不进行自动初始化。变量在使用前必须被定义,且只允许被定义一次。

声明

为了让多个文件可以访问相同的变量,C++ 区分了声明和定义。声明用于向程序表明变量存在,及其类型和名字;定义用于为变量分配空间,还可对变量进行初始化。

1
2
3
4
extern int i;	 // 声明变量
int i; // 定义变量

extern int i = 1; // 有初始化的声明,可视为定义

数据类型

类型 含义 最小存储空间
bool 布尔型
char 字符 8位
wchar_t 宽字符型 16位
short 短整型 16位
int 整型 16位
long 长整型 32位
float 单精度浮点数 6位有效数字
double 双精度浮点型 10位有效数字
long double 扩展精度浮点型 10位有效数字
1
wchar_t wc = L'a'; // 字符前面加 L 表示宽字符 

类型

数字

字符串

初始化

1
string s(10, '0');

子串

1
2
str.substr(3, 5);    // [3, 8)
str.substr(5); // [5, ~)

布尔

限定

static

1
2
3
4
5
6
7
class A {
public:
static A Instance() { // 实现单例
static A _a; // static 修饰,只会创建一次 A 对象
return _a;
}
};

常量

定义

数据结构

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 创建
int i[5];
int j[2][2];

// 初始化
int i[] = {1, 2, 3};
int j[][1] = {{1}, {2}, {3}};

// 二维数组初始化
int k[2][2] = {{1, 2}, {3}};
1 2
3 0

// 静态数组
int i[5] = {0}; // 创建静态数组, 并初始化, 没设置的元素被重置为 0
std::cout << sizeof(i) << std::endl; // 20, 5 * 4. 5 个元素, 每个占用 4 字节空间
int b[3][3] = {0}; // 多维数组

// 动态数组
int size = 3;
int *c = new int[size]; // 创建动态数组
std::cout << sizeof(c) << " - " << sizeof(*c) << std::endl; // 8 - 4. 8: 64位机器, 指针大小, 4: int 元素大小
memset(c, 0, size * sizeof(*c)); // 必须要乘 size 才能算出总的占用内存
delete[] c;

列表

1
2
3
// 初始化
vector<int> iv{1, 2, 3} // [1, 2, 3]
vector<int> iv(3, 1); // [1, 1, 1]

集合

映射

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
// 初始化
std::map<int, int> m = {
{1, 2},
{2, 3}
};

std::map<std::string, std::vector<int> > mapOfOccur = {
{ "Riti", { 3, 4, 5, 6 } },
{ "Jack", { 1, 2, 3, 5 } }
};

// 遍历
std::map<int, int> m = ...;
for (auto &entry : m) {
std::cout << entry.first << " -> " << entry.second << std::endl;
}

// 插入
map<int, string> mp;
mp.insert(pair<int,string>(1,"aaaaa"));
mp.insert(make_pair<int,string>(2,"bbbbb"));
mp.insert(map<int, string>::value_type(3,"ccccc"));
mp[4] = "ddddd";

std::map<char,int> mp;
mp.emplace('x',100);

// 查找
std::map<char,int>::iterator it = mp.find('x');
if (it != mp.end())
// exists
else
// not exists

// 删除
std::map<char,int>::iterator it = mp.find('x');
if (it != mp.end())
mp.erase(it);

语法

程序结构

注释

运算符

条件控制

循环

判断

函数

特殊成员函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 默认构造函数 (Default constructor)
classname ()
// 非默认函数
explicit classname(type param)
// 拷贝构造函数 (Copy constructor)
classname(const classname &other)
// 赋值构造 (Copy assignment operator)
classname& operator=(const classname &other)
// move 构造 (Move constructor)
classname(classname &&other)
// 赋值 move 构造 (Move assignment operator)
classname& operator=(classname &&other)

// 析构函数 Destructor
~classname()
Function syntax for class MyClass
Default constructor MyClass();
Copy constructor MyClass(const MyClass& other);
Move constructor MyClass(MyClass&& other) noexcept;
Copy assignment operator MyClass& operator=(const MyClass& other);
Move assignment operator MyClass& operator=(MyClass&& other) noexcept;
Destructor ~MyClass();

特性

语法糖

default

default 关键词为类的特殊默认无参函数(构造、析构、拷贝构造、拷贝赋值)提供默认行为。

1
2
3
4
5
6
7
8
9
10
11
class A
{
public:
A() = default;
A(const A&);
A& operator = (const A&);
~A() = default;
};

A::A(const X&) = default; // 拷贝构造函数
A& A::operator= (const A&) = default; // 拷贝赋值操作符

delete

default ,屏蔽默认行为。

1
2
3
4
5
6
7
class A
{
A& operator=(const A&) = delete; // assignment operator disabled
};

A a, b;
a = b; // ERROR: 拷贝赋值操作被禁用

thread_local

lambda

lambda 表达式格式如下。

1
[函数对象参数] (操作符重载函数参数) mutable 或 exception 声明 -> 返回值类型 {函数体}

函数对象参数

参数范围 参数传递方式 备注
没有函数对象参数 - -
= 表达式所有可访问局部变量(包括所在类的 this 对象) 值传递 -
& 表达式所有可访问局部变量(包括所在类的 this 对象) 引用传递 -
this 函数体内可以使用 Lambda 所在类中的成员变量 TBD -
a a 值传递 变量默认为 const,如果需要修改需为函数体添加 mutable 修饰符
&a a 引用传递
a,&b a,b a 为值传递,b 为引用传递 -
=,&a,&b 表达式所有可访问局部变量(包括所在类的 this 对象) a、b 引用传递,其他参数是值传递 -
&,a,b 表达式所有可访问局部变量(包括所在类的 this 对象) a、b 值传递,其他参数是引用传递 -

空指针处理

函数式编程

泛型编程

1
2
3
4
5
6
7
8
9
10
// 从标准输入读取T类型数据
template <typename T>
T r() {
T t;
cin >> t;
return t;
}

// 使用
int x = r<int>();
1
2
3
4
5
6
7
8
9
10
11
12
// 值交换
template <typename T>
void swapT(T& a, T& b) {
a ^= b;
b ^= a;
a ^= b;
}

// 使用
vector<int> iv{1, 2, 3};
swapT(iv[0], iv[2]);
// 1 2 3 -> 3 2 1

不限于类型。

1
2
3
4
5
6
7
8
9
template<unsigned N>
void f() {
std::cout << N << std::endl;
}

int main() {
f<10>();
return 0;
}

类型推演

重用方法

1
2
3
std::is_same<TA, TB>
typeid()
std::is_same_v<TA, TB> #include <variant>

类型读取及定义

1
2
template<auto object, class T=std::decay_t<decltype(*object)>>
int Function();

类型判断

1
2
3
4
5
6
7
8
9
#include <concepts>

template<typename Type>
concept CharTypes = std::is_same<Type, char>::value ||
std::is_same<Type, wchar_t>::value || std::is_same<Type, char8_t>::value ||
std::is_same<Type, char16_t>::value || std::is_same<Type, char32_t>::value;

template<CharTypes T>
class Some{};
1
2
3
4
5
6
7
8
#include <variant>

template<class T>
void f(T t) {
if (std::is_same_v<T, std::string>) {
// DO SOMETHING
}
}

宏定义 define

##

连接形参,忽略前后空白符。

1
2
3
4
5
6
#define Concat(a, b) a##b

int ab = 1, ax = 2, xa = 3;
std::cout << Concat(a, b) << std::endl; // output: 1
std::cout << AppendX(a) << std::endl; // output: 2
std::cout << XAppend(a) << std::endl; // output: 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// not ok
#define select(m, key) m##[#key]
// ok
#define select(m, key) (m)[#key]

//--- .
// not ok
#define select(m, key) m##.##key
#define select(m, key) m.##key
// ok
#define select(m, key) m.key


// 使用
std::map<std::string, std::string> m;
m["a"] = "0";
auto v = select(m, a);

// 在使用宏变量时,外加小括号, 比如 #define add(a, b) (a) + (b)

#@

字符化形参。

#

字符串化形参。

1
2
3
4
5
6
#define ToString(a) #a
std::cout << ToString(abc) << std::endl; // abc

// 拼接
#define ToSV(member) #member##sv
ToSV(time) // 等价于 "time"sv

Parameter pack

Parameter pack

A template parameter pack is a template parameter that accepts zero or more template arguments (non-types, types, or templates). A function parameter pack is a function parameter that accepts zero or more function arguments.

A template with at least one parameter pack is called a variadic template.

包含至少一个参数包的模板称为可变模板。

右值引用&&

C++ 11 引入右值引用主要是为了解决以下几个问题:

  1. 优化复制大对象的性能问题。

在传递一个对象时,如果使用常规的左值引用,就需要进行拷贝构造函数的调用,这会导致复制大对象的时候开销很大。而右值引用可以避免这种情况的发生。因为右值引用本身不会进行对象的拷贝操作,只是将对象所在的内存地址绑定到右值引用上,从而提高代码执行效率。

  1. 实现移动语义,支持转移资源所有权。

在C++11中,新增了std::move函数,可以将一个对象的资源所有权转移到另一个对象中,这就是移动语义。通过将对象的内部数据指针从源对象转移到目标对象,可以避免创建和销毁临时对象,从而提高代码执行效率。而实现移动语义,需要使用右值引用的特性。

总之,右值引用的引入,旨在提高C++代码的性能和效率,支持更加高效的对象传递和资源管理方式,并且为C++编程带来更多的灵活性和扩展性。

其他优化场景。

  • 函数中返回一个临时变量时,编译器会自动调用移动构造函数,并将临时变量的资源所有权移动到函数的返回值中,从而避免进行数据拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 示例 1
std::string GetString() {
std::string t = "abc";
std::string res = t + "def";
std::cout << "A " << static_cast<void *>(res.data()) << std::endl;
return res; // 函数返回临时变量, 未进行对象拷贝
}

int main() {
auto r = GetString(); // 右值引用赋值给左值变量, 不进行对象拷贝
std::cout << "B " << static_cast<void *>(r.data()) << std::endl;
std::cout << "---" << std::endl;
auto r2 = std::move(GetString());
std::cout << "B " << static_cast<void *>(r2.data()) << std::endl;
return 0;
} /* output
A 0x16eeeb100
B 0x16eeeb100
---
A 0x16eeeb0c0
B 0x16eeeb0d8
*/

注意

  • 对右值调用 std::move 没有作用

其他

常量

1
2
3
// MAX / MIN
INT_MAX
INT_MIN

进阶

C++ 程序质量保障

  • 代码覆盖率(code coverage)
  • 内存检查
    • asan
    • valgrind
  • CPU Profiler

malloc

常用的 malloc 库,及实践。

  • jemalloc
  • tcmalloc
    • 性能要好于 jemalloc
  • mimalloc
    • 偶尔会 core

RAII

RAII(Resource Acquisition Is Initialization,资源获取即初始化)使用局部变量来管理资源,是 C++ 中常用的资源管理方式。

时间

  • time unit
    • std::chrono::microseconds
    • std::chrono::milliseconds
    • std::chrono::second
    • std::chrono::minutes
    • std::chrono::hours
    • std::chrono::days
    • std::chrono::months
    • std::chrono::years
  • clock
    • steady_clock 单调递增时钟
    • system_clock 系统时间时钟
    • high_resolution_clock 高精度时钟
  • time_point
  • duration
  • duration_cast
1
std::chrono::time_point<Clock,Duration>::time_point

sample

1
2
3
4
5
using namespace std::chrono_literals; // 8h, 24m, 15s
// 当前时间
std::chrono::system_clock::now(); // return time_point
// duration
std::chrono::duration(10s); // 10 秒

linux xargs

描述

功能

  • 配合管道使用,为命令传递参数

使用

1
2
3
4
5
6
$ find /sbin -perm +700 | ls -l         # NO; 这个命令是错误的
$ find /sbin -perm +700 | xargs ll # NO; 无法配合alias使用
$ find /sbin -perm +700 | xargs ls -l # OK; 这样才是正确的

# 杀死进程
$ ps aux | grep <program-name> | awk '{ print($2)}' | xargs kill

注意

  • xargs 无法获取当前shell的alias,所有无法配合alias使用

参考

Java Socket 传输文件

描述

基于Java Socket传输文件/文件夹,包括客户端、服务端,具备以下特点。

  • 一次连接,传输多文件,在传输大量小文件场景下,可节约大量连接创建时间及资源消耗
  • 支持传输文件夹,但会跳过空文件夹
  • 服务端支持多线程
  • 支持结果回显

实现

依赖

程序集成在Maven工程内,lombok非必需,仅用于提升代码整洁度,如剔除需少量改动代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>

基础信息

用于传送每次请求附带参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(fluent = true)
public class FileTransferInfo {
public static final String TYPE_DELETE = "delete";
public static final String TYPE_WRITE = "write";
public static final String TYPE_CLOSE = "close";
public static final String MSG_DONE = "done";
public static final String MSG_ERROR = "error";

private String name;
private String path;
private String type;
}

服务端

服务端包含FileTransferServer.javaFileTransferServerThread.java 两个文件,用于实现多线程支持。

FileTransferServer

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
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.ServerSocketChannel;

public class FileTransferServer {
private ServerSocket ss;
private final int port;
private final String wd;

/**
* @param port network port for listening
* @param wd working directory, files will be saved into this folder
*/
public FileTransferServer(int port, String wd) {
this.port = port;
this.wd = wd;
}

public void start() throws Exception {
FileUtils.forceMkdir(new File(wd));

InetSocketAddress listenAddr = new InetSocketAddress(port);
ServerSocketChannel listener = ServerSocketChannel.open();
ss = listener.socket();
ss.setReuseAddress(true);
ss.bind(listenAddr);

while (true) {
Socket socket = ss.accept();
System.out.println("Received request: " + socket);
FileTransferServerThread thread = new FileTransferServerThread(socket, wd);
thread.start();
}
}

public void close() {
if (ss != null && !ss.isClosed()) {
try {
ss.close();
} catch (Exception ignored) {
}
}
}
}

FileTransferServerThread

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
67
68
69
70
71
72
73
import org.apache.commons.io.FileUtils;
import com.google.gson.Gson;

import java.io.*;
import java.net.Socket;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FileTransferServerThread extends Thread {
private static final Gson GSON = new Gson();
Socket socket;
String wd;

public FileTransferServerThread(Socket socket, String wd) {
this.socket = socket;
this.wd = wd;
}

@Override
public void run() {
try {
boolean loopFlag = true;
FileTransferInfo info;
DataOutputStream dot = new DataOutputStream(socket.getOutputStream());
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
DataInputStream dis = new DataInputStream(bis);

while (loopFlag) {
String msg = dis.readUTF();
info = GSON.fromJson(msg, FileTransferInfo.class);

if (FileTransferInfo.TYPE_CLOSE.equals(info.type())) {
loopFlag = false;
} else if (FileTransferInfo.TYPE_DELETE.equals(info.type())) {
Path fnl = Paths.get(wd, info.path());
FileUtils.deleteQuietly(fnl.toFile());
} else {
long length = dis.readLong();

Path fnl = Paths.get(wd, info.path(), info.name());
System.out.println("Receiving file: " + fnl.toAbsolutePath() + ", length: " + length);
FileUtils.forceMkdir(Paths.get(wd, info.path()).toFile());
FileUtils.touch(fnl.toFile());

FileOutputStream fos = new FileOutputStream(fnl.toFile());
BufferedOutputStream bos = new BufferedOutputStream(fos);
for (long j = 0; j < length; j++) {
bos.write(bis.read());
}
bos.close();
System.out.println("Saved file: " + fnl.toAbsolutePath());
}

dot.writeUTF(FileTransferInfo.MSG_DONE);
dot.flush();
}

System.out.println("Closing socket...");
dis.close();
dot.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}

客户端

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
import java.io.*;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Paths;

import com.google.gson.Gson;

public class FileTransferClient {
private static final Gson GSON = new Gson();
Socket socket;
DataOutputStream dos;
DataInputStream dis;

public FileTransferClient(Socket socket) throws IOException {
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
this.socket = socket;
this.dos = new DataOutputStream(bos);
this.dis = new DataInputStream(socket.getInputStream());
}

public void send(String path, File file) throws IOException {
if (file.isDirectory()) {
File[] files = file.listFiles();
for (int i = 0; files != null && i < files.length; ++i) {
File f = files[i];
send(Paths.get(path, file.getName()).toString(), f);
}
} else {
System.out.println("Sending file: " + file.getAbsolutePath() + ", length: " + file.length());
dos.writeUTF(GSON.toJson(new FileTransferInfo().name(file.getName()).path(path)));
dos.writeLong(file.length());
Files.copy(file.toPath(), dos);
dos.flush();
System.out.println(dis.readUTF());
}
}

public void close() throws IOException {
FileTransferInfo info = new FileTransferInfo().type(FileTransferInfo.TYPE_CLOSE);
dos.writeUTF(GSON.toJson(info));
dos.flush();
System.out.println(dis.readUTF());
dis.close();
dos.close();
socket.close();
}

public void delete(String path) throws IOException {
FileTransferInfo info = new FileTransferInfo().path(path).type(FileTransferInfo.TYPE_DELETE);
dos.writeUTF(GSON.toJson(info));
dos.flush();
System.out.println(dis.readUTF());
}
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
// 启动服务端
FileTransferServer server = new FileTransferServer(2221, "/tmp");
server.start();

// 客户端传送文件
String host = "127.0.0.1";
int port = 2221;
String path = "receiver";
Socket socket = new Socket(host, port);
FileTransferClient client = new FileTransferClient(socket);
client.delete(path);
client.send(path, new File("/Users/wii/Downloads/cat.jpg"));
client.close();

更多

  • 优化
    • 零拷贝
  • 代码

shell 编程

快捷键

1
2
3
4
5
6
7
8
9
10
11
12
Ctrl + R : 搜索历史命令
Ctrl + a : 指针跳到命令行开始
Ctrl + e : 指针跳到命令行结尾
Ctrl + b : 向后移动一个字符
Ctrl + f : 向前移动一个字符

Alt + right : 向右移动一个词
Alt + left : 向右移动一个词
Ctrl + X, Ctrl + X : 在当前位置和开始位置切换

Ctrl + s : 暂停终端输出
Ctrl + q : 恢复终端输出

特殊参数

1
2
3
4
5
6
7
8
9
10
11
12
13
$!  最后运行的后台线程 id
$? 最后运行的命令的结束码
$$ 脚本本身进程 id

$# 参数个数
$@ 参数列表. "$@" = "$1" "$2" "$3" "$..."
$* 参数列表. "$*" = "$1 $2 $3 ..."
$0 脚本文件名
$1~$n 参数

# 参数列表截取
${*:2} 截取第二个参数及之后
${*:2:3} 截取第二、三个参数

getopts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# opts
:前缀 忽略错误
:后缀 参数后必须有值

# example
:abc:de: 忽略参数错误,-c、-e后必须有值

while getopts ":ab:Bc:" opt; do
case $opt in
a) echo "found -a" ; a="ok" ;;
b) echo "found -b and value is: $OPTARG" ;;
*) "echo usage" ;;
esac
done

函数

1
2
3
4
5
echo() {
echo "$1"
}

echo Hello

test

test 命令通常被 [...] 替代

1
2
3
4
5
6
7
8
# [ ... ]
[ ... ] :

# if ... else ...
if [ expression ]
then
...
fi

[] 和 [[]] 的区别

  • 兼容性

    • [] 是内置命令 test 的可选项,在 Linux、Unix、POSIX 系统兼容

    • [[]] 是 Korn Shell 作为增强功能引入的,被 base、zsh 等支持,但在 POSIX 系统中不兼容

  • [[]][] 的差异

    • 比较运算符
      • 比如,[[ 1 < 2 ]],等价于 [ 1 \< 2 ]
    • 布尔运算符(-a -> &&-o -> ||
      • 比如,[[ 3 -eq 3 && 4 -eq 4 ]] 等价于 [ 3 -eq 3 -a 4 -eq 4 ]
    • 组合表达式
      • 比如,[[ 3 -eq 3 && (2 -eq 2 || 1 -eq 1) ]] 等价于 [ 3 -eq 3 -a \( 2 -eq 2 -o 1 -eq 1 \) ]
    • 模式匹配
      • 比如,[[ $name = *c* ]][] 中没有对应的匹配运算
    • 正则匹配
      • 比如,[ $name =~ ^Ali ][] 中没有对应的匹配运算
    • 单词切分([[]] 不会对变量值按空白符切分,[] 对应得需要添加 "" 来达到相似的效果)
      • 比如,name = "Wukong Sun"[[ $name ]] 等价于 [ "$name" ]

参数

字符串

1
2
3
4
5
6
-n 不为空
-z 为空
{string1} = {string2} string1 和 string2 相同
{string1} != {string2} string1 和 string2 不相同
{string1} < {string2} 基于 ASCII 码比较
{string1} > {string2} 基于 ASCII 码比较

正则匹配

1
2
3
4
"{data}" =~ ^{regex}$

# 示例
[[ "$date" =~ ^[0-9]{8}$ ]] && echo "YES"

变量

1
-v 变量是否 set

数字比较

1
2
3
4
5
6
7
{number1} -eq/-ne/-lt/-le/-gt/-ge {number2}
{number1} -eq {number2} 相等
{number1} -ne {number2} 不相等
{number1} -lt {number2} 小于
{number1} -le {number2} 小于等于
{number1} -gt {number2} 大于
{number1} -ge {number2} 大于等于

文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-a/-e 文件是否存在
-b 文件存储在块存储设备
-d 是否是文件夹
-f 文件存在且是常规文件
-h/-L 文件是符号链接
-p 文件是否是使用 mkfifo 创建的命名管道
-r 是否可读(运行命令的用户)
-s 文件存在且不为空
-S 是否是 Socket 文件
-t fd (file descriptor) 是否在终端中打开
-w 是否可写(运行命令的用户)
-x 是否可执行(运行命令的用户)
-O 运行命令的用户是否是文件 Owner
-G 文件是否被运行命令的用户 Group 拥有
{file1} -nt {file2} file1 是否比 file2 更新
{file1} -nt {file2} file1 是否比 file2 更旧
{file1} -ef {file2} file1 是否是 file2 的硬链接

多表达式

1
2
{expr1} -o {expr2} 或
{expr1} -a {expr2} 且

路径及文件

1
2
3
4
5
6
7
8
9
10
11
# 提取文件名
$(basename "/path/to/file") # file

# 提取文件路径(非绝对路径)
$(dirname "/path/to/file") # /path/to

# 获取绝对路径
$(pwd)/$(dirname "./path/to/file")

# 脚本路径
BASE=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 定义数组
ARR=(a b c)

# ARR[@]: 数组转换为字符串
echo "${ARR[@]}"
a b c

# ARR[*]
echo "${ARR[*]}"
a b c

# 数组赋值
ANOTHER=("${ARR[@]}")

# 长度
echo ${#ARR[@]}
3

# 序号
for idx in "${!ARR[@]}"; do
echo "$idx"
done

[@] VS [*]

数组的 [@][*] 都是取所有元素,但是有所差别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 定义
$ ARR=(a b c)
$ A=("${ARR[@]}")
$ B=("${ARR[*]}")

# A
$ echo ${#A[@]}
3
$ echo ${A[@]}
a b c

# B
$ echo ${#B[@]}
1
$ echo ${B[@]}
a b c

布尔

1
2
3
4
FLAG=true
if [[ $FLAG == true ]]; then
# do something
fi

字符串

比较

1
[ "$v" = "value" ]

截取

1
2
3
4
5
$ s=abcd
# 截取区间是 [left, right]
$ echo ${s:1}
bcd
$ echo ${s:1:2}

切分

IFS=';' read -ra SLICES <<< "$IN"

1
2
3
4
IFS=';' read -ra ADDR <<< "$IN" # 按 ; 分割
for i in "${ADDR[@]}"; do
# process "$i"
done

替换

1
echo ${LINE//{old}/{new}}

循环

while

语法

1
2
3
4
while command
do
Statement(s) to be executed if command is true
done

示例

1
2
3
4
5
6
7
8
9
a=0
while [ $a -lt 10 ]
do
echo $a
a=`expr $a + 1`
done

# 等待 port
while ! lsof -i:8080; do echo "wait for server ready"; sleep 1; done

数组

1
2
3
4
5
6
7
8
9
10
# 命令行
$ countries=(china us); for country in ${countries[@]}; echo $country
china
us

# 脚本
countries=(china us)
for country in ${countries[@]}; do
echo $country
done

范围

1
2
3
4
5
6
7
8
9
10
11
12
13
$ for id in {1..3}; echo $id   # 1 2 3

# 脚本
for id in {1..3}; do
echo $id
done

# seq [首数] [增量] 尾数
$ seq 1 3 # [1, 3]
$ seq 3 # [1, 3]
$ seq 1 2 5 # 1, 3, 5

$ for i in `seq 3`; do echo $i; done # 1 2 3

case…esac

1
2
3
4
5
6
7
8
9
10
11
12
word=a
case $word in
a)
echo "a $word"
;;
b)
echo "b $word"
;;
*)
echo "* $word"
;;
esac

getopts

参数

1
2
3
4
5
6
# opts
:前缀 忽略错误
:后缀 参数后必须有值

# example
:abc:de: 忽略参数错误,-c、-e后必须有值

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
usage() {
cat <<EOF
Usage: $0 [-a] [-b name] msg
EOF
}

while getopts ":ab:Bc:" opt; do
case $opt in
a) echo "found -a" ; a="hello" ;;
b) echo "found -b and value is: $OPTARG" ;;
c) echo "found -c and value is: $OPTARG" ;;
*) usage ;;
esac
done

shift $(($OPTIND - 1))

数据存储对比

数据库

KV存储

服务 备注
Redis
HBase
Pegasus

关系型数据库

服务 备注
MySQL

时序数据库

服务 备注
TDengine
InfluxDB

查询引擎

  • Impala
  • Presto

其他

OLAP & OLTP

OLTP OLAP
特性 操作处理 信息处理
面向 事务 分析
用户 操作人员,底层管理人员 决策人员,高级管理人员
功能 日程操作处理 分析决策
DB设计 面向应用 面向主题
数据 当前的,最新的,细节的,二维的,分离的 历史的,聚焦的,多维的,集成的,统一的
存取 读/写数十条记录 读上百万条数据
工作单位 简单的事务 复杂的查询
用户数 上千个 上百万个
规模 GB TB
时效 具有时效性要求 时效要求不严格
主要应用 数据库 数据仓库
优先 事务吞吐量 查询吞吐量、响应时间

待分类

  • Impala
  • Druid
  • ClickHouse
  • Hive
  • Impala
  • InfluxDB

参考

mac usage

初始化

homebrew

1
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

node

参见 前端/node

工具

homebrew

commond line tools

1
2
3
4
5
6
7
8
9
10
# install
$ xcode-select --install

# delete
$ sudo rm -rf `xcode-select -p` # 一般会在文件夹 /Library/Developer/CommandLineTools 内

# Problems
## Can’t install the software because it is not currently available from the Software Update server.
# 手动下载
https://developer.apple.com/download/more/?=command%20line%20tools

问题

拷贝文件导致图标变灰无法访问

尝试 这里 无果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 仅适用文件夹
# 命令行输入如下内容
exitFun() {
echo "ERROR: failed, due to $1"
exit 1
}

fix() {
[ ! -d "${1}" ] && exitFun "target is not folder"
mv "${1}" "${1}_back"
mkdir "${1}"
mv "${1}_back"/* "${1}/"
mv "${1}_back"/.[^.]* "${1}/"
rmdir "${1}_back"
}

# 修复,输入 fix <文件夹目录> OR 输入 fix [拖拽文件夹至终端]
$ fix <path>