[EBPF] 实时捕获DM数据库是否存在SQL阻塞

news2024/11/17 13:27:05

1. 介绍

eBPF(extened Berkeley Packet Filter)是一种内核技术,它允许开发人员在不修改内核代码的情况下运行特定的功能。eBPF 的概念源自于 Berkeley Packet Filter(BPF),后者是由贝尔实验室开发的一种网络过滤器,可以捕获和过滤网络数据包。

eBPF 可以在不侵入任何业务代码的基础上实现应用的可观测性。但是 eBPF 对 Linux 内核版本是有一定要求的(4.14 以上)。

2. 工作原理

eBPF 的工作原理主要分为三个步骤:加载、编译和执行。

eBPF 需要在内核中运行。这通常是由用户态的应用程序完成的,它会通过系统调用来加载 eBPF 程序。在加载过程中,内核会将 eBPF 程序的代码复制到内核空间。

eBPF 程序需要经过编译和执行。这通常是由Clang/LLVM的编译器完成,然后形成字节码后,将用户态的字节码装载进内核,并通过一个JIT编译步骤将程序的通用字节码转换为机器特定指令集,以优化程序的执行速度。

在内核中运行时,eBPF 程序通常会挂载到一个内核钩子(hook)上,以便在特定的事件发生时被执行。例如,可以将 eBPF 程序挂载到网络协议栈的某个位置,以便在收到网络数据包时被执行。

最后,eBPF 程序还需要经过内核安全机制的检查。这是为了确保 eBPF 程序不会破坏内核的稳定性和安全性。在检查过程中,内核会对 eBPF 程序的代码进行分析,以确保它不会进行恶意操作,如系统调用、内存访问等。如果 eBPF 程序通过了内核安全机制的检查,它就可以在内核中正常运行了。在运行过程中,eBPF 程序可以访问内核的数据结构,并通过内核接口与其他组件进行交互。例如,eBPF 程序可以捕获网络数据包,并通过内核接口将它们转发给用户态的应用程序。总之,eBPF 的工作原理是通过动态加载、执行和检查无损编译过的代码来实现的。

3.环境介绍

名称版本
linux版本(centos8.5)4.18.0-348.el8.x86_64
DM88.1.4.27
pythonPython 3.6.8

4.安装环境

4.1检查环境

CONFIG_DEBUG_INFO_BTF=y必须项,否则就是操作系统不支持

[root@localhost opt]# uname -r
4.18.0-348.el8.x86_64
[root@localhost opt]# cat /boot/config-`uname -r` | grep CONFIG_DEBUG_INFO_BTF
CONFIG_DEBUG_INFO_BTF=y

4.2安装依赖包

[root@localhost opt]# dnf install -y bison cmake ethtool flex git iperf3 libstdc++-devel python3-netaddr python3-pip gcc gcc-c++ make zlib-devel elfutils-libelf-devel
[root@localhost opt]# dnf install -y clang clang-devel llvm llvm-devel llvm-static ncurses-devel
[root@localhost opt]# dnf -y install netperf
[root@localhost opt]# pip3 install pyroute2
[root@localhost opt]# ln -s /usr/bin/python3 /usr/bin/python

4.3安装并编译bcc

[root@localhost opt]# git clone https://github.com/iovisor/bcc.git
[root@localhost opt]# mkdir bcc-build
[root@localhost opt]# cd bcc-build/
[root@localhost opt]# cmake ../bcc -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_LLVM_SHARED=1
[root@localhost opt]# make -j10
[root@localhost opt]# make install 

4.4添加环境变量

安装后,可以将 bcc 目录添加到$PATH,可以将其添加到 ~/.bashrc

[root@localhost opt]# vim ~/.bashrc
bcctools=/usr/share/bcc/tools
export PATH=$bcctools:$PATH
[root@localhost opt]# source ~/.bashrc

4.5安装DM并初始化实例

此步骤忽略

5.思路

5.1模拟阻塞场景

建表sql

create table lock_test01(id int primary key, name varchar(20));
create table lock_test02(id int primary key, name varchar(20));

insert into lock_test01(id, name) values(1, '1cheng');
insert into lock_test01(id, name) values(2, '1gao');

insert into lock_test02(id, name) values(1, '2cheng');
insert into lock_test02(id, name) values(2, '2gao');

阻塞场景

-- Session A 执行insert 不提交
insert into "SYSDBA"."LOCK_TEST01"("ID", "NAME")  VALUES(3, '3zzzz');
-- Session B 执行insert  会发生阻塞
insert into "SYSDBA"."LOCK_TEST01"("ID", "NAME")  VALUES(3, '4zzzz');

5.2判断堆栈中的核心函数

下面的堆栈是阻塞SQL的线程堆栈

Thread 127 (Thread 0x7fb29dfeb700 (LWP 19089)):
#0  0x00007fb64d8a77e8 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1  0x0000000000752f72 in os_event2_wait_timeout (event=event@entry=0x7fb6315e1fb0, s_time=s_time@entry=30) at /home/dmops/build/svns/1725521374498/os/osevent2.c:303
#2  0x0000000001e571cb in uevent_wait_timeout (env=env@entry=0x7fb29dfe6020, event=event@entry=0x7fb6315e1fa8, s_time=s_time@entry=30) at /home/dmops/build/svns/1725521374498/knl/uevent.c:111
#3  0x0000000000ccc186 in trx4_waiting_timeout (env=env@entry=0x7fb29dfe6020, trx=trx@entry=0x7fb6315e0898, w_time=w_time@entry=3) at /home/dmops/build/svns/1725521374498/trx/trx4.c:14642
#4  0x0000000000ccc32a in trx4_waiting_interval (env=env@entry=0x7fb29dfe6020, trx=trx@entry=0x7fb6315e0898, s_time=s_time@entry=0) at /home/dmops/build/svns/1725521374498/trx/trx4.c:14217
#5  0x0000000000cd69e4 in trx4_waiting (env=env@entry=0x7fb29dfe6020, trx=trx@entry=0x7fb6315e0898, s_time=s_time@entry=0) at /home/dmops/build/svns/1725521374498/trx/trx4.c:14383
#6  0x00000000018446f0 in nins2_check_second_unique (memobj=memobj@entry=0x7fb2e000d3b8, trx=trx@entry=0x7fb6315e0898, ptx=ptx@entry=0x7fb29dfdd2c0, pbc=pbc@entry=0x7fb29dfdcf30, bcur=bcur@entry=0x7fb29dfdce90, key=key@entry=0x7fb34c417a60, desc=desc@entry=0x7fb2ee14c098, tableid=tableid@entry=1017, search_mode=search_mode@entry=2, need_lock_row=need_lock_row@entry=1, cflt_rowid=cflt_rowid@entry=0x0, need_modify=need_modify@entry=0x7fb29dfdfb6c, pll_index_fill=pll_index_fill@entry=0) at /home/dmops/build/svns/1725521374498/op/nins2.c:8616
#7  0x0000000001847789 in nins2_second_unique_search_and_check (memobj=memobj@entry=0x7fb2e000d3b8, trx=trx@entry=0x7fb6315e0898, ptx=ptx@entry=0x7fb29dfdd2c0, pbc=pbc@entry=0x7fb29dfdcf30, index=index@entry=0x7fb2ee14bd70, table=table@entry=0x7fb2ee14aae8, key=key@entry=0x7fb34c417a60, need_lock_row=need_lock_row@entry=1, search_mode=search_mode@entry=2, desc=desc@entry=0x7fb2ee14c098, rec_len=19, rowid=rowid@entry=4, ign_cflt=ign_cflt@entry=0, cflt_rowid=cflt_rowid@entry=0x0, need_modify=need_modify@entry=0x7fb29dfdfb6c, bcur=bcur@entry=0x7fb29dfdce90, pll_index_fill=pll_index_fill@entry=0) at /home/dmops/build/svns/1725521374498/op/nins2.c:10126
#8  0x0000000001848fd9 in nins2_second_unique_insert_low (memobj=memobj@entry=0x7fb2e000d3b8, trx=0x7fb6315e0898, ptx=0x7fb29dfdd2c0, pbc=0x7fb29dfdcf30, index=index@entry=0x7fb2ee14bd70, table=0x7fb2ee14aae8, tuple=tuple@entry=0x7fb34c417998, key=key@entry=0x7fb34c417a60, need_lock_row=need_lock_row@entry=1, search_mode=search_mode@entry=2, n_rol_len_fixed_part=n_rol_len_fixed_part@entry=45, rowid=rowid@entry=4, with_trxid=with_trxid@entry=0, ign_cflt=ign_cflt@entry=0, cflt_rowid=cflt_rowid@entry=0x0, need_modify=need_modify@entry=0x7fb29dfdfb6c, bcur=bcur@entry=0x7fb29dfdce90, pll_index_fill=pll_index_fill@entry=0, org_trx_id_out=org_trx_id_out@entry=0x0) at /home/dmops/build/svns/1725521374498/op/nins2.c:10222
#9  0x00000000018492c5 in nins2_second_unique_insert (memobj=memobj@entry=0x7fb2e000d3b8, trx=<optimized out>, ptx=<optimized out>, pbc=<optimized out>, index=index@entry=0x7fb2ee14bd70, table=<optimized out>, tuple=0x7fb34c417998, key=0x7fb34c417a60, need_lock_row=1, search_mode=2, n_rol_len_fixed_part=45, rowid=4, ign_cflt=0, cflt_rowid=0x0, need_modify=0x7fb29dfdfb6c, bcur=0x7fb29dfdce90, pll_index_fill=0) at /home/dmops/build/svns/1725521374498/op/nins2.c:10267
#10 0x0000000001849a39 in nins2_index_insert_entry_low (memobj=memobj@entry=0x7fb2e000d3b8, trx=<optimized out>, ptx=ptx@entry=0x7fb29dfdd2c0, pbc=pbc@entry=0x7fb29dfdcf30, index=index@entry=0x7fb2ee14bd70, tuple=tuple@entry=0x7fb34c417998, key=0x7fb34c417998, key@entry=0x7fb34c417a60, need_lock_row=1279359584, need_lock_row@entry=1, search_mode=1, search_mode@entry=2, n_rol_len_fixed_part=2, n_rol_len_fixed_part@entry=45, rowid=0x7fb20000002d, rowid@entry=0x7fb29dfdff50, ign_cflt=4, ign_cflt@entry=0, cflt_rowid=cflt_rowid@entry=0x0, need_modify=0x0, need_modify@entry=0x7fb29dfdfb6c, table=0x7fb29dfdfb6c, table@entry=0x7fb2ee14aae8, with_rowid=2650656400, with_rowid@entry=0, vm_node=0x7fb300000000, vm_node@entry=0x7fb34c416fd0, bcur=bcur@entry=0x7fb29dfdce90, pll_index_fill=0) at /home/dmops/build/svns/1725521374498/op/nins2.c:10508
#11 0x000000000185c1f8 in nins2_index_insert_entry (env=0x7fb29dfe6020, memobj=0x7fb2e000d3b8, vm=vm@entry=0x7fb2e000d350, index=index@entry=0x7fb2ee14bd70, tuple=0x7fb34c417998, key=key@entry=0x7fb34c417a60, need_lock_low=1, n_rol_len_fixed_part=45, rowid=rowid@entry=0x7fb29dfdff50, ign_cflt=ign_cflt@entry=0, cflt_rowid=cflt_rowid@entry=0x0, need_modify=need_modify@entry=0x7fb29dfdfb6c, table=0x7fb2ee14aae8, with_rowid=0, vm_node=vm_node@entry=0x7fb34c416fd0) at /home/dmops/build/svns/1725521374498/op/nins2.c:11889
#12 0x000000000185ceea in nins2_exec_insert_low (nins2_vm=nins2_vm@entry=0x7fb34c416fd0) at /home/dmops/build/svns/1725521374498/op/nins2.c:12796
#13 0x000000000185da5f in nins2_exec_insert (nins2_vm=nins2_vm@entry=0x7fb34c416fd0) at /home/dmops/build/svns/1725521374498/op/nins2.c:13492
#14 0x000000000186077f in nins2_exec (nins2_vm=0x7fb34c416fd0) at /home/dmops/build/svns/1725521374498/op/nins2.c:18904
#15 0x000000000195eab8 in vm_run_low (vm=vm@entry=0x7fb2e000d350) at /home/dmops/build/svns/1725521374498/op/vm.c:6115
#16 0x000000000195ef30 in vm_run (vm=vm@entry=0x7fb2e000d350) at /home/dmops/build/svns/1725521374498/op/vm.c:6196
#17 0x000000000195f080 in vm_run_pln_low (env=env@entry=0x7fb29dfe6020, stmt=stmt@entry=0x7fb2e000c8f0, pln=pln@entry=0x7fb2ee138ac8, ret_ident_flag=ret_ident_flag@entry=0 '\000', n_ret_col=n_ret_col@entry=0, ret_col_ident=ret_col_ident@entry=0x0, err_desc=err_desc@entry=0x0) at /home/dmops/build/svns/1725521374498/op/vm.c:11602
#18 0x0000000001fae1d0 in ntsk_process_exec_low (env=env@entry=0x7fb29dfe6020, stmt=0x7fb2e000c8f0, pln=pln@entry=0x7fb2ee138ac8, ret_ident_flag=ret_ident_flag@entry=0 '\000', n_ret_col=<optimized out>, ret_col_ident=0x0, dlck_reprepare=dlck_reprepare@entry=0x7fb29dfe46bc) at /home/dmops/build/svns/1725521374498/mgr/ntsk.c:12603
#19 0x0000000001fb2256 in ntsk_process_prepare_and_exec (env=env@entry=0x7fb29dfe6020, sess=sess@entry=0x7fb2e00110f0, msg_in=msg_in@entry=0x7fb2e00029a8 "", stmtsql_out=stmtsql_out@entry=0x7fb29dfe57c8) at /home/dmops/build/svns/1725521374498/mgr/ntsk.c:13063
#20 0x0000000001fde785 in ntsk_process_cop (env=0x7fb29dfe6020, task=<optimized out>) at /home/dmops/build/svns/1725521374498/mgr/ntsk.c:21389
#21 0x0000000001e5b755 in uthr_db_main_for_sess (sess2=0x7fb2e00110f0) at /home/dmops/build/svns/1725521374498/knl/uthr.c:1367
#22 0x00007fb64d8a117a in start_thread () from /lib64/libpthread.so.0
#23 0x00007fb64ccb5dc3 in clone () from /lib64/libc.so.6
  1. 头部 pthread_cond_timedwait 和 uevent_wait_timeout 都是自定义函数,而不是标准库提供的系统函数。

2) os_event2_wait_timeout:这是一个封装函数,它调用 pthread_cond_timedwait 并接受一个事件和超时时间(30秒)。

  1. uevent_wait_timeout:这个函数在内核中用于等待 uevent 事件,传入的参数包括环境指针和事件指针,以及超时时间。

  2. trx4_waiting_timeout:这个函数可能是一个事务相关的等待函数,传入了环境、事务和等待时间(3秒)

根据函数命名以及堆栈的含义,pthread_cond_timedwait 和 uevent_wait_timeout 肯定是代码底层的通用函数,所以初步判断SQL阻塞使用 trx4_waiting_timeout 函数就可以 我们此时进行测试。

6.编写代码

[root@localhost opt]# vim ebpf_trx.py

from bcc import BPF
  
# 定义 eBPF 程序
bpf_text = """
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
#include <linux/timekeeping.h>

BPF_HASH(start_time, u64);

// 处理 uprobe 事件
int trace_ntsk_process_cop(struct pt_regs *ctx) {
    u64 pid_tgid = bpf_get_current_pid_tgid(); // 获取 PID 和 TGID
    u64 thread_id = pid_tgid >> 32; // 获取线程号
    u64 timestamp = bpf_ktime_get_ns();

    // 记录开始时间
    start_time.update(&pid_tgid, &timestamp);
    bpf_trace_printk("ntsk_process_cop called: pid=%d, thread_id=%d, timestamp=%llu", pid_tgid & 0xFFFFFFFF, thread_id, timestamp);
    return 0;
}

// 处理返回事件
int trace_ret_ntsk_process_cop(struct pt_regs *ctx) {
    u64 pid_tgid = bpf_get_current_pid_tgid(); // 获取 PID 和 TGID
    u64 thread_id = pid_tgid >> 32; // 获取线程号
    u64 *start_ts = start_time.lookup(&pid_tgid);

    if (start_ts != 0) {
        u64 end_time = bpf_ktime_get_ns();
        u64 total_time = end_time - *start_ts;
        u64 total_time_ms = total_time / 1000000;
        bpf_trace_printk("ntsk_process_cop exited: pid=%d, thread_id=%d, total_time=%llu ms", pid_tgid & 0xFFFFFFFF, thread_id, total_time_ms);
        start_time.delete(&pid_tgid);
    }
    return 0;
}
"""

# 加载 eBPF 程序
b = BPF(text=bpf_text)

# 设置 uprobe
b.attach_uprobe(name="/opt/dmdbms/bin/dmserver", sym="trx4_waiting_timeout", fn_name="trace_ntsk_process_cop")
b.attach_uretprobe(name="/opt/dmdbms/bin/dmserver", sym="trx4_waiting_timeout", fn_name="trace_ret_ntsk_process_cop")

# 输出跟踪信息
print("正在跟踪 uprobe... 按 Ctrl-C 结束。")

# 输出追踪信息
b.trace_print()

代码解释

  • BPF_HASH(start_time, u64); 创建一个哈希表 start_time,键类型为 u64,用于存储每个进程的开始时间。它通过 PID(进程标识符)来索引
  • bpf_get_current_pid_tgid(): 获取当前进程的 PID 和线程 ID
  • attach_uprobe ()表示将钩子附加到dmserver的trx4_waiting_timeout函数的头部位置
  • attach_uretprobe()表示将钩子附加到dmserver的trx4_waiting_timeout函数的结尾位置
  • 在函数开始的时候存储开始时间在函数结尾时相减就是执行该函数的耗时,并输出PID 以及 threadID即可。

7.验证代码

[root@localhost opt]# python ebpf_trx.py 

验证结果说明

  • 运行程序以后,会发现只有当SQL阻塞的时候才会输出信息(进程号、线程号、函数的执行耗时) 说明对trx4_waiting_timeout函数添加钩子是没有问题
  • 运行时会发现每隔3S输出一次,添加符号文件的堆栈可以看到trx4_waiting_timeout函数的w_time=3,这块可以一一对应。
  • 有了这些信息就能够做很多事情,比如说将信息推送到监控平台等。

8.总结与思考

  1. 利用ebpf技术在不登录数据库的情况下,通过对数据库的函数添加钩子,实时的对数据库是否存在阻塞判断
  2. 现有的监控逻辑都是新建数据库用户执行SQL语句来判断阻塞情况,告警的实时性取决于监控的周期频率,而使用ebpf技术能够解决这个痛点
  3. ebpf有多种实现方式,这里便于理解测试的话 使用python语言内置c语言的形式进行了说明。涉及到生产环境应该是使用go语言或者c语言去做能够有效避免源码泄露等问题
  4. 如给sql线程添加钩子,有代sql变量的偏移量的话,就能够不登录数据库的情况下捕获到慢SQL,就能够有效的解决很多问题了,有待探究

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2171421.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安卓开发板_MTK开发板_联发科开发评估套件Demo板接口介绍

开发板是一种功能丰富的电路平台&#xff0c;专为开发人员设计&#xff0c;集成了多种传感器、扩展接口和通信模块。这使得开发者能够高效进行原型设计和功能验证&#xff0c;极大地简化了软硬件开发的过程。 此次介绍的安卓开发板由MT8788核心板与底板构成&#xff0c;特别之处…

mac怎么设置ip地址映射

最近开发的项目分为了两种版本&#xff0c;一个自己用的&#xff0c;一个是卖出去的。 卖出的域名是和自己的不一样的&#xff0c;系统中有一些功能是只有卖出去的版本有的&#xff0c;但我们开发完之后还得测试&#xff0c;那就需要给自己的电脑配置一个IP地址映射了&#xf…

力扣面试150 寻找峰值 二分

Problem: 162. 寻找峰值 &#x1f468;‍&#x1f3eb; 参考图解 class Solution {public int findPeakElement(int[] nums) {int l 0; // 初始化左边界&#xff0c;表示数组的起始位置int r nums.length - 1; // 初始化右边界&#xff0c;表示数组的结束位置// 当左边界和…

“AI+Security”系列第3期(六):打造最懂安全的智能体-无极AI安全智能体平台落地与实践

近日&#xff0c;由安全极客、Wisemodel 社区、InForSec 网络安全研究国际学术论坛和海升集团联合主办的 “AI Security” 系列第 3 期技术沙龙&#xff1a;“AI 安全智能体&#xff0c;重塑安全团队工作范式” 活动顺利举行。此次活动吸引了线上线下超过千名观众参与。 活动…

基于微信开发助手企鹅音乐微信小程序的设计与实现(源码+文档+讲解)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

pyside6与协程

目录 一、常见错误 错误一、使用协程函数作为槽函数。 错误二、在Qt循环中创建新的loop 二、解决方法&#xff1a; ①安装库qasync ②修改Qt入口 ③异步槽函数 ④异步函数 ⑤整体示例 一、常见错误 错误一、使用协程函数作为槽函数。 这样是肯定是不行&#xff…

BufferQueue低延迟优化,以及SurfaceView帧率上限问题解决

目录 了解BufferQueue 为什么会出现问题&#xff1f; 如何优化&#xff1f; 最近在做一个与音视频播放相关的项目&#xff0c;使用到了MediaCodec解码后送到SurfaceView播放场景。发现SurfaceView播放上限是60HZ&#xff0c;不符合项目需求&#xff0c;故而进行了研究并找到…

Jupyter Notebook 产生 jupyter_notebook_config.py 配置文件

Jupyter Notebook 产生 jupyter_notebook_config.py 配置文件 引言正文第一步第二步第三步引言 今天遇到了一个问题,当我安装了 VS code,之后又在 VS code 中安装了 Jupyter notebook 的 extension。之后可以在 VS code 中正常使用 Jupyter Notebook 的所有功能。但是,当我…

sqli-labs时间盲注、布尔盲注脚本

script.py&#xff1a;提供参数&#xff0c;用于调用布尔盲注或时间注入的函数 import time_type import bool_type ​ ​ # inject_type: 1.布尔盲注2.时间注入 # http_type&#xff1a;1.GET请求2.POST请求 # dict_para_data&#xff1a;所有的参数&#xff0c;和默认值 # v…

打靶记录18——narak

靶机: https://download.vulnhub.com/ha/narak.ova 推荐使用 VM Ware 打开靶机 难度&#xff1a;中 目标&#xff1a;取得 root 权限 2 Flag 攻击方法&#xff1a; 主机发现端口扫描信息收集密码字典定制爆破密码Webdav 漏洞PUT 方法上传BF 语言解码MOTD 注入CVE-2021-3…

SQL | 查询带有单引号的异常数据和replace与insert插入的区别

学习SQL 查找单引号异常数据知识点 replace 和 insertinsert 插入 timestamp 参数 查找单引号异常数据 表数据 代码 SELECT * FROM sys_barcode WHERE name LIKE %%;知识点 单引号&#xff08;&#xff09;在 SQL 查询中需要使用两个连续的单引号&#xff08;&#xff09;来表…

Vue3.X + SpringBoot小程序 | AI大模型项目 | 饮食陪伴官

gitee平台源码 github平台源码 饮食陪伴师是一个管理饮食的原生大模型小程序&#xff0c;优势&#xff1a; 精确营养监控&#xff1a;用户记录饮食后&#xff0c;我们会计算出食用的营养成分与分量&#xff0c;并反馈给用户。饮食建议有效&#xff1a;大模型经过我们训练具备大…

003、网关路由问题

1. nginx配置404跳转回默认路由 https://blog.csdn.net/masteryee/article/details/83689954 https://blog.csdn.net/IbcVue/article/details/133230460 https://www.jb51.net/server/317970ynk.htm https://blog.csdn.net/u014438244/article/details/120531287 https://blog…

光耦合器在信号传输和隔离中的作用

光耦合器&#xff0c;也称为光隔离器&#xff0c;是电子电路中的关键元件&#xff0c;它结合了两个基本功能&#xff1a;信号传输和电气隔离。它们允许信号在电路的不同部分之间传递&#xff0c;同时保持它们彼此电气隔离。此功能对于保护敏感的低压控制电路免受更高电压、噪声…

ST188单光束反射式红外光电传感器心率测量原理

光电传感器心率测量原理 ST188传感器测量脉搏的具体原理如下&#xff1a; 当手指轻轻按压在ST188红外光电传感器上时&#xff0c;传感器内部的红外发射二极管会发出红外线。这些红外线穿透手指皮肤&#xff0c;照射到血液上。由于脉搏跳动时&#xff0c;血液的体积和压力会发生…

manjaro KDE桌面的使用

manjaro KDE桌面的使用 正儿八经的摆龙门阵 近些年不喜欢发博客的我&#xff0c;今天来一篇与开发扯不上关系的文章。这篇文章发布后&#xff0c;该文章不再做更新。有相关爱好的小伙伴们可以选择私信。 manjaro的介绍我就不用说了&#xff0c;想大家看到这里都是对manjaro有…

Python实战:爬取网页图片

文章目录 一、实战概述二、图片网站三、爬取图片1、编写程序&#xff0c;实现功能2、运行程序&#xff0c;查看结果 四、实战小结 一、实战概述 在本实战项目中&#xff0c;我们编写了一个Python程序&#xff0c;用于从指定的图片网站&#xff08;https://pic.netbian.com/4kf…

西圣、漫步者头戴式耳机哪个音质好?热门主流头戴式耳机专业评测

一直以来头戴式蓝牙耳机凭借其独特的优势&#xff0c;逐渐成为了音乐爱好者、游戏玩家以及日常通勤者的首选&#xff0c;它们不仅融合了卓越的音质体验、便捷的无线连接&#xff0c;还融入了先进的降噪技术和人性化的佩戴设计&#xff0c;为用户带来了前所未有的听觉盛宴与舒适…

第十一章 分布式存储之哈希算法

目录 一、哈希取余分区 二、一致性哈希算法分区 三、哈希槽分区 在学习通过Docker进行Redis集群部署之前&#xff0c;简单聊一点Redis分布式集群存储相关的哈希算法问题&#xff1a; 一、哈希取余分区 2亿条记录就是2亿个KV&#xff0c;我们单机不行必须要分布式多机&…

高分作品《基于Java+MySQL图书销售管理系统》+源代码+文档说明

文章目录 源代码下载地址项目介绍项目功能界面预览 项目备注源代码下载地址 源代码下载地址 点击这里下载源码 项目介绍 使用JavaSEJavaawtMySQL搭建的一个图书销售管理系统&#xff0c;主要涉及一个数据库&#xff0c;内含四张SQL表&#xff0c;管理员信息表&#xff0c;用…