diff --git a/content/posts/react.md b/content/posts/react.md new file mode 100644 index 0000000..692918a --- /dev/null +++ b/content/posts/react.md @@ -0,0 +1,14 @@ ++++ +title = 'React' +date = 2024-03-14T19:42:12+08:00 +draft = false +tags = ['frontend', 'react'] ++++ + + + +* jsx:component返回的jsx,看起来像html,实际是javascript,通过babel将jsx编译成javascript,这些js最后通过dom操作生产html。jsx融合了html,css,js三个。 +* props:是属性,是上级component向下级component传递的只读的信息 +* state:使用useState()返回一个初始值v和一个function setV。useState是一个react Hook。根据当前state更新新的state需要用lambda function +* UI = 很多components = f(state) +* Npx create-react-app@5 travel-list 运行:npm install; npm start diff --git a/public/404.html b/public/404.html index 4dc7be2..da113b1 100644 --- a/public/404.html +++ b/public/404.html @@ -25,6 +25,8 @@ + + @@ -33,7 +35,7 @@ style="--bg: #faf8f1" lang="en-us" > - + - 404 Page not found - My New Hugo Site + 404 Page not found - greathongtu 的 Blog @@ -56,7 +58,7 @@ name="description" content="A personal blog" /> - + @@ -65,14 +67,14 @@ - + - + @@ -82,46 +84,26 @@ - - - - - - + - - - - - - - - - - - - - - - - - + + + + - - @@ -130,8 +112,8 @@
My New Hugo Sitegreathongtu 的 Blog
+ + + github + + + +
@@ -218,8 +216,8 @@ class="opaco mx-auto flex h-[4.5rem] max-w-3xl items-center px-8 text-[0.9em] opacity-60" >
- © 2023 - My New Hugo Site + © 2024 + greathongtu 的 Blog
Powered by Hugo️️ - + - My New Hugo Site + greathongtu 的 Blog @@ -56,7 +58,7 @@ name="description" content="A personal blog" /> - + @@ -65,14 +67,14 @@ - + - + @@ -82,52 +84,25 @@ - - + - - - - - - - - - - - - - - - - - - - - - + + + + - - - - @@ -137,8 +112,8 @@
My New Hugo Sitegreathongtu 的 Blog
+ + + github + + + +
@@ -236,11 +227,89 @@
-

Second

+

React

+ + React +
+ + + + + +
+ +

Cpp Concurrency

+ + Cpp Concurrency +
+ + + + + +
+ +

Map Reduce 的实现

+ + Map Reduce 的实现 +
+ + + + + +
+ +

Go 探究

+ + Go 探究 +
+ + + + + +
+ +

sql 总结

+ + sql 总结 +
+ + + + + +
+ +

Linux 网络I/O复用并发模型

+ + Linux 网络I/O复用并发模型 +
+ + + + + +
+ +

wsl 代理问题

Nov 17, 2023 - Second + wsl 代理问题
@@ -249,11 +318,11 @@

Second

-

My First Post

+

Vim 进阶

Nov 16, 2023 - My First Post + Vim 进阶
@@ -267,8 +336,8 @@

My First Post

class="opaco mx-auto flex h-[4.5rem] max-w-3xl items-center px-8 text-[0.9em] opacity-60" >
- © 2023 - My New Hugo Site + © 2024 + greathongtu 的 Blog
Powered by Hugo️️ - My New Hugo Site - https://example.org/ - Recent content on My New Hugo Site + greathongtu 的 Blog + http://localhost:1313/ + Recent content on greathongtu 的 Blog Hugo -- gohugo.io en-us - Wed, 15 Nov 2023 22:41:25 +0800 - - - Second - https://example.org/posts/second/ - Wed, 15 Nov 2023 22:41:25 +0800 - https://example.org/posts/second/ - Second One hello, this is second! -can you see the content? - - - My First Post - https://example.org/posts/my-first-post/ - Wed, 15 Nov 2023 22:21:08 +0800 - https://example.org/posts/my-first-post/ - Hello println!("helo"); fn out() -> String { "df".into() } this is the content in the first blog. -df + Thu, 14 Mar 2024 19:42:12 +0800 + + + React + http://localhost:1313/posts/react/ + Thu, 14 Mar 2024 19:42:12 +0800 + http://localhost:1313/posts/react/ + jsx:component返回的jsx,看起来像html,实际是javascript,通过babel将jsx编译成javascript,这些js最后通过dom操作生产html。jsx融合了html,css,js三个。 props:是属性,是上级component向下级component传递的只读的信息 state:使用useState()返回一个初始值v和一个function setV。useState是一个react Hook。根据当前state更新新的state需要用lambda function UI = 很多components = f(state) Npx create-react-app@5 travel-list 运行:npm install; npm start + + + Cpp Concurrency + http://localhost:1313/posts/cpp-concurrency/ + Fri, 08 Dec 2023 14:52:36 +0800 + http://localhost:1313/posts/cpp-concurrency/ + #include <iostream> #include <thread> void hello() { std::cout << "Hello Concurrent World\n"; } int main() { std::cout << "first main" << std::endl; std::thread t(hello); std::cout << "from main thread!" << std::endl; // t.join(); t.detach(); } 等待:join 不等:detach 如后台监控线程 精细化控制: condition variables and futures join之前发生exception怎么办呢:try catch. 更好的方式:RAII class thread_guard { std::thread &t; public: explicit thread_guard(std::thread &t_) : t(std::move(t_)) { if (!t.joinable()) { throw std::logic_error(“No thread”); } } ~thread_guard() { t.join(); } thread_guard(thread_guard const &) = delete; thread_guard &operator=(thread_guard const &) = delete; }; // usage std::thread t(my_func); thread_guard g(t); 向thread对象传参的时候要注意隐式类型转换: + + + Map Reduce 的实现 + http://localhost:1313/posts/map-reduce/ + Fri, 01 Dec 2023 20:24:26 +0800 + http://localhost:1313/posts/map-reduce/ + 数据结构设计 状态都存在 Coordinator 中, Worker 无状态 Task 结构 每次 Worker 向 Coordinator 请求 Task, type Task struct { Input string // Map 阶段的输入文件名 TaskPhase Phase // Worker眼中的阶段: Map/Reduce/Exit/Wait NReduce int // Reduce 的数量 Index int // 该 Task 在 Coordinator中的索引 Intermediates []string // Map生成的中间文件名 Output string // Reduce 生成的最终输出文件名 } Coordinator type Coordinator struct { TaskQueue chan *Task // 待处理的 Task 队列 TaskMetadatas map[int]*TaskMetadata // Coordinator 维护的 Task 元数据 Phase Phase // Coordinator 处于哪个阶段: Map/Reduce/Exit/Wait NReduce int InputFiles []string // Map 阶段的输入文件名 Intermediates [][]string mutex sync. + + + Go 探究 + http://localhost:1313/posts/go-design/ + Sun, 19 Nov 2023 01:29:52 +0800 + http://localhost:1313/posts/go-design/ + 数组 如果数组中元素的个数小于或者等于 4 个,那么所有的变量会直接在栈上初始化,如果数组元素大于 4 个,变量就会在静态存储区初始化然后拷贝到栈上。 为什么? slice []int []interface{} 切片提供了对数组中部分连续片段的引用,而作为数组的引用,我们可以在运行区间可以修改它的长度和范围。当切片底层的数组长度不足时就会触发扩容,切片指向的数组可能会发生变化,不过在上层看来切片是没有变化的,上层只需要与切片打交道不需要关心数组的变化。 追加和扩容 先确定切片大致容量 如果期望容量大于当前容量的两倍就会使用期望容量; 如果当前切片的长度小于 1024 就会将容量翻倍; 如果当前切片的长度大于 1024 就会每次增加 25% 的容量,直到新容量大于期望容量; 再根据切片中的元素大小对齐内存 切片拷贝 无论是编译期间拷贝还是运行时拷贝,两种拷贝方式都会通过 runtime.memmove 将整块内存的内容拷贝到目标的内存区域中: 大切片扩容或者复制时可能会发生大规模的内存拷贝,要注意。 map type hmap struct { count int flags uint8 B uint8 noverflow uint16 hash0 uint32 buckets unsafe.Pointer oldbuckets unsafe.Pointer nevacuate uintptr extra *mapextra } type mapextra struct { overflow *[]*bmap oldoverflow *[]*bmap nextOverflow *bmap } count 表示当前哈希表中的元素数量; B 表示当前哈希表持有的 buckets 数量,但是因为哈希表中桶的数量都 2 的倍数,所以该字段会存储对数,也就是 len(buckets) == 2^B; hash0 是哈希的种子,它能为哈希函数的结果引入随机性,这个值在创建哈希表时确定,并在调用哈希函数时作为参数传入; oldbuckets 是哈希在扩容时用于保存之前 buckets 的字段,它的大小是当前 buckets 的一半; 如上图所示哈希表 runtime. + + + sql 总结 + http://localhost:1313/posts/sql-summary/ + Fri, 17 Nov 2023 01:12:44 +0800 + http://localhost:1313/posts/sql-summary/ + mysql 执行流程 SQL语句 - 查询缓存 - 解析器(语法分析 语意分析) - 优化器(逻辑优化 物理优化) - 执行器 为什么说 B+ 树查找行记录,最多只需1~3次磁盘IO InnoDB 中页大小为16KB,一个键值对大概 8B+8B, 也就是一个页可以存1K个 KV pair,1K约等于1000个。 所以深度为3的 b+ 树索引可以维护 10^3 * 10^3 * 10^3 = 10 亿条记录。 正常每个节点不可能都填满了,所以一般 b+ 树高度在2到4 层。根节点在内存内,所以一般查找某一 KV 的行记录时只需1到3次磁盘IO InnoDB 关键特性 Insert Buffer 升级为 Change Buffer 对于非聚集索引的插入或更新操作,不是每次直接插入到索引页,而是先判断插入的非聚集索引页是否在缓冲池中,若在则直接插入;若不在,则先放入一个 Insert Buffer 对象中,然后再以一定的频率和情况进行 Insert Buffer 和辅助索引页子节点的 merge 操作。如此一来多个插入合并到一个操作中(因为在一个索引页中)。 条件: 1. 索引是辅助索引; 2. 索引不是唯一的。 Insert Buffer 是一个 B+ 树 Double Write Adaptive Hash Index Async IO Flush Neighbor Page 覆盖索引 覆盖索引(covering index ,或称为索引覆盖)即从非主键索引中就能查到的记录,而不需要查询主键索引中的记录,避免了回表的产生减少了树的搜索次数,显著提升性能 + + + Linux 网络I/O复用并发模型 + http://localhost:1313/posts/linux-io/ + Fri, 17 Nov 2023 01:04:58 +0800 + http://localhost:1313/posts/linux-io/ + 阻塞非阻塞 阻塞占用资源少,但是一个线程通过阻塞IO,只能处理一个请求。 非阻塞需要一直轮询,占用CPU资源大。 解决方法一:阻塞+多线程/多进程。 浪费资源 解决方法二:非阻塞忙轮询 while(true) { for i in 流[] { if(i has data) { read/ other process } } } 方法三:IO多路复用:既能阻塞等待,不浪费CPU资源,也能同一时刻监听多个IO请求的状态 select: while(true) { select(流[]); // 阻塞 max 1024个 for i in 流[] { if(i has data) { read/ other process } } } epoll: while(true) { 可处理的流 = epoll_wait(epoll_fd);// 阻塞 max `cat /proc/sys/fs/file-max` 个 for i in 可处理的流[] { read/ other process } } select/ poll select 实现多路复用的方式是,将已连接的 Socket 都放到一个文件描述符集合,然后调用 select 函数将文件描述符集合拷贝到内核里,让内核来检查是否有网络事件产生,检查的方式很粗暴,就是通过遍历文件描述符集合的方式,当检查到有事件产生后,将此 Socket 标记为可读或可写, 接着再把整个文件描述符集合拷贝回用户态里,然后用户态还需要再通过遍历的方法找到可读或可写的 Socket,然后再对其处理。 + + + wsl 代理问题 + http://localhost:1313/posts/wsl-network/ + Fri, 17 Nov 2023 00:41:53 +0800 + http://localhost:1313/posts/wsl-network/ + 问题 win11 使用 vs code 连接 wsl 的时候,一些 vs code 的拓展无法连接网络,报错: Failed to establish a socket connection to proxies: ["PROXY 127.0.0.1:7890"] 或者 go build 因为网络问题无法继续。 原因 Windows 开启 Clash 之后,Windows 系统代理一般被设置为 127.0.0.1:7890,VS Code 会继承这个代理设置,wsl 中的 vscode-server 也继承了这个代理设置。但是 wsl 在 127.0.0.1:7890 上并没有代理服务,导致 vscode-server 联网失败。所以 wsl 中正确设置 “http_proxy” 和 “https_proxy” 环境变量可以解决问题。 解决办法 在 wsl 中正确设置 path # ~/.bashrc # 获取 Host IP WINDOWS_IP=$(ip route | grep default | awk '{print $3}') PROXY_HTTP="http://${WINDOWS_IP}:7890" # 设置环境变量 export http_proxy="${PROXY_HTTP}" export https_proxy="${PROXY_HTTP}" # ~/. + + + Vim 进阶 + http://localhost:1313/posts/vim/ + Thu, 16 Nov 2023 14:39:47 +0800 + http://localhost:1313/posts/vim/ + Repeat using . and ; // use ' to revert ;, u to revert . var foo = "method("+argument1+","+argument2+")"; f+ to Move, s<space>+<space><Esc> to Change. use ; to repeat search and . to repeat change var foo = "method(" + argument1 + "," + argument2 + ")"; Dot Formula The Ideal: one keystroke to Move, one keystroke to Execute. Act, Repeat, Reverse Operator + Motion = Action (dw、guaw、gcap) Misc 如何在每行的最后加上分号? 先A; 然后就可以重复j. diff --git a/public/page/1/index.html b/public/page/1/index.html index 349f50c..0e4714b 100644 --- a/public/page/1/index.html +++ b/public/page/1/index.html @@ -1,10 +1,10 @@ - https://example.org/ - + http://localhost:1313/ + - + diff --git a/public/posts/cpp-concurrency/index.html b/public/posts/cpp-concurrency/index.html new file mode 100644 index 0000000..b4e54f8 --- /dev/null +++ b/public/posts/cpp-concurrency/index.html @@ -0,0 +1,649 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cpp Concurrency - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + +
+
+

Cpp Concurrency

+ + +
+ + + + + + +
+ +
+ +
#include <iostream>
+#include <thread>
+void hello() { std::cout << "Hello Concurrent World\n"; }
+int main() {
+  std::cout << "first main" << std::endl;
+  std::thread t(hello);
+  std::cout << "from main thread!" << std::endl;
+  // t.join();
+  t.detach();
+}
+

等待:join

+

不等:detach 如后台监控线程

+

精细化控制: condition variables and futures

+

join之前发生exception怎么办呢:try catch. 更好的方式:RAII

+
class thread_guard {
+  std::thread &t;
+
+public:
+  explicit thread_guard(std::thread &t_) : t(std::move(t_)) {
+    if (!t.joinable()) {
+      throw std::logic_error(No thread);
+    }
+  }
+  ~thread_guard() {
+    t.join(); 
+  }
+  thread_guard(thread_guard const &) = delete;
+  thread_guard &operator=(thread_guard const &) = delete;
+};
+
+// usage
+std::thread t(my_func);
+thread_guard g(t);
+

向thread对象传参的时候要注意隐式类型转换:

+
char buffer[1024];
+// dangling pointer alert!
+std::thread t(f,3,buffer);
+// Ok
+std::thread t(f,3,std::string(buffer));
+

thread对象的function的参数要引用的情况:

+
// widget_data 需要引用
+void update_data_for_widget(widget_id w, widget_data &data);
+void oops_again(widget_id w) {
+  widget_data data;
+  // 这里先拷贝w 和 data,然后传rvalues 给需要non-const reference的function
+  // compile error!
+  std::thread t(update_data_for_widget, w, data);
+  // 解决办法:
+  std::thread t(update_data_for_widget, w, std::ref(data));
+  display_status();
+  t.join();
+  process_widget_data(data);
+}
+

std::thread像std::bind一样

+
class X {
+public:
+  void do_lengthy_work();
+};
+X my_x;
+// 第三个参数开始就是成员函数的参数
+std::thread t(&X::do_lengthy_work, &my_x);
+

可以把unique_ptr move进参数。thread对象本身也是可以move不可以copy的

+
std::thread t1(some_function);
+std::thread t2 = std::thread(some_other_function);
+// terminate! you must explicitly wait for a thread to complete or detach it before destruction,
+t1 = std::move(t2);
+

用std::vector管理std::thread 可以在runtime创建动态数量的thread

+

thread id : std::thread::get_id() 或者 std::this_thread::get_id()

+

Sharing Data with mutex

+

If all shared data is read-only, there’s no problem。

+

std::mutex -> std::lock_guard -> std::scoped_lock

+

Don’t pass pointers and references to protected data outside the scope of the lock,

+

避免死锁的方法:

+
    +
  • lock the two mutexes in a fixed order
  • +
  • std::lock 同时锁多个mutex 而不会死锁
  • +
+
void swap(X &lhs, X &rhs) {
+  if (&lhs == &rhs)
+    return;
+  std::lock(lhs.m, rhs.m);
+  // 注意 adopt_lock
+  std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock);
+  std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock);
+  swap(lhs.some_detail, rhs.some_detail);
+}
+

用 scoped_lock 简化:

+
void swap(X &lhs, X &rhs) {
+  if (&lhs == &rhs)
+    return;
+  std::scoped_lock guard(lhs.m, rhs.m);
+  swap(lhs.some_detail, rhs.some_detail);
+}
+
    +
  • don’t wait for another thread if there’s a chance it’s waiting for you
  • +
  • AVOID NESTED LOCKS 已经有一个锁的时候就不要再acquire lock
  • +
  • AVOID CALLING USER-SUPPLIED CODE WHILE HOLDING A LOCK
  • +
  • USE A LOCK HIERARCHY: 从上层锁到下层
  • +
+

比lock_guard更灵活的 std::unique_lock

+

using std::unique_lock and std::defer_lock B, rather than std::lock_guard and std::adopt_lock. 更灵活但更耗空间,更慢。最好还是用scoped_lock。

+
void swap(X &lhs, X &rhs) {
+  if (&lhs == &rhs)
+    return;
+  std::unique_lock<std::mutex> lock_a(lhs.m, std::defer_lock);
+  std::unique_lock<std::mutex> lock_b(rhs.m, std::defer_lock);
+  std::lock(lock_a, lock_b);
+  swap(lhs.some_detail, rhs.some_detail);
+}
+

Transferring mutex ownership between scopes

+

因为std::unique_lock不拥有mutex,mutex的ownership可以在intance之间move

+

Locking at an appropriate granularity

+
void get_and_process_data() {
+  std::unique_lock<std::mutex> my_lock(the_mutex);
+  some_class data_to_process = get_next_data_chunk();
+  my_lock.unlock();
+  result_type result = process(data_to_process);
+  my_lock.lock();
+  write_result(data_to_process, result);
+}
+

appropriate granularity不仅指data的数量,还指锁的时长

+
class Y {
+private:
+  int some_detail;
+  mutable std::mutex m;
+  int get_detail() const {
+    std::lock_guard<std::mutex> lock_a(m);
+    return some_detail;
+  }
+
+public:
+  Y(int sd) : some_detail(sd) {}
+  friend bool operator==(Y const &lhs, Y const &rhs) {
+    if (&lhs == &rhs)
+      return true;
+    int const lhs_value = lhs.get_detail();
+    int const rhs_value = rhs.get_detail();
+    return lhs_value == rhs_value;
+  }
+};
+

if you don’t hold the required locks for the entire duration of an operation, you’re exposing yourself to race conditions.上面相等只能说明lhs在某一时间跟rhs在另外一个时间是相等。

+

除mutex 别的保护shared data的方法

+

One common case is where the shared data needs protection only from concurrent access while it’s being initialized, but after that no explicit synchronization is required(比如data创建之后就是read only的了)

+

Protecting shared data during initialization

+
    +
  • +

    单线程的时候可以Lazy initialization:看一下没创建的话再去创建。

    +
  • +
  • +

    naive加锁的多线程方法unnecessary serialization,性能太差

    +
  • +
  • +

    double checked locking 会race:

    +
  • +
+
void undefined_behaviour_with_double_checked_locking() {
+  if (!resource_ptr) { // read
+    std::lock_guard<std::mutex> lk(resource_mutex);
+    if (!resource_ptr) {
+      resource_ptr.reset(new some_resource); // write
+    }
+  }
+  resource_ptr->do_something();
+}
+

read 和 write 没有同步,会race。 +even if a thread sees the pointer written by another thread, it might not see the newly created instance of some_resource, resulting in the call to do_something() e operating on incorrect values.

+
    +
  • std::once_flag 和 std::call_once
  • +
+
std::shared_ptr<some_resource> resource_ptr;
+std::once_flag resource_flag;
+void init_resource() { resource_ptr.reset(new some_resource); }
+void foo() {
+  // the pointer will have been initialized by some thread (synchronizly) by the time std::call_once returns
+  // synchronization data 存在 once_flag 里
+  std::call_once(resource_flag, init_resource);
+  resource_ptr->do_something();
+}
+

除了通过function的方式,还可以被用在lazy initialization of class +members

+
class X {
+private:
+  connection_info connection_details;
+  connection_handle connection;
+  std::once_flag connection_init_flag;
+  void open_connection() {
+    connection = connection_manager.open(connection_details);
+  }
+
+public:
+  X(connection_info const &connection_details_)
+      : connection_details(connection_details_) {}
+  void send_data(data_packet const &data) {
+    std::call_once(connection_init_flag, &X::open_connection, this);
+    connection.send_data(data);
+  }
+  data_packet receive_data() {
+    std::call_once(connection_init_flag, &X::open_connection, this);
+    return connection.receive_data();
+  }
+};
+

上面的例子中 send_data() 和 receive_data() 两者中更先被call的完成init.

+

像mutex一样,once_flag也不能被copy、move

+
    +
  • local static variables的初始化occur the first time control passes through its declaration。多线程环境下这就会race,c++11之后的compiler就没问题了。所以可以通过这种方式替代call_once() where a single global instance is required
  • +
+

Protecting rarely updated data structures

+

比如DNS信息:读写锁:std::shared_mutex 和 std::shared_timed_mutex.

+

通过std::shared_lockstd::shared_mutex 获得shared access; +通过std::lock_guardstd::shared_mutex 或者std::unique_lockstd::shared_mutex 获得 exclusive access

+

(mutable 关键词:在const 函数中也可以修改mutable变量,所以适合用在mutex上,因为mutex要lock、unlock)

+

Recursive locking

+

重复锁同一个mutex是UB.

+

std::recursive_mutex:可重复锁,(但要记得锁了几次最后还得解锁几次,通过RAII就行)

+

Most of the time, if you think you want a recursive mutex, you probably need to change your design instead.

+

common use: 每一个public成员函数都lock了,一个public成员函数会call 另一个. +But such usage is not recommended because it can lead to sloppy thinking and bad design. The class invariants are typically broken.

+

Better to extract a new private member function which does not lock the mutex

+

Synchronizing concurrent operations

+

上面讲的是保护data, 这里讲同步action:condition variables 和 futures。还有latches and barriers

+
    +
  • Waiting for an event
  • +
  • Waiting for one-off events with futures
  • +
  • Waiting with a time limit
  • +
  • Using the synchronization of operations to simplify code
  • +
+

condition_variables

+

为什么要条件变量:等event发生,要么不停地检查flag浪费资源;要么std::this_thread::sleep_for() 但是不好确定到底睡多久。

+
std::mutex mut;
+std::queue<data_chunk> data_queue;
+std::condition_variable data_cond;
+void data_preparation_thread() {
+  while (more_data_to_prepare()) {
+    data_chunk const data = prepare_data();
+    {
+      std::lock_guard<std::mutex> lk(mut);
+      data_queue.push(data);
+    }
+    data_cond.notify_one();
+  }
+}
+void data_processing_thread() {
+  while (true) {
+    std::unique_lock<std::mutex> lk(mut);
+    data_cond.wait(lk, [] { return !data_queue.empty(); });
+    data_chunk data = data_queue.front();
+    data_queue.pop();
+    lk.unlock();
+    process(data);
+    if (is_last_chunk(data))
+      break;
+  }
+}
+

上面用unique_lock 搭配conditon_variable 是因为the waiting thread must unlock the mutex while it’s waiting and lock it again afterward, and std::lock_guard doesn’t provide that flexibility

+

futures

+

std::future<> 和 std::shared_future<>

+

Returning values from background tasks

+

use std::async to start an asynchronous task,returns a std::future object。 call get() on the future to block until returning the value.

+
std::future<int> the_answer = std::async(find_the_answer_to_ltuae);
+do_other_stuff();
+std::cout << "The answer is " << the_answer.get() << std::endl;
+
struct X {
+  void foo(int, std::string const &);
+  std::string bar(std::string const &);
+};
+X x;
+// Calls p->foo(42,"hello") where p is &x
+auto f1 = std::async(&X::foo, &x, 42, "hello");
+// Calls tmpx.bar("goodbye") where tmpx is a copy of x
+auto f2 = std::async(&X::bar, x, "goodbye");
+struct Y {
+  double operator()(double);
+};
+

an additional parameter to std::async before the function to call: std::launch, 要么是std::launch::deferred 等到wait() 或者get()被call的时候才调用;要么是std::launch::async 开一个新线程。或者std::launch::deferred | std::launch::async 代表由实现自己选择(默认)。

+

Associating a task with a future

+

wrapping the task in an instance of the std::packaged_task<> class template or by writing code to explicitly set the values using the std::promise<> class template.

+

当std::packaged_task<> 对象被invoke的时候,它call对应的function or callable object,让future ready. Can be used as a building block for thread pools

+
std::mutex m;
+std::deque<std::packaged_task<void()>> tasks;
+bool gui_shutdown_message_received();
+void get_and_process_gui_message();
+void gui_thread() {
+  while (!gui_shutdown_message_received()) {
+    get_and_process_gui_message();
+    std::packaged_task<void()> task;
+    {
+      std::lock_guard<std::mutex> lk(m);
+      if (tasks.empty())
+        continue;
+      task = std::move(tasks.front());
+      tasks.pop_front();
+    }
+    task();
+  }
+}
+std::thread gui_bg_thread(gui_thread);
+template <typename Func> std::future<void> post_task_for_gui_thread(Func f) {
+  std::packaged_task<void()> task(f);
+  std::future<void> res = task.get_future();
+  std::lock_guard<std::mutex> lk(m);
+  tasks.push_back(std::move(task));
+  return res;
+}
+

std::packaged_task wraps a function or other callable object

+

promises

+

tasks that can’t be expressed as a simple function call or those tasks where the result may come from more than one place, 第三种创建future的方式:std::promise

+

std::promise/std::future pair:通过std::promise的get_future()成员函数获得std::future object。std::promise 通过set_value()设置value让 std::future object ready,future object就可以读到。

+

如果destroy the std::promise without setting a value: exception

+
void process_connections(connection_set &connections) {
+  while (!done(connections)) {
+    for (connection_iterator connection = connections.begin(),
+                             end = connections.end();
+         connection != end; ++connection) {
+      if (connection->has_incoming_data()) {
+        data_packet data = connection->incoming();
+        std::promise<payload_type> &p = connection->get_promise(data.id);
+        p.set_value(data.payload);
+      }
+      if (connection->has_outgoing_data()) {
+        outgoing_packet data = connection->top_of_outgoing_queue();
+        connection->send(data.payload);
+        data.promise.set_value(true);
+      }
+    }
+  }
+}
+

Saving an exception for the future

+

future.get() 会返回异常而不是value 当发生异常的时候

+
extern std::promise<double> some_promise;
+try {
+  some_promise.set_value(calculate_value());
+} catch (...) {
+  some_promise.set_exception(std::current_exception());
+}
+// 如果知道type of the exception,最好用这个而不是try catch
+some_promise.set_exception(std::make_exception_ptr(std::logic_error("foo ")));
+

Waiting from multiple threads

+

用std::shared_future, multiple threads can wait for the same event

+
// implicit transfer of ownership
+std::shared_future<std::string> sf(some_promise.get_future());
+// share() 方法将future变为shared_future
+auto sf = p.get_future().share();
+

Waiting with a time limit

+

memory order and atomic

+

memory order是线程之间进行顺序绑定用的。

+

release sequence 指一个release 可以和后面的很多个acquire 同步

+

如果acquire操作看见release fence后面的store的结果,那fence就和acquire同步;如果acquire fence后面的load看见release操作的结果,那release和fence同步。

+

Release fence可以防止fence前的内存操作重排到fence后的任意store之后,即阻止loadstore重排和storestore重排。

+

acquire fence可以防止fence后的内存操作重排到fence前的任意load之前,即阻止loadload重排和loadstore重排

+

std::atomic_thread_fence(memory_order_acq_rel)和std::atomic_thread_fence(memory_order_seq_cst)都是full fence。

+

基于atomic_thread_fence(外加一个任意序的原子变量操作)的同步和基于原子操作的同步很类似,比如最常用的,都可以形成release acquire语义,但是从上面的描述可以看出,fence的效果要比基于原子变量的效果更强,在weak memory order平台的开销也更大。

+

以release为例,对于基于原子变量的release opration,仅仅是阻止前面的内存操作重排到该release opration之后,而release fence则是阻止重排到fence之后的任意store operation之后。

+

因为fence的同步效果和原子操作上的同步效果比较相似,可以互相组合,自然的,使用fence的同步会有三种情况,
fence - atomic同步,fence - fence同步和atomic-fence -同步。

+

If a non-atomic operation is sequenced before an atomic operation, and that atomic operation happens before an operation in another thread, the non-atomic operation also happens before that operation in the other thread. 参考代码:5.3.6的 listing5.13 +所以mutex的实现的基础就是这个:lock()是acquire,unlock是release,所以unlock之前的操作在unlock之前,也就在下一个thread的lock之前。

+

原子变量实现自旋锁:https://blog.51cto.com/quantfabric/2581173 相比mutex,是busy waiting而不是sleep waiting,不会使线程阻塞;少了用户态内核态的切换。

+

无锁编程性能并不一定更好,比如cache受影响。

+

原子变量的底层实现可能会使用mutex。

+
+ + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + diff --git a/public/posts/go-design/index.html b/public/posts/go-design/index.html new file mode 100644 index 0000000..bc4311c --- /dev/null +++ b/public/posts/go-design/index.html @@ -0,0 +1,575 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Go 探究 - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + +
+
+

Go 探究

+ + +
+ + + + + + +
+ +
+ +

数组

+

如果数组中元素的个数小于或者等于 4 个,那么所有的变量会直接在栈上初始化,如果数组元素大于 4 个,变量就会在静态存储区初始化然后拷贝到栈上。 为什么?

+

slice

+
[]int
+[]interface{}
+

slice

+

切片提供了对数组中部分连续片段的引用,而作为数组的引用,我们可以在运行区间可以修改它的长度和范围。当切片底层的数组长度不足时就会触发扩容,切片指向的数组可能会发生变化,不过在上层看来切片是没有变化的,上层只需要与切片打交道不需要关心数组的变化。

+

追加和扩容

+
    +
  • 先确定切片大致容量 +
      +
    1. 如果期望容量大于当前容量的两倍就会使用期望容量;
    2. +
    3. 如果当前切片的长度小于 1024 就会将容量翻倍;
    4. +
    5. 如果当前切片的长度大于 1024 就会每次增加 25% 的容量,直到新容量大于期望容量;
    6. +
    +
  • +
  • 再根据切片中的元素大小对齐内存
  • +
+

切片拷贝

+

无论是编译期间拷贝还是运行时拷贝,两种拷贝方式都会通过 runtime.memmove 将整块内存的内容拷贝到目标的内存区域中: +slice

+

大切片扩容或者复制时可能会发生大规模的内存拷贝,要注意。

+

map

+
type hmap struct {
+	count     int
+	flags     uint8
+	B         uint8
+	noverflow uint16
+	hash0     uint32
+
+	buckets    unsafe.Pointer
+	oldbuckets unsafe.Pointer
+	nevacuate  uintptr
+
+	extra *mapextra
+}
+
+type mapextra struct {
+	overflow    *[]*bmap
+	oldoverflow *[]*bmap
+	nextOverflow *bmap
+}
+
    +
  • count 表示当前哈希表中的元素数量;
  • +
  • B 表示当前哈希表持有的 buckets 数量,但是因为哈希表中桶的数量都 2 的倍数,所以该字段会存储对数,也就是 len(buckets) == 2^B;
  • +
  • hash0 是哈希的种子,它能为哈希函数的结果引入随机性,这个值在创建哈希表时确定,并在调用哈希函数时作为参数传入;
  • +
  • oldbuckets 是哈希在扩容时用于保存之前 buckets 的字段,它的大小是当前 buckets 的一半;
  • +
+

map

+

如上图所示哈希表 runtime.hmap 的桶是 runtime.bmap。每一个 runtime.bmap 都能存储 8 个键值对,当哈希表中存储的数据过多,单个桶已经装满时就会使用 extra.nextOverflow 这个bmap 存储溢出的数据。

+
type bmap struct {
+    topbits  [8]uint8
+    // 以下字段在运行时也是通过计算内存地址的方式访问的
+    keys     [8]keytype
+    values   [8]valuetype
+    pad      uintptr
+    overflow uintptr
+}
+

map 初始化

+
    +
  1. 字面量 +
      +
    • 当哈希表中的元素数量少于或者等于 25 个时:
    • +
    +
    hash := make(map[string]int, 3)
    +hash["1"] = 2
    +hash["3"] = 4
    +hash["5"] = 6
    +
      +
    • 元素的数量超过了 25 个: 编译器会创建两个数组分别存储键和值,这些键值对会通过 for 循环加入哈希:
    • +
    +
  2. +
  3. 运行时 +
      +
    • 当创建的哈希被分配到栈上并且其容量小于 BUCKETSIZE = 8 时,Go 语言在编译阶段会快速初始化哈希,这也是编译器对小容量的哈希做的优化
    • +
    • 除此之外,只要我们使用 make 创建哈希,最后都调用了 runtime.makemap: +
        +
      1. 计算哈希占用的内存是否溢出或者超出能分配的最大值;
      2. +
      3. 调用 runtime.fastrand 获取一个随机的哈希种子;
      4. +
      5. 根据传入的 hint 计算出需要的最小需要的桶的数量;
      6. +
      7. 使用 runtime.makeBucketArray 创建用于保存桶的数组: 当桶的数量小于 2^4 时不创建溢出桶;否则创建 2^(B-4) 个溢出桶。(正常桶和溢出桶在内存中的存储空间是连续的)
      8. +
      +
    • +
    +
  4. +
+

map 读写

+
    +
  • 读: +用于选择桶序号的是哈希的最低几位,而用于加速访问的是哈希的高 8 位,这种设计能够减少同一个桶中有大量相等 tophash 的概率影响性能。 +map +注意:哈希表扩容并不是原子过程
  • +
  • 写: +overflow +
      +
    • 如果当前桶已经满了,哈希会调用 runtime.hmap.newoverflow 创建新桶或者使用 runtime.hmap 预先在 noverflow 中创建好的桶来保存数据,新创建的桶不仅会被追加到已有桶的末尾,还会增加哈希表的 noverflow 计数器。
    • +
    • 如果当前键值对在哈希中不存在,哈希会为新键值对规划存储的内存地址
    • +
    +
  • +
+

扩容

+

runtime.mapassign 函数会在以下两种情况发生时触发哈希的扩容:

+
    +
  • 装载因子已经超过 6.5;
  • +
  • 哈希使用了太多溢出桶;
  • +
+

根据触发的条件不同扩容的方式分成两种,如果这次扩容是溢出的桶太多导致的,那么这次扩容就是等量扩容 sameSizeGrow, 通过复用已有的哈希扩容机制解决缓慢的内存泄露问题,一旦哈希中出现了过多的溢出桶,它会创建新桶保存数据,垃圾回收会清理老的溢出桶并释放内存。

+

hashgrow

+
    +
  • 扩容期间获取键值对的逻辑: 当哈希表的 oldbuckets 存在时,会先定位到旧桶并在该桶没有被分流时从中获取键值对。
  • +
  • 扩容期间写入/删除值:触发 runtime.growWork 增量拷贝哈希表中的内容:
  • +
+

string

+
// 在运行时都会使用如下的 reflect.StringHeader 表示
+type StringHeader struct {
+	Data uintptr
+	Len  int
+}
+

字符串是一个只读的切片类型。所有在字符串上的写入操作都是通过拷贝实现的。 +

+

反引号声明的字符串在遇到需要手写 JSON 的场景下非常方便

+
json := `{"author": "draven", "tags": ["golang"]}`
+

字符串拼接

+

使用 + 符号调用了 addstr 函数:

+
    +
  • 如果字符串数量小于或者等于 5 个,那么会调用 concatstring{2,3,4,5} 等一系列函数;最终调用 runtime.concatstrings
  • +
  • 如果超过 5 个,那么会选择 runtime.concatstrings 传入一个数组切片;
  • +
+
func concatstrings(buf *tmpBuf, a []string) string {
+	idx := 0
+	l := 0
+	count := 0
+	for i, x := range a {
+		n := len(x)
+		if n == 0 {
+			continue
+		}
+		l += n
+		count++
+		idx = i
+	}
+	if count == 0 {
+		return ""
+	}
+	if count == 1 && (buf != nil || !stringDataOnStack(a[idx])) {
+		return a[idx]
+	}
+	s, b := rawstringtmp(buf, l)
+	for _, x := range a {
+		copy(b, x)
+		b = b[len(x):]
+	}
+	return s
+}
+
    +
  • +

    如果非空字符串的数量为 1 并且当前的字符串不在栈上,就可以直接返回该字符串,不需要做出额外操作 +string

    +
  • +
  • +

    但正常情况下,运行时会调用 copy 将输入的多个字符串拷贝到目标字符串所在的内存空间。新的字符串是一片新的内存空间,与原来的字符串也没有任何关联,一旦需要拼接的字符串非常大,拷贝带来的性能损失是无法忽略的。

    +
  • +
+

类型转换

+

string 和 []byte 之间的转换:

+
    +
  1. string(bytes): 使用 runtime.slicebytetostring 函数:先处理两种比较常见的情况,也就是长度为 0 或者 1 的字节数组;处理过后会根据传入的缓冲区大小决定是否需要为新字符串分配一片内存空间,最后通过 runtime.memmove 将原 []byte 中的字节全部复制到新的内存空间中。
  2. +
  3. string to []byte: 使用 runtime.stringtoslicebyte 函数: +
      +
    • 当传入缓冲区时,它会使用传入的缓冲区存储 []byte;
    • +
    • 当没有传入缓冲区时,运行时会调用 runtime.rawbyteslice 创建新的字节切片并将字符串中的内容拷贝过去;
    • +
    +
  4. +
+

string 小结

+

在做拼接和类型转换等操作时一定要注意性能的损耗,遇到需要极致性能的场景一定要尽量减少类型转换的次数。

+

function call

+

c-function-call

+
    +
  • 六个以及六个以下的参数会按照顺序分别使用 edi、esi、edx、ecx、r8d 和 r9d 六个寄存器传递;
  • +
  • 六个以上的参数会使用栈传递,函数的参数会以从右到左的顺序依次存入栈中;
  • +
+

而函数的返回值是通过 eax 寄存器进行传递的,由于只使用一个寄存器存储返回值,所以 C 语言的函数不能同时返回多个值。

+

go-function-call

+
    +
  • CPU 访问栈的开销比访问寄存器高几十倍;
  • +
  • C 需要单独处理函数参数过多的情况;
  • +
  • Go 不需要考虑超过寄存器数量的参数应该如何传递;
  • +
  • Go 不需要考虑不同架构上的寄存器差异;
  • +
  • Go 函数入参和出参的内存空间需要在栈上进行分配;
  • +
+

参数传递

+

Go 语言选择了传值的方式,无论是传递基本类型、结构体还是指针,都会对传递的参数进行拷贝

+

接口

+

Go 在接口中我们只能定义方法签名,不能包含成员变量. 接口的实现都是隐式的

+
type error interface {
+	Error() string
+}
+
+ + + + + + + + + + + + + + + + + + + +
结构体实现接口结构体指针实现接口
结构体初始化变量通过不通过
结构体指针初始化变量通过通过
+
type Cat struct {}
+type Duck interface { ... }
+
+func (c  Cat) Quack {}  // 使用结构体实现接口
+func (c *Cat) Quack {}  // 使用结构体指针实现接口
+
+var d Duck = Cat{}      // 使用结构体初始化变量
+var d Duck = &Cat{}     // 使用结构体指针初始化变量
+

方法的接受者是结构体,而初始化的变量是结构体指针之所以可以:作为指针的 &Cat{} 变量能够隐式地获取到指向的结构体

+

Go 语言的接口类型不是任意类型

+
package main
+
+type TestStruct struct{}
+
+func NilOrNot(v interface{}) bool {
+	return v == nil
+}
+
+func main() {
+	var s *TestStruct
+	fmt.Println(s == nil)      // #=> true
+	fmt.Println(NilOrNot(s))   // #=> false
+}
+

原因: 调用 NilOrNot 函数时发生了隐式类型转换,转换后的变量不仅包含转换前的变量,还包含变量的类型信息 TestStruct

+

数据结构

+
    +
  • 不包含任何方法的 interface{} 类型 eface:只包含指向底层数据和类型的两个指针。所以 Go 语言的任意类型都可以转换成 interface{} +
    type eface struct { // 16 字节
    +    _type *_type
    +    data  unsafe.Pointer
    +}
    +
  • +
  • runtime.iface: +
    type iface struct { // 16 字节
    +    tab  *itab
    +    data unsafe.Pointer
    +}
    +
  • +
+

itab 和 _type 的区别:

+
    +
  • _type: Go 语言类型的运行时表示,包含类型的大小、哈希、对齐以及种类等。
  • +
  • itab 32 字节,包含用于动态派发的虚函数表 fun [1]uintptr
  • +
+

使用结构体实现接口带来的开销会大于使用指针实现,而动态派发在结构体上的表现非常差,这也提醒我们应当尽量避免使用结构体类型实现接口。

+

reflect

+

反射修改变量:

+
func main() {
+	i := 1
+	v := reflect.ValueOf(&i)
+	v.Elem().SetInt(10)
+	fmt.Println(i)
+}
+
+$ go run reflect.go
+10
+
+ + + +
+ + go + +
+ + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + diff --git a/public/posts/index.html b/public/posts/index.html index 4bde302..ede9dfe 100644 --- a/public/posts/index.html +++ b/public/posts/index.html @@ -25,6 +25,8 @@ + + @@ -33,7 +35,7 @@ style="--bg: #faf8f1" lang="en-us" > - + - Posts - My New Hugo Site + Posts - greathongtu 的 Blog @@ -56,7 +58,7 @@ name="description" content="A personal blog" /> - + @@ -65,14 +67,14 @@ - + - + @@ -82,52 +84,25 @@ - - + - - - - - - - - - - - - - - - - - - - - - + + + + - - - - @@ -137,8 +112,8 @@
My New Hugo Sitegreathongtu 的 Blog
+ + + github + + + +
@@ -229,11 +220,89 @@
-

Second

+

React

+ + React +
+ + + + + +
+ +

Cpp Concurrency

+ + Cpp Concurrency +
+ + + + + +
+ +

Map Reduce 的实现

+ + Map Reduce 的实现 +
+ + + + + +
+ +

Go 探究

+ + Go 探究 +
+ + + + + +
+ +

sql 总结

+ + sql 总结 +
+ + + + + +
+ +

Linux 网络I/O复用并发模型

+ + Linux 网络I/O复用并发模型 +
+ + + + + +
+ +

wsl 代理问题

Nov 17, 2023 - Second + wsl 代理问题
@@ -242,11 +311,11 @@

Second

-

My First Post

+

Vim 进阶

Nov 16, 2023 - My First Post + Vim 进阶
@@ -260,8 +329,8 @@

My First Post

class="opaco mx-auto flex h-[4.5rem] max-w-3xl items-center px-8 text-[0.9em] opacity-60" >
- © 2023 - My New Hugo Site + © 2024 + greathongtu 的 Blog
Powered by Hugo️️ - Posts on My New Hugo Site - https://example.org/posts/ - Recent content in Posts on My New Hugo Site + Posts on greathongtu 的 Blog + http://localhost:1313/posts/ + Recent content in Posts on greathongtu 的 Blog Hugo -- gohugo.io en-us - Wed, 15 Nov 2023 22:41:25 +0800 - - - Second - https://example.org/posts/second/ - Wed, 15 Nov 2023 22:41:25 +0800 - https://example.org/posts/second/ - Second One hello, this is second! -can you see the content? - - - My First Post - https://example.org/posts/my-first-post/ - Wed, 15 Nov 2023 22:21:08 +0800 - https://example.org/posts/my-first-post/ - Hello println!(&#34;helo&#34;); fn out() -&gt; String { &#34;df&#34;.into() } this is the content in the first blog. -df + Thu, 14 Mar 2024 19:42:12 +0800 + + + React + http://localhost:1313/posts/react/ + Thu, 14 Mar 2024 19:42:12 +0800 + http://localhost:1313/posts/react/ + jsx:component返回的jsx,看起来像html,实际是javascript,通过babel将jsx编译成javascript,这些js最后通过dom操作生产html。jsx融合了html,css,js三个。 props:是属性,是上级component向下级component传递的只读的信息 state:使用useState()返回一个初始值v和一个function setV。useState是一个react Hook。根据当前state更新新的state需要用lambda function UI = 很多components = f(state) Npx create-react-app@5 travel-list 运行:npm start + + + Cpp Concurrency + http://localhost:1313/posts/cpp-concurrency/ + Fri, 08 Dec 2023 14:52:36 +0800 + http://localhost:1313/posts/cpp-concurrency/ + #include &lt;iostream&gt; #include &lt;thread&gt; void hello() { std::cout &lt;&lt; &#34;Hello Concurrent World\n&#34;; } int main() { std::cout &lt;&lt; &#34;first main&#34; &lt;&lt; std::endl; std::thread t(hello); std::cout &lt;&lt; &#34;from main thread!&#34; &lt;&lt; std::endl; // t.join(); t.detach(); } 等待:join 不等:detach 如后台监控线程 精细化控制: condition variables and futures join之前发生exception怎么办呢:try catch. 更好的方式:RAII class thread_guard { std::thread &amp;t; public: explicit thread_guard(std::thread &amp;t_) : t(std::move(t_)) { if (!t.joinable()) { throw std::logic_error(“No thread”); } } ~thread_guard() { t.join(); } thread_guard(thread_guard const &amp;) = delete; thread_guard &amp;operator=(thread_guard const &amp;) = delete; }; // usage std::thread t(my_func); thread_guard g(t); 向thread对象传参的时候要注意隐式类型转换: + + + Map Reduce 的实现 + http://localhost:1313/posts/map-reduce/ + Fri, 01 Dec 2023 20:24:26 +0800 + http://localhost:1313/posts/map-reduce/ + 数据结构设计 状态都存在 Coordinator 中, Worker 无状态 Task 结构 每次 Worker 向 Coordinator 请求 Task, type Task struct { Input string // Map 阶段的输入文件名 TaskPhase Phase // Worker眼中的阶段: Map/Reduce/Exit/Wait NReduce int // Reduce 的数量 Index int // 该 Task 在 Coordinator中的索引 Intermediates []string // Map生成的中间文件名 Output string // Reduce 生成的最终输出文件名 } Coordinator type Coordinator struct { TaskQueue chan *Task // 待处理的 Task 队列 TaskMetadatas map[int]*TaskMetadata // Coordinator 维护的 Task 元数据 Phase Phase // Coordinator 处于哪个阶段: Map/Reduce/Exit/Wait NReduce int InputFiles []string // Map 阶段的输入文件名 Intermediates [][]string mutex sync. + + + Go 探究 + http://localhost:1313/posts/go-design/ + Sun, 19 Nov 2023 01:29:52 +0800 + http://localhost:1313/posts/go-design/ + 数组 如果数组中元素的个数小于或者等于 4 个,那么所有的变量会直接在栈上初始化,如果数组元素大于 4 个,变量就会在静态存储区初始化然后拷贝到栈上。 为什么? slice []int []interface{} 切片提供了对数组中部分连续片段的引用,而作为数组的引用,我们可以在运行区间可以修改它的长度和范围。当切片底层的数组长度不足时就会触发扩容,切片指向的数组可能会发生变化,不过在上层看来切片是没有变化的,上层只需要与切片打交道不需要关心数组的变化。 追加和扩容 先确定切片大致容量 如果期望容量大于当前容量的两倍就会使用期望容量; 如果当前切片的长度小于 1024 就会将容量翻倍; 如果当前切片的长度大于 1024 就会每次增加 25% 的容量,直到新容量大于期望容量; 再根据切片中的元素大小对齐内存 切片拷贝 无论是编译期间拷贝还是运行时拷贝,两种拷贝方式都会通过 runtime.memmove 将整块内存的内容拷贝到目标的内存区域中: 大切片扩容或者复制时可能会发生大规模的内存拷贝,要注意。 map type hmap struct { count int flags uint8 B uint8 noverflow uint16 hash0 uint32 buckets unsafe.Pointer oldbuckets unsafe.Pointer nevacuate uintptr extra *mapextra } type mapextra struct { overflow *[]*bmap oldoverflow *[]*bmap nextOverflow *bmap } count 表示当前哈希表中的元素数量; B 表示当前哈希表持有的 buckets 数量,但是因为哈希表中桶的数量都 2 的倍数,所以该字段会存储对数,也就是 len(buckets) == 2^B; hash0 是哈希的种子,它能为哈希函数的结果引入随机性,这个值在创建哈希表时确定,并在调用哈希函数时作为参数传入; oldbuckets 是哈希在扩容时用于保存之前 buckets 的字段,它的大小是当前 buckets 的一半; 如上图所示哈希表 runtime. + + + sql 总结 + http://localhost:1313/posts/sql-summary/ + Fri, 17 Nov 2023 01:12:44 +0800 + http://localhost:1313/posts/sql-summary/ + mysql 执行流程 SQL语句 - 查询缓存 - 解析器(语法分析 语意分析) - 优化器(逻辑优化 物理优化) - 执行器 为什么说 B+ 树查找行记录,最多只需1~3次磁盘IO InnoDB 中页大小为16KB,一个键值对大概 8B+8B, 也就是一个页可以存1K个 KV pair,1K约等于1000个。 所以深度为3的 b+ 树索引可以维护 10^3 * 10^3 * 10^3 = 10 亿条记录。 正常每个节点不可能都填满了,所以一般 b+ 树高度在2到4 层。根节点在内存内,所以一般查找某一 KV 的行记录时只需1到3次磁盘IO InnoDB 关键特性 Insert Buffer 升级为 Change Buffer 对于非聚集索引的插入或更新操作,不是每次直接插入到索引页,而是先判断插入的非聚集索引页是否在缓冲池中,若在则直接插入;若不在,则先放入一个 Insert Buffer 对象中,然后再以一定的频率和情况进行 Insert Buffer 和辅助索引页子节点的 merge 操作。如此一来多个插入合并到一个操作中(因为在一个索引页中)。 条件: 1. 索引是辅助索引; 2. 索引不是唯一的。 Insert Buffer 是一个 B+ 树 Double Write Adaptive Hash Index Async IO Flush Neighbor Page 覆盖索引 覆盖索引(covering index ,或称为索引覆盖)即从非主键索引中就能查到的记录,而不需要查询主键索引中的记录,避免了回表的产生减少了树的搜索次数,显著提升性能 + + + Linux 网络I/O复用并发模型 + http://localhost:1313/posts/linux-io/ + Fri, 17 Nov 2023 01:04:58 +0800 + http://localhost:1313/posts/linux-io/ + 阻塞非阻塞 阻塞占用资源少,但是一个线程通过阻塞IO,只能处理一个请求。 非阻塞需要一直轮询,占用CPU资源大。 解决方法一:阻塞+多线程/多进程。 浪费资源 解决方法二:非阻塞忙轮询 while(true) { for i in 流[] { if(i has data) { read/ other process } } } 方法三:IO多路复用:既能阻塞等待,不浪费CPU资源,也能同一时刻监听多个IO请求的状态 select: while(true) { select(流[]); // 阻塞 max 1024个 for i in 流[] { if(i has data) { read/ other process } } } epoll: while(true) { 可处理的流 = epoll_wait(epoll_fd);// 阻塞 max `cat /proc/sys/fs/file-max` 个 for i in 可处理的流[] { read/ other process } } select/ poll select 实现多路复用的方式是,将已连接的 Socket 都放到一个文件描述符集合,然后调用 select 函数将文件描述符集合拷贝到内核里,让内核来检查是否有网络事件产生,检查的方式很粗暴,就是通过遍历文件描述符集合的方式,当检查到有事件产生后,将此 Socket 标记为可读或可写, 接着再把整个文件描述符集合拷贝回用户态里,然后用户态还需要再通过遍历的方法找到可读或可写的 Socket,然后再对其处理。 + + + wsl 代理问题 + http://localhost:1313/posts/wsl-network/ + Fri, 17 Nov 2023 00:41:53 +0800 + http://localhost:1313/posts/wsl-network/ + 问题 win11 使用 vs code 连接 wsl 的时候,一些 vs code 的拓展无法连接网络,报错: Failed to establish a socket connection to proxies: [&quot;PROXY 127.0.0.1:7890&quot;] 或者 go build 因为网络问题无法继续。 原因 Windows 开启 Clash 之后,Windows 系统代理一般被设置为 127.0.0.1:7890,VS Code 会继承这个代理设置,wsl 中的 vscode-server 也继承了这个代理设置。但是 wsl 在 127.0.0.1:7890 上并没有代理服务,导致 vscode-server 联网失败。所以 wsl 中正确设置 “http_proxy” 和 “https_proxy” 环境变量可以解决问题。 解决办法 在 wsl 中正确设置 path # ~/.bashrc # 获取 Host IP WINDOWS_IP=$(ip route | grep default | awk &#39;{print $3}&#39;) PROXY_HTTP=&#34;http://${WINDOWS_IP}:7890&#34; # 设置环境变量 export http_proxy=&#34;${PROXY_HTTP}&#34; export https_proxy=&#34;${PROXY_HTTP}&#34; # ~/. + + + Vim 进阶 + http://localhost:1313/posts/vim/ + Thu, 16 Nov 2023 14:39:47 +0800 + http://localhost:1313/posts/vim/ + Repeat using . and ; // use &#39; to revert ;, u to revert . var foo = &#34;method(&#34;+argument1+&#34;,&#34;+argument2+&#34;)&#34;; f+ to Move, s&lt;space&gt;+&lt;space&gt;&lt;Esc&gt; to Change. use ; to repeat search and . to repeat change var foo = &#34;method(&#34; + argument1 + &#34;,&#34; + argument2 + &#34;)&#34;; Dot Formula The Ideal: one keystroke to Move, one keystroke to Execute. Act, Repeat, Reverse Operator + Motion = Action (dw、guaw、gcap) Misc 如何在每行的最后加上分号? 先A; 然后就可以重复j. diff --git a/public/posts/linux-io/index.html b/public/posts/linux-io/index.html new file mode 100644 index 0000000..d812040 --- /dev/null +++ b/public/posts/linux-io/index.html @@ -0,0 +1,438 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Linux 网络I/O复用并发模型 - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + +
+
+

Linux 网络I/O复用并发模型

+ + +
+ + + + + + +
+ +
+ +

阻塞非阻塞

+

阻塞占用资源少,但是一个线程通过阻塞IO,只能处理一个请求。 +非阻塞需要一直轮询,占用CPU资源大。

+
    +
  • 解决方法一:阻塞+多线程/多进程。 浪费资源
  • +
  • 解决方法二:非阻塞忙轮询
  • +
+
while(true) {
+    for i in [] {
+        if(i has data) {
+            read/ other process
+        }
+    }
+}
+
    +
  • 方法三:IO多路复用:既能阻塞等待,不浪费CPU资源,也能同一时刻监听多个IO请求的状态 +select:
  • +
+
while(true) {
+    select([]); // 阻塞 max 1024个
+    for i in [] {
+        if(i has data) {
+            read/ other process
+        }
+    }
+}
+

epoll:

+
while(true) {
+    可处理的流 = epoll_wait(epoll_fd);// 阻塞 max `cat /proc/sys/fs/file-max` 个
+    for i in 可处理的流[] {
+        read/ other process
+    }
+}
+

select/ poll

+

select 实现多路复用的方式是,将已连接的 Socket 都放到一个文件描述符集合,然后调用 select 函数将文件描述符集合拷贝到内核里,让内核来检查是否有网络事件产生,检查的方式很粗暴,就是通过遍历文件描述符集合的方式,当检查到有事件产生后,将此 Socket 标记为可读或可写, 接着再把整个文件描述符集合拷贝回用户态里,然后用户态还需要再通过遍历的方法找到可读或可写的 Socket,然后再对其处理。

+

所以,对于 select 这种方式,需要进行 2 次「遍历」文件描述符集合,一次是在内核态里,一个次是在用户态里 ,而且还会发生 2 次「拷贝」文件描述符集合,先从用户空间传入内核空间,由内核修改后,再传出到用户空间中。

+

epoll

+

第一点,epoll 在内核里使用红黑树来跟踪进程所有待检测的文件描述字,把需要监控的 socket 通过 epoll_ctl() 函数加入内核中的红黑树里,红黑树是个高效的数据结构,增删改一般时间复杂度是 O(logn)。而 select/poll 内核里没有类似 epoll 红黑树这种保存所有待检测的 socket 的数据结构,所以 select/poll 每次操作时都传入整个 socket 集合给内核,而 epoll 因为在内核维护了红黑树,可以保存所有待检测的 socket ,所以只需要传入一个待检测的 socket,减少了内核和用户空间大量的数据拷贝和内存分配。

+

第二点, epoll 使用事件驱动的机制,内核里维护了一个链表来记录就绪事件,当某个 socket 有事件发生时,通过回调函数内核会将其加入到这个就绪事件列表中,当用户调用 epoll_wait() 函数时,只会返回有事件发生的文件描述符的个数,不需要像 select/poll 那样轮询扫描整个 socket 集合,大大提高了检测的效率。

+

epoll API

+
    +
  1. 创建epoll,在内核创建一颗红黑树
  2. +
+
// @param size 告诉内核监听的数目
+// @returns 返回一个epoll句柄(fd)
+int epoll_create(int size);
+
    +
  1. 控制epoll,在红黑树上crud
  2. +
+
// @param op: EPOLL_CTL_ADD; EPOLL_CTL_MOD; EPOLL_CTL_DEL
+// @param event 常见的有EPOLLIN; EPOLLOUT
+// @returns 成功返回0,失败-1,errno查看错误信息
+int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
+
+// example:
+struct epoll_event new_event;
+new_event.events = EPOLLIN | EPOLLOUT;
+new_event.data.fd = 5;
+epoll_ctl(epfd, EPOLL_CTL_ADD, 5, &new_event);
+
    +
  1. 等待epoll,触发阻塞
  2. +
+
// @param event 从内核得到的事件集合
+// @param maxevents 告知内核这个events有多大
+int epoll_wait(int epfd, struct epoll_event *event, int maxevents, int timeout);
+
+// example:
+struct epoll_event my_event[1000];
+int event_cnt = epoll_wait(epfd, my_event, 1000, -1);
+

epoll 编程框架

+
int epfd = epoll_create(1000);
+
+epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &listen_event);
+
+while(1) {
+    int active_cnt = epoll_wait(epfd, events, 1000, -1);
+    for(int i=0; i<active_cnt; i++) {
+        if(events[i].data.fd == listen_fd) {
+            // accept 三次握手。并且将新accept的fd 加进epoll
+        } else if(events[i].events & EPOLLIN) {
+            // 对此fd 进行读操作
+        } else if(events[i].events & EPOLLOUT) {
+            // 对此fd 进行写操作
+        } 
+    }
+}
+

水平触发LT边缘触发ET

+

LT:只要用户不处理就一直把这些事件返回,涉及到内核态到用户态的拷贝,上下文的转换,稳定但消耗大。 +ET:只把事件返回一次,性能高。 +类似TCP/UDP 可靠重传

+

常见多路IO复用并发模型

+

模型一 单线程Accept

+

Server:

+
    +
  1. create ListenFd
  2. +
  3. Bind + Listen
  4. +
  5. Accept(ListenFd) // 这里会阻塞
  6. +
  7. Read + Write
  8. +
+

Client:

+
    +
  1. Connect
  2. +
  3. Read + Write
  4. +
+

缺点:其他client会阻塞。非并发。

+

模型二 单线程Accept + 多线程读写业务(无I/O复用)

+

Server: +Accept 之后创建新thread进行读写,主线程只Accept

+

缺点:要的线程太多,也会增加CPU切换成本;对于长连接,客户端只要不关闭server就要保持这个链接的状态,占用连接资源和线程的开销。

+

模型三 单线程多路I/O复用

+

Server:

+
    +
  1. create ListenFd
  2. +
  3. Bind + Listen
  4. +
  5. 多路I/O复用:创建 ListenFd 并监听。有Client1 Connect 请求,检测到ListenFd触发读事件,则进行Accept 建立连接,并将新生成的connFd1加入到监听I/O集合中
  6. +
  7. 读写的时候不能及时相应新Client的连接请求。
  8. +
+

并发少的时候,消息延迟要求不高,可以采用。

+

模型四 单线程多路I/O复用 + 多线程业务工作池

+

读取数据交给worker pool工作线程池,里面的的线程只处理消息业务,不进行socket读写操作。 +工作池处理完业务,触发connFd写事件,将回执客户端的消息通过main thread写给对方。 +实际上读写的业务并发为1,但是业务流程的并发为worker pool 线程数量。 +缺点:读写依然是main thread单独处理,最高的读写并行通道依然为1;返回给Client依旧需要排队。

+

模型五 单线程多路I/O复用 + 多线程多路I/O复用(线程池)

+

main thread 只用来监听ListenFd 并 Accept,线程池负责ConnFds

+

缺点:多个身处同一个Thread的客户端会出现读写延迟现象。

+

模型五 单线程多路I/O复用 + 多进程多路I/O复用(进程池)

+

多进程抢占处理ListenFd事件,进程池中的某个进程进行Accept,然后ConnFd

+

多进程内存资源空间占用稍微大一些

+

模型六 单线程多路I/O复用 + 多线程多路I/O复用 + 多线程

+

线程池中的线程还会继续 new thread。 +过于理想化,需要很多核

+

模型五是最合适的

+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + diff --git a/public/posts/map-reduce/index.html b/public/posts/map-reduce/index.html new file mode 100644 index 0000000..87320e5 --- /dev/null +++ b/public/posts/map-reduce/index.html @@ -0,0 +1,353 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Map Reduce 的实现 - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + +
+
+

Map Reduce 的实现

+ + +
+ + + + + + +
+ +
+ +

数据结构设计

+

状态都存在 Coordinator 中, Worker 无状态

+

Task 结构

+

每次 Worker 向 Coordinator 请求 Task,

+
type Task struct {
+	Input         string // Map 阶段的输入文件名
+	TaskPhase     Phase // Worker眼中的阶段: Map/Reduce/Exit/Wait
+	NReduce       int   // Reduce 的数量
+	Index         int   // 该 Task 在 Coordinator中的索引
+	Intermediates []string  // Map生成的中间文件名
+	Output        string // Reduce 生成的最终输出文件名
+}
+

Coordinator

+

+type Coordinator struct {
+	TaskQueue     chan *Task // 待处理的 Task 队列
+	TaskMetadatas map[int]*TaskMetadata // Coordinator 维护的 Task 元数据
+	Phase         Phase // Coordinator 处于哪个阶段: Map/Reduce/Exit/Wait
+	NReduce       int
+	InputFiles    []string  // Map 阶段的输入文件名
+	Intermediates [][]string 
+
+	mutex sync.Mutex // 用于保护并发安全
+}
+
+type TaskMetadata struct {
+	TaskStatus TaskStatus 	// Coordinator 关心的 task状态 Unassigned/Working/Finished
+	StartTime  time.Time    // 用于防止 Worker 超时
+	Task       *Task
+}
+

MapReduce 的执行流程

+
    +
  1. Coordinator 启动,并初始化,监听 Worker 的RPC请求。同时要启动一个goroutine 检查超时的任务,超时的任务要重新插入人物队列。 +
      +
    • 两种RPC, 一个是Worker请求任务的RPC, 还有一个是Worker完成任务的通知的RPC
    • +
    +
  2. +
  3. Worker 启动,向 Coordinator 请求任务,根据返回的 task 的 Phase (Map/Reduce/Exit/Wait) 分别进行下一步操作 +
      +
    • Map 阶段:读取输入文件,调用用户提供的 Map 函数,生成中间文件,然后向 Coordinator 通知任务完成
    • +
    • Reduce 阶段:读取中间文件,调用用户提供的 Reduce 函数,生成最终输出文件,然后向 Coordinator 通知任务完成
    • +
    • Exit 阶段:直接 return
    • +
    • Wait 阶段:sleep 5 s
    • +
    +
  4. +
  5. Coordinator 每次收到 Worker 完成任务的通知,除了进行状态的更新,都会检查是否所有任务都已经完成,如果是,就进入下一个阶段
  6. +
  7. 最终 Coordinator 到达 Exit Phase, 退出
  8. +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + diff --git a/public/posts/page/1/index.html b/public/posts/page/1/index.html index 75439f9..ac9cba2 100644 --- a/public/posts/page/1/index.html +++ b/public/posts/page/1/index.html @@ -1,10 +1,10 @@ - https://example.org/posts/ - + http://localhost:1313/posts/ + - + diff --git a/public/posts/react/index.html b/public/posts/react/index.html new file mode 100644 index 0000000..70a46c6 --- /dev/null +++ b/public/posts/react/index.html @@ -0,0 +1,304 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + React - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + +
+
+

React

+ + +
+ + + + + + +
+ +
+ +
    +
  • jsx:component返回的jsx,看起来像html,实际是javascript,通过babel将jsx编译成javascript,这些js最后通过dom操作生产html。jsx融合了html,css,js三个。
  • +
  • props:是属性,是上级component向下级component传递的只读的信息
  • +
  • state:使用useState()返回一个初始值v和一个function setV。useState是一个react Hook。根据当前state更新新的state需要用lambda function
  • +
  • UI = 很多components = f(state)
  • +
  • Npx create-react-app@5 travel-list 运行:npm install; npm start
  • +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + diff --git a/public/posts/sql-summary/index.html b/public/posts/sql-summary/index.html new file mode 100644 index 0000000..7065f3f --- /dev/null +++ b/public/posts/sql-summary/index.html @@ -0,0 +1,461 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sql 总结 - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + +
+
+

sql 总结

+ + +
+ + + + + + +
+ +
+ +

mysql 执行流程

+

SQL语句 - 查询缓存 - 解析器(语法分析 语意分析) - 优化器(逻辑优化 物理优化) - 执行器

+

为什么说 B+ 树查找行记录,最多只需1~3次磁盘IO

+

InnoDB 中页大小为16KB,一个键值对大概 8B+8B, 也就是一个页可以存1K个 KV pair,1K约等于1000个。 所以深度为3的 b+ 树索引可以维护 10^3 * 10^3 * 10^3 = 10 亿条记录。 正常每个节点不可能都填满了,所以一般 b+ 树高度在2到4 层。根节点在内存内,所以一般查找某一 KV 的行记录时只需1到3次磁盘IO

+

InnoDB 关键特性

+
    +
  • Insert Buffer 升级为 Change Buffer +对于非聚集索引的插入或更新操作,不是每次直接插入到索引页,而是先判断插入的非聚集索引页是否在缓冲池中,若在则直接插入;若不在,则先放入一个 Insert Buffer 对象中,然后再以一定的频率和情况进行 Insert Buffer 和辅助索引页子节点的 merge 操作。如此一来多个插入合并到一个操作中(因为在一个索引页中)。 +条件: 1. 索引是辅助索引; 2. 索引不是唯一的。 +Insert Buffer 是一个 B+ 树
  • +
  • Double Write
  • +
  • Adaptive Hash Index
  • +
  • Async IO
  • +
  • Flush Neighbor Page
  • +
+

覆盖索引

+

覆盖索引(covering index ,或称为索引覆盖)即从非主键索引中就能查到的记录,而不需要查询主键索引中的记录,避免了回表的产生减少了树的搜索次数,显著提升性能

+

索引下推

+
# 根据 key1 的索引先找大于 z 的,然后再判断a结尾,之后才回表
+select * from s1 where key1 > 'z' AND key1 like '$a'
+

EXISTS 和 IN 的区别

+
# EXISTS 的实现相当于外表循环: A 表小用 exists B表小用IN
+# EXISTS 先查外表; IN 先查内表
+# not in not exists:如果查询语句使用了not in,那么内外表都进行全表扫描,没有用到索引;而not extsts的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快
+select * from A where cc IN (SELECT cc from B)
+
+select * from A where EXISTS (select cc from B where B.cc =A.cc)
+

范式

+

1NF: 每个属性要是原子性的 +2NF:每一条数据记录都是唯一可标识的。所有非主键字段必须完全依赖主键 +3NF:所有非主键字段不能依赖于其他非主键字段 +反范式:有些冗余是必要的。业务优先 +巴斯范式:更强的3NF

+

ER模型

+

entity(矩形)-relationship(菱形) +属性(椭圆形)

+

一个实体转换成一个数据表,属性转换成表的字段。

+

使用主键和外键越多越好:证明实体之间冗余度低

+

调优的维度和步骤

+
    +
  1. 选择合适的DBMS
  2. +
  3. 优化表设计
  4. +
  5. 优化逻辑查询
  6. +
  7. 优化物理查询(索引的创建和使用)
  8. +
  9. 使用Redis 作为缓存
  10. +
  11. 库级优化(读写分离,数据分片)
  12. +
+

ACID

+
    +
  • atomic:一个事务要么commit 要么rollback
  • +
  • consistency:数据从一个合法性状态到另一个合法性状态。语义上的一致性
  • +
  • isolation:一个事务内部的操作和并发的其他事务是隔离的
  • +
  • durability:事务提交后改变是永久的。通过事务日志保证的。包括重做日志redo log回滚日志undo log
  • +
+

数据并发问题:

+
    +
  • 脏写:不可接受
  • +
  • 脏读:Session A读了 Session B更新但没commit 的字段
  • +
  • 不可重复读:Session A读了 Session B更新且commit 的字段
  • +
  • 幻读:A读了一个字段,B又插入了一些新的行;这时候A读会发现多处几行
  • +
+

隔离级别

+
    +
  • read uncommited 有脏读问题
  • +
  • read commited 有不可重复读问题
  • +
  • repeatable read 有幻读问题
  • +
  • serializable 并行太低
  • +
+

MySQL 事务日志

+
    +
  • +

    redo log:存储引擎层生成的日志,记录的是物理级别上的页修改日志,主要为了保证数据的可靠性。事务commit 的时候将redo log buffer 刷新到 redo log file

    +
  • +
  • +

    undo log:存储引擎层生成的日志,记录的是逻辑操作日志。如Insert语句在undo log里要记录一条对应的Delete 操作。主要用于事务的回滚一致性非锁定读(undo log 回滚行记录到某种特定的版本——MVCC)

    +
  • +
  • +

    为什么需要redo log:缓冲池的设计是为了协调 CPU 速度与磁盘速度的鸿沟。若每次一个页发生变化,就将新页的版本刷新到磁盘,开销太大。且若刷新时发生宕机,数据就无法恢复了。所以引入 Write Ahead Log 策略:事务提交时,先写redo log,再修改页。这是 ACID 中 Durability 的要求。

    +
  • +
+

Checkpoint: +1. 缩短数据库恢复时间。 +2. 缓冲池不够用时,脏页刷新到磁盘 +3. redolog 不可用时,刷新脏页

+
    +
  • +

    redo log 和 bin log 区别:redo log是存储引擎层生成的;bin log 是数据库层产生的,只有事务提交时才会一次写入到bin log中,而redo log 会一直记录

    +
  • +
  • +

    undo log 作用:1. 回滚数据 2.MVCC

    +
  • +
  • +

    undo log类型

    +
      +
    1. insert undo log:事务隔离性的要求导致commit之后可以直接删除undo log
    2. +
    3. update undo log:可能需要提供MVCC机制,不能立刻删除。undo log 在事务 commit 之后,为了重用,放到链表中。purge线程判断是否删除 undo log 及 undo log 所在的页
    4. +
    +
  • +
+

并发问题的解决方法

+
    +
  • 方案一:读用MVCC, 写用加锁 +
      +
    • 在 read committed 隔离级别下,每次select 都生成一个 ReadView
    • +
    • 在 repeatable read 隔离级别下,第一次select 时生成一个 ReadView,后续复用这个ReadView
    • +
    +
  • +
  • 方案二:读写都用锁(S lock,X lock)
  • +
+
SELECT ... LOCK IN SHARED MODE;
+SELECT ... FOR SHARE; # S lock
+SELECT ... FOR UPDATE; # X lock
+

表锁的分类

+
    +
  • S lock & X lock
  • +
  • 意向锁(intention lock):给某一行加上排他锁,DB会给更大一级的空间加上意向锁,告诉其他人这个数据页或数据表上有人上过排他锁了
  • +
  • 自增锁(auto-inc lock):向使用含有AUTO_INCREMENT列的表中插入数据时需要获取的一种特殊的表级锁
  • +
  • 元数据锁(MDL lock):当对表结构变更的时候要加MDL写锁,crud加MDL读锁
  • +
+

行锁的分类

+
    +
  • 记录锁(record lock)
  • +
  • 间隙锁(Gap lock):MySQL在repeatable read级别下解决幻读问题方法1:MVCC, 2.加锁。加锁的时候无法给尚不存在的幻影记录加锁。gap lock就是为了解决这个问题
  • +
  • 临键锁(Next—Key lock):前两种锁的结合:即锁住某条记录,又阻止其他事务在该记录前边的间隙插入新记录。在repeatable read 级别下使用
  • +
  • 插入意向锁(Insert Intention Locks):一个事务在插入记录时要判断插入位置是否有gap锁(包括next-key lock),有的话要等待,同时生成一个插入意向锁表示有事务想插入
  • +
+

页锁

+
    +
  • 悲观锁 乐观锁
  • +
  • 显示锁 隐式锁
  • +
+

处理死锁

+
    +
  1. 等待直到超时
  2. +
  3. 死锁检测进行死锁处理
  4. +
+

MVCC

+

实现依赖于 隐藏字段(trx_id, roll_pointer) Undo Log Read View +是一种快照读,前提是隔离级别不是串行级别。read uncommitted也不需要MVCC。 +ReadView 主要4个重要内容: creator_trx_id, trx_ids(当前活跃的读写事务), up_limit_id(活跃事务中最小的事务ID), low_limit_id(生成ReadView 时系统应该分配给下一个事务的ID)。

+

ReadView 判断记录的哪个版本可见:靠对比被访问版本的 trx_id 与 ReadView 中的 creator_trx_id up_limit_id low_limit_id trx_ids

+

ReadView 整体操作流程:

+
    +
  1. 首先获取事务自己的版本号,也就是事务ID
  2. +
  3. 获取 ReadView
  4. +
  5. 查询获得的数据,与 ReadView 中的事务版本号相比
  6. +
  7. 如果不符合 ReadView 规则,就要从 Undo Log 获取历史快照
  8. +
  9. 返回符合规则的结果
  10. +
+
    +
  • 在 read committed 隔离级别下,每次select 都生成一个 ReadView
  • +
  • 在 repeatable read 隔离级别下,第一次select 时生成一个
  • +
+

日志类型

+
    +
  • 慢查询日志
  • +
  • 通用查询日志
  • +
  • 错误日志
  • +
  • 二进制日志(记录所有更改数据的语句(ddl,dml,没有查询的),用于主从服务器数据同步;服务器故障恢复,相比redo log,binlog是逻辑日志,redolog是物理日志)
  • +
  • 中继日志(relay log:用于主从服务器架构,从服务器用来存放主服务器bin log的一个中间文件)
  • +
  • 数据定义语句日志
  • +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + diff --git a/public/posts/vim/index.html b/public/posts/vim/index.html new file mode 100644 index 0000000..acad406 --- /dev/null +++ b/public/posts/vim/index.html @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Vim 进阶 - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + +
+
+

Vim 进阶

+ + +
+ + + + + + +
+ +
+ +

Repeat using . and ;

+
// use ' to revert ;, u to revert .
+var foo = "method("+argument1+","+argument2+")";
+
    +
  • f+ to Move, s<space>+<space><Esc> to Change.
  • +
  • use ; to repeat search and . to repeat change
  • +
+
var foo = "method(" + argument1 + "," + argument2 + ")";
+
+

Dot Formula

+

The Ideal:

+
    +
  • one keystroke to Move, one keystroke to Execute.
  • +
  • Act, Repeat, Reverse
  • +
  • Operator + Motion = Action (dw、guaw、gcap)
  • +
+

Misc

+
    +
  • 如何在每行的最后加上分号? 先A; 然后就可以重复j. j. j.
  • +
  • 查找:光标在单词上的时候输入*也可以
  • +
  • 查找并替换: cwxxx 然后重复 n. n. n. 也可以用 :%s/content/copy/g 命令
  • +
  • 更改大小写:g~、gu 和 gU
  • +
  • w: word, s: sentence, p: paragraph, 一般来说,d{motion} 命令和 aw、as 和 ap 配合使用比较好,而 c{motion} 和 iw is ip一起用效果会更好
  • +
  • :!ls exec shell command inside vim, % means current file
  • +
  • +
      +
    1. modify column data: visual select then c, finally <Esc>
    2. +
    3. 非方形区域也行,如在行尾添加分号:选中block,go to end using $
    4. +
    +
  • +
  • :w !sudo tee % > /dev/null 以超级用户权限保存文件
  • +
  • :%s/Practical/Pragmatic/ 每行内的第一个“Practical”替换为“Pragmatic”
  • +
  • in visual mode: o: Goto other end of highlighted text.
  • +
  • in visual mode: r-: change them all to —–
  • +
  • in insert mode: <C-o>zz to get into insert normal mode.(move curr line to center and continue insert)
  • +
  • 18<C-x>: find the first number later in this line, -18 to it.
  • +
  • in insert mode: <C-w> delete back one word. <C-u> delete back to start of line
  • +
  • in insert mode: <C-r>=6*24<CR> insert the result
  • +
  • R to replace mode
  • +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + diff --git a/public/posts/wsl-network/index.html b/public/posts/wsl-network/index.html new file mode 100644 index 0000000..b130ced --- /dev/null +++ b/public/posts/wsl-network/index.html @@ -0,0 +1,336 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wsl 代理问题 - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + +
+
+

wsl 代理问题

+ + +
+ + + + + + +
+ +
+ +

问题

+

win11 使用 vs code 连接 wsl 的时候,一些 vs code 的拓展无法连接网络,报错: +Failed to establish a socket connection to proxies: ["PROXY 127.0.0.1:7890"]

+

或者 go build 因为网络问题无法继续。

+

原因

+

Windows 开启 Clash 之后,Windows 系统代理一般被设置为 127.0.0.1:7890,VS Code 会继承这个代理设置,wsl 中的 vscode-server 也继承了这个代理设置。但是 wsl 在 127.0.0.1:7890 上并没有代理服务,导致 vscode-server 联网失败。所以 wsl 中正确设置 “http_proxy” 和 “https_proxy” 环境变量可以解决问题。

+

解决办法

+
    +
  1. 在 wsl 中正确设置 path
  2. +
+
# ~/.bashrc
+
+# 获取 Host IP
+WINDOWS_IP=$(ip route | grep default | awk '{print $3}')
+PROXY_HTTP="http://${WINDOWS_IP}:7890"
+# 设置环境变量
+export http_proxy="${PROXY_HTTP}"
+export https_proxy="${PROXY_HTTP}"
+
# ~/.config/fish/config.fish
+
+set -l WINDOWS_IP (ip route | grep default | awk '{print $3}')
+set -l PROXY_HTTP "http://$WINDOWS_IP:7890"
+set -gx http_proxy $PROXY_HTTP
+set -gx https_proxy $PROXY_HTTP
+
    +
  1. +

    Clash 打开 allow lan (表示允许局域网(Local Area Network, LAN)访问。当启用 “allow lan” 选项时,局域网内的其他设备可以连接到运行 Clash 的设备,并通过其代理服务访问互联网。这样,你可以将运行 Clash 的设备作为一个代理服务器,供局域网内的其他设备使用。)

    +
  2. +
  3. +

    为防止 Windows 防火墙阻止 Ubuntu 子系统访问 Host IP,使用管理员权限打开powershell,在 Windows 防火墙中增加一条规则:

    +
  4. +
+
New-NetFirewallRule -DisplayName "WSL" -Direction Inbound -InterfaceAlias "vEthernet (WSL)" -Action Allow
+
    +
  1. 可能需要重启
  2. +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + diff --git a/public/sitemap.xml b/public/sitemap.xml index c014100..4dad9e5 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -2,20 +2,70 @@ - https://example.org/ - 2023-11-15T22:41:25+08:00 + http://localhost:1313/tags/frontend/ + 2024-03-14T19:42:12+08:00 - https://example.org/posts/ - 2023-11-15T22:41:25+08:00 + http://localhost:1313/ + 2024-03-14T19:42:12+08:00 - https://example.org/posts/second/ - 2023-11-15T22:41:25+08:00 + http://localhost:1313/posts/ + 2024-03-14T19:42:12+08:00 - https://example.org/posts/my-first-post/ - 2023-11-15T22:21:08+08:00 + http://localhost:1313/tags/react/ + 2024-03-14T19:42:12+08:00 - https://example.org/categories/ + http://localhost:1313/posts/react/ + 2024-03-14T19:42:12+08:00 - https://example.org/tags/ + http://localhost:1313/tags/ + 2024-03-14T19:42:12+08:00 + + http://localhost:1313/posts/cpp-concurrency/ + 2023-12-08T14:52:36+08:00 + + http://localhost:1313/tags/distributed-system/ + 2023-12-01T20:24:26+08:00 + + http://localhost:1313/tags/map-reduce/ + 2023-12-01T20:24:26+08:00 + + http://localhost:1313/posts/map-reduce/ + 2023-12-01T20:24:26+08:00 + + http://localhost:1313/tags/go/ + 2023-11-19T01:29:52+08:00 + + http://localhost:1313/posts/go-design/ + 2023-11-19T01:29:52+08:00 + + http://localhost:1313/tags/sql/ + 2023-11-17T01:12:44+08:00 + + http://localhost:1313/posts/sql-summary/ + 2023-11-17T01:12:44+08:00 + + http://localhost:1313/tags/io/ + 2023-11-17T01:04:58+08:00 + + http://localhost:1313/tags/linux/ + 2023-11-17T01:04:58+08:00 + + http://localhost:1313/posts/linux-io/ + 2023-11-17T01:04:58+08:00 + + http://localhost:1313/tags/wsl/ + 2023-11-17T00:41:53+08:00 + + http://localhost:1313/posts/wsl-network/ + 2023-11-17T00:41:53+08:00 + + http://localhost:1313/tags/editor/ + 2023-11-16T14:39:47+08:00 + + http://localhost:1313/tags/vim/ + 2023-11-16T14:39:47+08:00 + + http://localhost:1313/posts/vim/ + 2023-11-16T14:39:47+08:00 diff --git a/public/tags/distributed-system/index.html b/public/tags/distributed-system/index.html new file mode 100644 index 0000000..739fc0c --- /dev/null +++ b/public/tags/distributed-system/index.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Distributed System - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + + + +

#Distributed System

+ + + + + + + + + + + + +
+ +

Map Reduce 的实现

+ + Map Reduce 的实现 +
+ + + + + + +
+ + + + + diff --git a/public/tags/distributed-system/index.xml b/public/tags/distributed-system/index.xml new file mode 100644 index 0000000..72dc3c2 --- /dev/null +++ b/public/tags/distributed-system/index.xml @@ -0,0 +1,19 @@ + + + + Distributed System on greathongtu 的 Blog + http://localhost:1313/tags/distributed-system/ + Recent content in Distributed System on greathongtu 的 Blog + Hugo -- gohugo.io + en-us + Fri, 01 Dec 2023 20:24:26 +0800 + + + Map Reduce 的实现 + http://localhost:1313/posts/map-reduce/ + Fri, 01 Dec 2023 20:24:26 +0800 + http://localhost:1313/posts/map-reduce/ + 数据结构设计 状态都存在 Coordinator 中, Worker 无状态 Task 结构 每次 Worker 向 Coordinator 请求 Task, type Task struct { Input string // Map 阶段的输入文件名 TaskPhase Phase // Worker眼中的阶段: Map/Reduce/Exit/Wait NReduce int // Reduce 的数量 Index int // 该 Task 在 Coordinator中的索引 Intermediates []string // Map生成的中间文件名 Output string // Reduce 生成的最终输出文件名 } Coordinator type Coordinator struct { TaskQueue chan *Task // 待处理的 Task 队列 TaskMetadatas map[int]*TaskMetadata // Coordinator 维护的 Task 元数据 Phase Phase // Coordinator 处于哪个阶段: Map/Reduce/Exit/Wait NReduce int InputFiles []string // Map 阶段的输入文件名 Intermediates [][]string mutex sync. + + + diff --git a/public/tags/distributed-system/page/1/index.html b/public/tags/distributed-system/page/1/index.html new file mode 100644 index 0000000..384b8e7 --- /dev/null +++ b/public/tags/distributed-system/page/1/index.html @@ -0,0 +1,10 @@ + + + + http://localhost:1313/tags/distributed-system/ + + + + + + diff --git a/public/tags/editor/index.html b/public/tags/editor/index.html new file mode 100644 index 0000000..b1ea30d --- /dev/null +++ b/public/tags/editor/index.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Editor - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + + + +

#Editor

+ + + + + + + + + + + + +
+ +

Vim 进阶

+ + Vim 进阶 +
+ + + + + + +
+ + + + + diff --git a/public/tags/editor/index.xml b/public/tags/editor/index.xml new file mode 100644 index 0000000..2105afb --- /dev/null +++ b/public/tags/editor/index.xml @@ -0,0 +1,19 @@ + + + + Editor on greathongtu 的 Blog + http://localhost:1313/tags/editor/ + Recent content in Editor on greathongtu 的 Blog + Hugo -- gohugo.io + en-us + Thu, 16 Nov 2023 14:39:47 +0800 + + + Vim 进阶 + http://localhost:1313/posts/vim/ + Thu, 16 Nov 2023 14:39:47 +0800 + http://localhost:1313/posts/vim/ + Repeat using . and ; // use &#39; to revert ;, u to revert . var foo = &#34;method(&#34;+argument1+&#34;,&#34;+argument2+&#34;)&#34;; f+ to Move, s&lt;space&gt;+&lt;space&gt;&lt;Esc&gt; to Change. use ; to repeat search and . to repeat change var foo = &#34;method(&#34; + argument1 + &#34;,&#34; + argument2 + &#34;)&#34;; Dot Formula The Ideal: one keystroke to Move, one keystroke to Execute. Act, Repeat, Reverse Operator + Motion = Action (dw、guaw、gcap) Misc 如何在每行的最后加上分号? 先A; 然后就可以重复j. + + + diff --git a/public/tags/editor/page/1/index.html b/public/tags/editor/page/1/index.html new file mode 100644 index 0000000..3b75539 --- /dev/null +++ b/public/tags/editor/page/1/index.html @@ -0,0 +1,10 @@ + + + + http://localhost:1313/tags/editor/ + + + + + + diff --git a/public/tags/frontend/index.html b/public/tags/frontend/index.html new file mode 100644 index 0000000..fe1c58c --- /dev/null +++ b/public/tags/frontend/index.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Frontend - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + + + +

#Frontend

+ + + + + + + + + + + + +
+ +

React

+ + React +
+ + + + + + +
+ + + + + diff --git a/public/tags/frontend/index.xml b/public/tags/frontend/index.xml new file mode 100644 index 0000000..ee02aa1 --- /dev/null +++ b/public/tags/frontend/index.xml @@ -0,0 +1,19 @@ + + + + Frontend on greathongtu 的 Blog + http://localhost:1313/tags/frontend/ + Recent content in Frontend on greathongtu 的 Blog + Hugo -- gohugo.io + en-us + Thu, 14 Mar 2024 19:42:12 +0800 + + + React + http://localhost:1313/posts/react/ + Thu, 14 Mar 2024 19:42:12 +0800 + http://localhost:1313/posts/react/ + jsx:component返回的jsx,看起来像html,实际是javascript,通过babel将jsx编译成javascript,这些js最后通过dom操作生产html。jsx融合了html,css,js三个。 props:是属性,是上级component向下级component传递的只读的信息 state:使用useState()返回一个初始值v和一个function setV。useState是一个react Hook。根据当前state更新新的state需要用lambda function UI = 很多components = f(state) Npx create-react-app@5 travel-list 运行:npm start + + + diff --git a/public/tags/frontend/page/1/index.html b/public/tags/frontend/page/1/index.html new file mode 100644 index 0000000..154b3c2 --- /dev/null +++ b/public/tags/frontend/page/1/index.html @@ -0,0 +1,10 @@ + + + + http://localhost:1313/tags/frontend/ + + + + + + diff --git a/public/tags/go/index.html b/public/tags/go/index.html new file mode 100644 index 0000000..4a7b064 --- /dev/null +++ b/public/tags/go/index.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Go - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + + + +

#Go

+ + + + + + + + + + + + +
+ +

Go 探究

+ + Go 探究 +
+ + + + + + +
+ + + + + diff --git a/public/tags/go/index.xml b/public/tags/go/index.xml new file mode 100644 index 0000000..c87b038 --- /dev/null +++ b/public/tags/go/index.xml @@ -0,0 +1,19 @@ + + + + Go on greathongtu 的 Blog + http://localhost:1313/tags/go/ + Recent content in Go on greathongtu 的 Blog + Hugo -- gohugo.io + en-us + Sun, 19 Nov 2023 01:29:52 +0800 + + + Go 探究 + http://localhost:1313/posts/go-design/ + Sun, 19 Nov 2023 01:29:52 +0800 + http://localhost:1313/posts/go-design/ + 数组 如果数组中元素的个数小于或者等于 4 个,那么所有的变量会直接在栈上初始化,如果数组元素大于 4 个,变量就会在静态存储区初始化然后拷贝到栈上。 为什么? slice []int []interface{} 切片提供了对数组中部分连续片段的引用,而作为数组的引用,我们可以在运行区间可以修改它的长度和范围。当切片底层的数组长度不足时就会触发扩容,切片指向的数组可能会发生变化,不过在上层看来切片是没有变化的,上层只需要与切片打交道不需要关心数组的变化。 追加和扩容 先确定切片大致容量 如果期望容量大于当前容量的两倍就会使用期望容量; 如果当前切片的长度小于 1024 就会将容量翻倍; 如果当前切片的长度大于 1024 就会每次增加 25% 的容量,直到新容量大于期望容量; 再根据切片中的元素大小对齐内存 切片拷贝 无论是编译期间拷贝还是运行时拷贝,两种拷贝方式都会通过 runtime.memmove 将整块内存的内容拷贝到目标的内存区域中: 大切片扩容或者复制时可能会发生大规模的内存拷贝,要注意。 map type hmap struct { count int flags uint8 B uint8 noverflow uint16 hash0 uint32 buckets unsafe.Pointer oldbuckets unsafe.Pointer nevacuate uintptr extra *mapextra } type mapextra struct { overflow *[]*bmap oldoverflow *[]*bmap nextOverflow *bmap } count 表示当前哈希表中的元素数量; B 表示当前哈希表持有的 buckets 数量,但是因为哈希表中桶的数量都 2 的倍数,所以该字段会存储对数,也就是 len(buckets) == 2^B; hash0 是哈希的种子,它能为哈希函数的结果引入随机性,这个值在创建哈希表时确定,并在调用哈希函数时作为参数传入; oldbuckets 是哈希在扩容时用于保存之前 buckets 的字段,它的大小是当前 buckets 的一半; 如上图所示哈希表 runtime. + + + diff --git a/public/tags/go/page/1/index.html b/public/tags/go/page/1/index.html new file mode 100644 index 0000000..d8944eb --- /dev/null +++ b/public/tags/go/page/1/index.html @@ -0,0 +1,10 @@ + + + + http://localhost:1313/tags/go/ + + + + + + diff --git a/public/tags/index.html b/public/tags/index.html index 55dfdb8..3c02f0f 100644 --- a/public/tags/index.html +++ b/public/tags/index.html @@ -25,6 +25,8 @@ + + @@ -33,7 +35,7 @@ style="--bg: #faf8f1" lang="en-us" > - + - Tags - My New Hugo Site + Tags - greathongtu 的 Blog @@ -56,7 +58,7 @@ name="description" content="A personal blog" /> - + @@ -65,14 +67,14 @@ - + - + @@ -82,52 +84,25 @@ - - + - - - - - - - - - - - - - - - - - - - - - + + + + - - - - @@ -137,8 +112,8 @@
My New Hugo Sitegreathongtu 的 Blog
+ + + github + + + +
@@ -236,8 +227,8 @@

#Tags

class="opaco mx-auto flex h-[4.5rem] max-w-3xl items-center px-8 text-[0.9em] opacity-60" >
- © 2023 - My New Hugo Site + © 2024 + greathongtu 的 Blog
Powered by Hugo️️ - Tags on My New Hugo Site - https://example.org/tags/ - Recent content in Tags on My New Hugo Site + Tags on greathongtu 的 Blog + http://localhost:1313/tags/ + Recent content in Tags on greathongtu 的 Blog Hugo -- gohugo.io en-us - + Thu, 14 Mar 2024 19:42:12 +0800 + + + Frontend + http://localhost:1313/tags/frontend/ + Thu, 14 Mar 2024 19:42:12 +0800 + http://localhost:1313/tags/frontend/ + + + + React + http://localhost:1313/tags/react/ + Thu, 14 Mar 2024 19:42:12 +0800 + http://localhost:1313/tags/react/ + + + + Distributed System + http://localhost:1313/tags/distributed-system/ + Fri, 01 Dec 2023 20:24:26 +0800 + http://localhost:1313/tags/distributed-system/ + + + + Map Reduce + http://localhost:1313/tags/map-reduce/ + Fri, 01 Dec 2023 20:24:26 +0800 + http://localhost:1313/tags/map-reduce/ + + + + Go + http://localhost:1313/tags/go/ + Sun, 19 Nov 2023 01:29:52 +0800 + http://localhost:1313/tags/go/ + + + + Sql + http://localhost:1313/tags/sql/ + Fri, 17 Nov 2023 01:12:44 +0800 + http://localhost:1313/tags/sql/ + + + + Io + http://localhost:1313/tags/io/ + Fri, 17 Nov 2023 01:04:58 +0800 + http://localhost:1313/tags/io/ + + + + Linux + http://localhost:1313/tags/linux/ + Fri, 17 Nov 2023 01:04:58 +0800 + http://localhost:1313/tags/linux/ + + + + Wsl + http://localhost:1313/tags/wsl/ + Fri, 17 Nov 2023 00:41:53 +0800 + http://localhost:1313/tags/wsl/ + + + + Editor + http://localhost:1313/tags/editor/ + Thu, 16 Nov 2023 14:39:47 +0800 + http://localhost:1313/tags/editor/ + + + + Vim + http://localhost:1313/tags/vim/ + Thu, 16 Nov 2023 14:39:47 +0800 + http://localhost:1313/tags/vim/ + + diff --git a/public/tags/io/index.html b/public/tags/io/index.html new file mode 100644 index 0000000..1c4cc88 --- /dev/null +++ b/public/tags/io/index.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Io - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + + + +

#Io

+ + + + + + + + + + + + +
+ +

Linux 网络I/O复用并发模型

+ + Linux 网络I/O复用并发模型 +
+ + + + + + +
+ + + + + diff --git a/public/tags/io/index.xml b/public/tags/io/index.xml new file mode 100644 index 0000000..ec7ea9b --- /dev/null +++ b/public/tags/io/index.xml @@ -0,0 +1,19 @@ + + + + Io on greathongtu 的 Blog + http://localhost:1313/tags/io/ + Recent content in Io on greathongtu 的 Blog + Hugo -- gohugo.io + en-us + Fri, 17 Nov 2023 01:04:58 +0800 + + + Linux 网络I/O复用并发模型 + http://localhost:1313/posts/linux-io/ + Fri, 17 Nov 2023 01:04:58 +0800 + http://localhost:1313/posts/linux-io/ + 阻塞非阻塞 阻塞占用资源少,但是一个线程通过阻塞IO,只能处理一个请求。 非阻塞需要一直轮询,占用CPU资源大。 解决方法一:阻塞+多线程/多进程。 浪费资源 解决方法二:非阻塞忙轮询 while(true) { for i in 流[] { if(i has data) { read/ other process } } } 方法三:IO多路复用:既能阻塞等待,不浪费CPU资源,也能同一时刻监听多个IO请求的状态 select: while(true) { select(流[]); // 阻塞 max 1024个 for i in 流[] { if(i has data) { read/ other process } } } epoll: while(true) { 可处理的流 = epoll_wait(epoll_fd);// 阻塞 max `cat /proc/sys/fs/file-max` 个 for i in 可处理的流[] { read/ other process } } select/ poll select 实现多路复用的方式是,将已连接的 Socket 都放到一个文件描述符集合,然后调用 select 函数将文件描述符集合拷贝到内核里,让内核来检查是否有网络事件产生,检查的方式很粗暴,就是通过遍历文件描述符集合的方式,当检查到有事件产生后,将此 Socket 标记为可读或可写, 接着再把整个文件描述符集合拷贝回用户态里,然后用户态还需要再通过遍历的方法找到可读或可写的 Socket,然后再对其处理。 + + + diff --git a/public/tags/io/page/1/index.html b/public/tags/io/page/1/index.html new file mode 100644 index 0000000..cd68445 --- /dev/null +++ b/public/tags/io/page/1/index.html @@ -0,0 +1,10 @@ + + + + http://localhost:1313/tags/io/ + + + + + + diff --git a/public/tags/linux/index.html b/public/tags/linux/index.html new file mode 100644 index 0000000..44e04be --- /dev/null +++ b/public/tags/linux/index.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Linux - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + + + +

#Linux

+ + + + + + + + + + + + +
+ +

Linux 网络I/O复用并发模型

+ + Linux 网络I/O复用并发模型 +
+ + + + + + +
+ + + + + diff --git a/public/tags/linux/index.xml b/public/tags/linux/index.xml new file mode 100644 index 0000000..246367a --- /dev/null +++ b/public/tags/linux/index.xml @@ -0,0 +1,19 @@ + + + + Linux on greathongtu 的 Blog + http://localhost:1313/tags/linux/ + Recent content in Linux on greathongtu 的 Blog + Hugo -- gohugo.io + en-us + Fri, 17 Nov 2023 01:04:58 +0800 + + + Linux 网络I/O复用并发模型 + http://localhost:1313/posts/linux-io/ + Fri, 17 Nov 2023 01:04:58 +0800 + http://localhost:1313/posts/linux-io/ + 阻塞非阻塞 阻塞占用资源少,但是一个线程通过阻塞IO,只能处理一个请求。 非阻塞需要一直轮询,占用CPU资源大。 解决方法一:阻塞+多线程/多进程。 浪费资源 解决方法二:非阻塞忙轮询 while(true) { for i in 流[] { if(i has data) { read/ other process } } } 方法三:IO多路复用:既能阻塞等待,不浪费CPU资源,也能同一时刻监听多个IO请求的状态 select: while(true) { select(流[]); // 阻塞 max 1024个 for i in 流[] { if(i has data) { read/ other process } } } epoll: while(true) { 可处理的流 = epoll_wait(epoll_fd);// 阻塞 max `cat /proc/sys/fs/file-max` 个 for i in 可处理的流[] { read/ other process } } select/ poll select 实现多路复用的方式是,将已连接的 Socket 都放到一个文件描述符集合,然后调用 select 函数将文件描述符集合拷贝到内核里,让内核来检查是否有网络事件产生,检查的方式很粗暴,就是通过遍历文件描述符集合的方式,当检查到有事件产生后,将此 Socket 标记为可读或可写, 接着再把整个文件描述符集合拷贝回用户态里,然后用户态还需要再通过遍历的方法找到可读或可写的 Socket,然后再对其处理。 + + + diff --git a/public/tags/linux/page/1/index.html b/public/tags/linux/page/1/index.html new file mode 100644 index 0000000..1cb9593 --- /dev/null +++ b/public/tags/linux/page/1/index.html @@ -0,0 +1,10 @@ + + + + http://localhost:1313/tags/linux/ + + + + + + diff --git a/public/tags/map-reduce/index.html b/public/tags/map-reduce/index.html new file mode 100644 index 0000000..d23f8e0 --- /dev/null +++ b/public/tags/map-reduce/index.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Map Reduce - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + + + +

#Map Reduce

+ + + + + + + + + + + + +
+ +

Map Reduce 的实现

+ + Map Reduce 的实现 +
+ + + + + + +
+ + + + + diff --git a/public/tags/map-reduce/index.xml b/public/tags/map-reduce/index.xml new file mode 100644 index 0000000..3c60e1e --- /dev/null +++ b/public/tags/map-reduce/index.xml @@ -0,0 +1,19 @@ + + + + Map Reduce on greathongtu 的 Blog + http://localhost:1313/tags/map-reduce/ + Recent content in Map Reduce on greathongtu 的 Blog + Hugo -- gohugo.io + en-us + Fri, 01 Dec 2023 20:24:26 +0800 + + + Map Reduce 的实现 + http://localhost:1313/posts/map-reduce/ + Fri, 01 Dec 2023 20:24:26 +0800 + http://localhost:1313/posts/map-reduce/ + 数据结构设计 状态都存在 Coordinator 中, Worker 无状态 Task 结构 每次 Worker 向 Coordinator 请求 Task, type Task struct { Input string // Map 阶段的输入文件名 TaskPhase Phase // Worker眼中的阶段: Map/Reduce/Exit/Wait NReduce int // Reduce 的数量 Index int // 该 Task 在 Coordinator中的索引 Intermediates []string // Map生成的中间文件名 Output string // Reduce 生成的最终输出文件名 } Coordinator type Coordinator struct { TaskQueue chan *Task // 待处理的 Task 队列 TaskMetadatas map[int]*TaskMetadata // Coordinator 维护的 Task 元数据 Phase Phase // Coordinator 处于哪个阶段: Map/Reduce/Exit/Wait NReduce int InputFiles []string // Map 阶段的输入文件名 Intermediates [][]string mutex sync. + + + diff --git a/public/tags/map-reduce/page/1/index.html b/public/tags/map-reduce/page/1/index.html new file mode 100644 index 0000000..e14e189 --- /dev/null +++ b/public/tags/map-reduce/page/1/index.html @@ -0,0 +1,10 @@ + + + + http://localhost:1313/tags/map-reduce/ + + + + + + diff --git a/public/tags/page/1/index.html b/public/tags/page/1/index.html index e444b76..4a695e8 100644 --- a/public/tags/page/1/index.html +++ b/public/tags/page/1/index.html @@ -1,10 +1,10 @@ - https://example.org/tags/ - + http://localhost:1313/tags/ + - + diff --git a/public/tags/react/index.html b/public/tags/react/index.html new file mode 100644 index 0000000..1d93fc7 --- /dev/null +++ b/public/tags/react/index.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + React - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + + + +

#React

+ + + + + + + + + + + + +
+ +

React

+ + React +
+ + + + + + +
+ + + + + diff --git a/public/tags/react/index.xml b/public/tags/react/index.xml new file mode 100644 index 0000000..76ab5e5 --- /dev/null +++ b/public/tags/react/index.xml @@ -0,0 +1,19 @@ + + + + React on greathongtu 的 Blog + http://localhost:1313/tags/react/ + Recent content in React on greathongtu 的 Blog + Hugo -- gohugo.io + en-us + Thu, 14 Mar 2024 19:42:12 +0800 + + + React + http://localhost:1313/posts/react/ + Thu, 14 Mar 2024 19:42:12 +0800 + http://localhost:1313/posts/react/ + jsx:component返回的jsx,看起来像html,实际是javascript,通过babel将jsx编译成javascript,这些js最后通过dom操作生产html。jsx融合了html,css,js三个。 props:是属性,是上级component向下级component传递的只读的信息 state:使用useState()返回一个初始值v和一个function setV。useState是一个react Hook。根据当前state更新新的state需要用lambda function UI = 很多components = f(state) Npx create-react-app@5 travel-list 运行:npm start + + + diff --git a/public/tags/react/page/1/index.html b/public/tags/react/page/1/index.html new file mode 100644 index 0000000..f5874a5 --- /dev/null +++ b/public/tags/react/page/1/index.html @@ -0,0 +1,10 @@ + + + + http://localhost:1313/tags/react/ + + + + + + diff --git a/public/tags/sql/index.html b/public/tags/sql/index.html new file mode 100644 index 0000000..44b8b53 --- /dev/null +++ b/public/tags/sql/index.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sql - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + + + +

#Sql

+ + + + + + + + + + + + +
+ +

sql 总结

+ + sql 总结 +
+ + + + + + +
+ + + + + diff --git a/public/tags/sql/index.xml b/public/tags/sql/index.xml new file mode 100644 index 0000000..23a8e66 --- /dev/null +++ b/public/tags/sql/index.xml @@ -0,0 +1,19 @@ + + + + Sql on greathongtu 的 Blog + http://localhost:1313/tags/sql/ + Recent content in Sql on greathongtu 的 Blog + Hugo -- gohugo.io + en-us + Fri, 17 Nov 2023 01:12:44 +0800 + + + sql 总结 + http://localhost:1313/posts/sql-summary/ + Fri, 17 Nov 2023 01:12:44 +0800 + http://localhost:1313/posts/sql-summary/ + mysql 执行流程 SQL语句 - 查询缓存 - 解析器(语法分析 语意分析) - 优化器(逻辑优化 物理优化) - 执行器 为什么说 B+ 树查找行记录,最多只需1~3次磁盘IO InnoDB 中页大小为16KB,一个键值对大概 8B+8B, 也就是一个页可以存1K个 KV pair,1K约等于1000个。 所以深度为3的 b+ 树索引可以维护 10^3 * 10^3 * 10^3 = 10 亿条记录。 正常每个节点不可能都填满了,所以一般 b+ 树高度在2到4 层。根节点在内存内,所以一般查找某一 KV 的行记录时只需1到3次磁盘IO InnoDB 关键特性 Insert Buffer 升级为 Change Buffer 对于非聚集索引的插入或更新操作,不是每次直接插入到索引页,而是先判断插入的非聚集索引页是否在缓冲池中,若在则直接插入;若不在,则先放入一个 Insert Buffer 对象中,然后再以一定的频率和情况进行 Insert Buffer 和辅助索引页子节点的 merge 操作。如此一来多个插入合并到一个操作中(因为在一个索引页中)。 条件: 1. 索引是辅助索引; 2. 索引不是唯一的。 Insert Buffer 是一个 B+ 树 Double Write Adaptive Hash Index Async IO Flush Neighbor Page 覆盖索引 覆盖索引(covering index ,或称为索引覆盖)即从非主键索引中就能查到的记录,而不需要查询主键索引中的记录,避免了回表的产生减少了树的搜索次数,显著提升性能 + + + diff --git a/public/tags/sql/page/1/index.html b/public/tags/sql/page/1/index.html new file mode 100644 index 0000000..0b73a57 --- /dev/null +++ b/public/tags/sql/page/1/index.html @@ -0,0 +1,10 @@ + + + + http://localhost:1313/tags/sql/ + + + + + + diff --git a/public/tags/vim/index.html b/public/tags/vim/index.html new file mode 100644 index 0000000..76ebf7e --- /dev/null +++ b/public/tags/vim/index.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Vim - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + + + +

#Vim

+ + + + + + + + + + + + +
+ +

Vim 进阶

+ + Vim 进阶 +
+ + + + + + +
+ + + + + diff --git a/public/tags/vim/index.xml b/public/tags/vim/index.xml new file mode 100644 index 0000000..c71a7b6 --- /dev/null +++ b/public/tags/vim/index.xml @@ -0,0 +1,19 @@ + + + + Vim on greathongtu 的 Blog + http://localhost:1313/tags/vim/ + Recent content in Vim on greathongtu 的 Blog + Hugo -- gohugo.io + en-us + Thu, 16 Nov 2023 14:39:47 +0800 + + + Vim 进阶 + http://localhost:1313/posts/vim/ + Thu, 16 Nov 2023 14:39:47 +0800 + http://localhost:1313/posts/vim/ + Repeat using . and ; // use &#39; to revert ;, u to revert . var foo = &#34;method(&#34;+argument1+&#34;,&#34;+argument2+&#34;)&#34;; f+ to Move, s&lt;space&gt;+&lt;space&gt;&lt;Esc&gt; to Change. use ; to repeat search and . to repeat change var foo = &#34;method(&#34; + argument1 + &#34;,&#34; + argument2 + &#34;)&#34;; Dot Formula The Ideal: one keystroke to Move, one keystroke to Execute. Act, Repeat, Reverse Operator + Motion = Action (dw、guaw、gcap) Misc 如何在每行的最后加上分号? 先A; 然后就可以重复j. + + + diff --git a/public/tags/vim/page/1/index.html b/public/tags/vim/page/1/index.html new file mode 100644 index 0000000..da64300 --- /dev/null +++ b/public/tags/vim/page/1/index.html @@ -0,0 +1,10 @@ + + + + http://localhost:1313/tags/vim/ + + + + + + diff --git a/public/tags/wsl/index.html b/public/tags/wsl/index.html new file mode 100644 index 0000000..91bb70e --- /dev/null +++ b/public/tags/wsl/index.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Wsl - greathongtu 的 Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ + +
+ + + + +

#Wsl

+ + + + + + + + + + + + +
+ +

wsl 代理问题

+ + wsl 代理问题 +
+ + + + + + +
+ + + + + diff --git a/public/tags/wsl/index.xml b/public/tags/wsl/index.xml new file mode 100644 index 0000000..a655a37 --- /dev/null +++ b/public/tags/wsl/index.xml @@ -0,0 +1,19 @@ + + + + Wsl on greathongtu 的 Blog + http://localhost:1313/tags/wsl/ + Recent content in Wsl on greathongtu 的 Blog + Hugo -- gohugo.io + en-us + Fri, 17 Nov 2023 00:41:53 +0800 + + + wsl 代理问题 + http://localhost:1313/posts/wsl-network/ + Fri, 17 Nov 2023 00:41:53 +0800 + http://localhost:1313/posts/wsl-network/ + 问题 win11 使用 vs code 连接 wsl 的时候,一些 vs code 的拓展无法连接网络,报错: Failed to establish a socket connection to proxies: [&quot;PROXY 127.0.0.1:7890&quot;] 或者 go build 因为网络问题无法继续。 原因 Windows 开启 Clash 之后,Windows 系统代理一般被设置为 127.0.0.1:7890,VS Code 会继承这个代理设置,wsl 中的 vscode-server 也继承了这个代理设置。但是 wsl 在 127.0.0.1:7890 上并没有代理服务,导致 vscode-server 联网失败。所以 wsl 中正确设置 “http_proxy” 和 “https_proxy” 环境变量可以解决问题。 解决办法 在 wsl 中正确设置 path # ~/.bashrc # 获取 Host IP WINDOWS_IP=$(ip route | grep default | awk &#39;{print $3}&#39;) PROXY_HTTP=&#34;http://${WINDOWS_IP}:7890&#34; # 设置环境变量 export http_proxy=&#34;${PROXY_HTTP}&#34; export https_proxy=&#34;${PROXY_HTTP}&#34; # ~/. + + + diff --git a/public/tags/wsl/page/1/index.html b/public/tags/wsl/page/1/index.html new file mode 100644 index 0000000..40adfa0 --- /dev/null +++ b/public/tags/wsl/page/1/index.html @@ -0,0 +1,10 @@ + + + + http://localhost:1313/tags/wsl/ + + + + + +