WWW.YOUINFO.SITE
标签聚合 mysql

/tag/mysql

LinuxDo 最新话题 · 2026-06-11 17:33:51+08:00 · tech

NewAPI 的日志目前是单表存储,已经看到太多佬友因为日志撑爆磁盘了,下面给出一种解决方式,可能不完全兼容,但是个人测试下来没有什么问题 与 NewAPI 版本无关,基本所有版本都可以用,我用的版本是 1.0.0-rc.10 自带的日志删除 最常用的是使用 NewAPI 自带的 “清理历史日志” 来进行删除 // 代码逻辑如下 // 直接按照时间戳删除 LOG_DB.Where("created_at < ?", targetTimestamp).Limit(limit).Delete(&Log{}) 这里有两个问题: Delete 的效率不高,或者说在数据量过大的情况下使用 Delete 带来的 IO 可能成为灾难 Delete 之后磁盘空间并不会回收,需要手动回收 通过数据库分片解决日志删除 适用于 MySQL,如果您是其他的 DB 如 Postgres / SQLite,将下面的内容提供给 AI 相信也会有对应的解决方案 分片的核心逻辑是,将日志拆分到对应的日期上,比如分片 logs_20260601 只存储当天的数据,在超出需要删除的日期后,比如 1 个月后的 2026/07/01,直接删除 logs_20260601 分片,分片占用的空间会立刻释放,对应的日志数据也会直接删除 不过这里要提示您,所有的 DB 变更都可能引入风险,请您在执行前通过 mysqldump 或者类似工具,导出完整的数据结构和数据内容,确保执行出现异常可以随时回滚 执行下面的步骤前,建议停机,否则可能导致数据不完整 ,如果数据完整性不在考虑范围内,也可以在线更新 Step 1 检查数据符合预期 (Read) 执行下面的查询语句,确保 count 为 0,即所有的日志的创建时间不为空 SELECT COUNT(*) AS null_created_at_count FROM logs WHERE created_at IS NULL; Step 2 创建临时表 (Write) CREATE TABLE logs_new LIKE logs; Step 3 添加准备分区语句 (Read) 通过下面的 SQL,可以获得一个建表 SQL,对 DB 无任何副作用,可以放心执行 下面的 @start_date 和 @end_date 两行,可以按照您的实际数据存储情况调整,下面的配置为创建 180 天前到 7 天后的分区,如果您的日志已经回收或删除过了,可以考虑缩减分区数量 SET time_zone = '+08:00'; 这里可以按照您的需求调整为具体的时区 分区必须提前创建,否则插入数据会有问题 ,所以您至少需要创建 7 天后的备用 SET SESSION group_concat_max_len = 1024 * 1024; SET time_zone = '+08:00'; SET @start_date = DATE_SUB(CURDATE(), INTERVAL 180 DAY); SET @end_date = DATE_ADD(CURDATE(), INTERVAL 7 DAY); WITH RECURSIVE dates AS ( SELECT @start_date AS d UNION ALL SELECT DATE_ADD(d, INTERVAL 1 DAY) FROM dates WHERE d < @end_date ) SELECT GROUP_CONCAT( CONCAT( ' PARTITION p', DATE_FORMAT(d, '%Y%m%d'), ' VALUES LESS THAN (', UNIX_TIMESTAMP(DATE_ADD(d, INTERVAL 1 DAY)), ')' ) ORDER BY d SEPARATOR ',\n' ) INTO @parts FROM dates; SET @sql = CONCAT( 'ALTER TABLE logs_new MODIFY `created_at` bigint NOT NULL, DROP PRIMARY KEY, DROP INDEX `idx_created_at_id`, ADD PRIMARY KEY (`id`, `created_at`) PARTITION BY RANGE (`created_at`) ( ', @parts, ', PARTITION pmax VALUES LESS THAN MAXVALUE )' ); SELECT @sql; Step 4 执行分区语句 (Write) 执行上一步输出的 SQL,会将新的 logs_new 表调整为分区表 Step 5 导入旧的数据 (Write) 取决于您的数据量,这一步可能会花费一些时间 INSERT INTO logs_new SELECT * FROM logs; Step 6 校验数据已经导入完成 (Read) 如果您是停机更新,确保两条 SQL 输出的内容是一致的 如果您是在线更新,确保数据接近或一致 SELECT "logs" as `table`, COUNT(*), MIN(created_at), MAX(created_at) FROM logs UNION SELECT "logs_new" as `table`, COUNT(*), MIN(created_at), MAX(created_at) FROM logs_new; Step 7 确认分区 (Read) 下面的 SQL 会打印新表的所有分区,以及每个分区的数据量 SELECT PARTITION_NAME, PARTITION_DESCRIPTION, TABLE_ROWS FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'logs_new' ORDER BY PARTITION_ORDINAL_POSITION; Step 8 切表 (Write) 切换流量到新表,执行完成后请检查 NewAPI 各组件各页面是否能正常工作,特别是依赖日志的内容 RENAME TABLE logs TO logs_old, logs_new TO logs; Step 9 创建自动回收创建/分区任务 (Write) MySQL 不会自动创建或者删除分区,需要您创建定时任务来实现,下面会给出创建/删除分区的任务,您可以按需添加 Step 9.1 开启 MySQL Event Scheduler 在数据库中执行下面的命令,通常需要 root 用户,无需重启 SET GLOBAL event_scheduler = ON; SHOW VARIABLES LIKE 'event_scheduler'; 在配置文件的 mysqld 章节,增加下面的配置,无需重启 这一步是为了保证,即使后面重启数据库,Event Scheduler 仍然会是开启状态 [mysqld] event_scheduler=ON Step 9.2 创建新建分区任务 下面的两个 SQL 都需要执行 第一个 SQL 中的 SET time_zone = '+08:00'; 可按需修改为您的时区 第二个 SQL 中的时间您可以修改为适用于您的服务的时间,目前设置的是每天的 02:00,用于控制定时触发的时机 DELIMITER $$ DROP PROCEDURE IF EXISTS sp_logs_create_future_partitions$$ CREATE PROCEDURE sp_logs_create_future_partitions() BEGIN DECLARE v_i INT DEFAULT 0; DECLARE v_d DATE; DECLARE v_partition_name VARCHAR(32); DECLARE v_less_than BIGINT; DECLARE v_exists INT DEFAULT 0; DECLARE v_sql TEXT; SET time_zone = '+08:00'; WHILE v_i <= 7 DO SET v_d = DATE_ADD(CURDATE(), INTERVAL v_i DAY); SET v_partition_name = CONCAT('p', DATE_FORMAT(v_d, '%Y%m%d')); SET v_less_than = UNIX_TIMESTAMP(DATE_ADD(v_d, INTERVAL 1 DAY)); SELECT COUNT(*) INTO v_exists FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'logs' AND PARTITION_NAME = v_partition_name; IF v_exists = 0 THEN SET v_sql = CONCAT( 'ALTER TABLE logs REORGANIZE PARTITION pmax INTO (', 'PARTITION ', v_partition_name, ' VALUES LESS THAN (', v_less_than, '), ', 'PARTITION pmax VALUES LESS THAN MAXVALUE', ')' ); SET @sql = v_sql; PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; END IF; SET v_i = v_i + 1; END WHILE; END$$ DELIMITER ; DROP EVENT IF EXISTS ev_logs_create_future_partitions; CREATE EVENT ev_logs_create_future_partitions ON SCHEDULE EVERY 1 DAY STARTS TIMESTAMP(CURRENT_DATE, '02:00:00') DO CALL sp_logs_create_future_partitions(); Step 9.3 创建删除分区任务 (可选) 这个是用于替代 NewAPI 自带的删除数据任务,如果您有需要可以配置这里的自动删除 第一个 SQL 中的 INTERVAL 180 DAY 表示删除 180 天之前的数据,可以按需修改, SET time_zone = '+08:00'; 也可按需修改为您的时区 第二个 SQL 中的时间您可以修改为适用于您的服务的时间,目前设置的是每天的 03:00,用于控制定时触发的时机 DELIMITER $$ DROP PROCEDURE IF EXISTS sp_logs_drop_old_partitions$$ CREATE PROCEDURE sp_logs_drop_old_partitions() BEGIN DECLARE v_cutoff_date DATE; DECLARE v_cutoff_ts BIGINT; DECLARE v_drop_partitions TEXT; DECLARE v_sql TEXT; SET time_zone = '+08:00'; SET v_cutoff_date = DATE_SUB(CURDATE(), INTERVAL 180 DAY); SET v_cutoff_ts = UNIX_TIMESTAMP(v_cutoff_date); SELECT GROUP_CONCAT(PARTITION_NAME ORDER BY PARTITION_ORDINAL_POSITION) INTO v_drop_partitions FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'logs' AND PARTITION_NAME REGEXP '^p[0-9]{8}$' AND CAST(PARTITION_DESCRIPTION AS UNSIGNED) <= v_cutoff_ts; IF v_drop_partitions IS NOT NULL AND v_drop_partitions <> '' THEN SET v_sql = CONCAT( 'ALTER TABLE logs DROP PARTITION ', v_drop_partitions ); SET @sql = v_sql; PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; END IF; END$$ DELIMITER ; DROP EVENT IF EXISTS ev_logs_drop_old_partitions; CREATE EVENT ev_logs_drop_old_partitions ON SCHEDULE EVERY 1 DAY STARTS TIMESTAMP(CURRENT_DATE, '00:10:00') DO CALL sp_logs_drop_old_partitions(); Step 9.4 检查任务 show events; SHOW PROCEDURE STATUS WHERE Db = DATABASE(); Step 10 大功告成 一切已准备就绪,请使用吧!数据库将按照您的配置自动创建新的分片,回收旧的分片,后续如果有调整,也可以直接修改 SQL 配置再次执行。 您可以定期通过下面的 SQL 来检查分区任务的运行状态和分区的数据量,请检查 pmax 分区数据量为 0,且已经创建了 7 天后的分区 SELECT PARTITION_NAME, PARTITION_DESCRIPTION, TABLE_ROWS FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'logs' ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 15; 1 个帖子 - 1 位参与者 阅读完整话题

LinuxDo 最新话题 · 2026-05-27 00:58:33+08:00 · tech

我经常在手机上用termux倒腾一点东西嘛,这几年来来去去玩的东西不少,不过留下来的不多 下面给大家汇总介绍一些 在这里提醒一下,手机本身散热就不大行,加上供电需要改;如果纯软件上也要root,说实话软件控制效果不好,要长期玩还是得改硬件供电、散热,或者日常只开着几个服务,这里介绍的不涉及root,只要你手机能装termux就能用; 我这套配置在局域网下是可以正常运行的,当然不是全开,日常就 couchdb 开着同步obsidian, nginx 开着做导航, vaultwarden+sqlite 开着; llamacpp开着Bge-m3-Q8.gguf 但是很少运行, sqlite 可以换成 MySQL 如果你同时开着 NextCloud 的话,不过nextcloud的云盘功能很大程度上被obsidian的同步给替换了,毕竟nextcloud要同时开着 apache2 | mariadb-server | nextcloud | nginx 嘛,相对来说太繁夯了; 佬们,日常用的前提必须是稳的、稳定、稳定;如果不改供电和散热的话不要贪多,只开两三个就可以了,而且定期重启一下,或者换平板来,实测比手机强的多。 设备是 一加8T(soc865 12+256) 日常25-30℃,不会太烫,有新文件向量处理的时候会烫一会,但是日常小文件完全没问题,如果有大量文件还是别折磨手机了, 真的会炸 通过couchdb做obsidian的同步方案 使用llamacpp来部署个 Bge-m3 向量模型来给obsidian的笔记用 vaultwarden密码管理器 局域网下假域名 使用comfyui部署Z-Image-Turbo来绘图 NextCloud自部署网盘 MySQL数据库 介绍从termux原始环境中部署的开始,如果你跳转到后边发现有依赖缺失可以到前边找找 termux初始化 termux从GitHub直接下 termux-GitHub 1. 需要用到的软件 我刚接触是跟着国光大佬的教程学的,但是我对终端美化没什么需求,就没改动,可以搜搜大佬的教程,可以下 zsh 之类的把终端改的很好看 # 选一个China或者Asian的源,方便 termux-change-repo # 更新一下先 pkg update && pkg upgrade -y # 加一些可能用到的源 pkg install tur-repo root-repo -y # 安装一些需要的软件 pkg install vim wget nginx tmux git git-lfs proot-distro uv unzip curl cmake npm -y 2. 安装Debian容器 termux的proot-distro更新后好像改了很多东西, # Version 5.0 release notes 用法上没什么改变,但是好像有些东西会报错 ## 下载Debian:12 # 这里可以开代理,会快很多 proot-distro install debian:12 # 进入 proot-distro login debian # 可以直接写个 bash脚本,能少敲几个词语 touch login_debian.sh echo 'proot-distro login debian' > login_debian.sh chmod +x login_debian.sh ./login_debian.sh 3. proot-distro容器与termux文件互传 因为proot-distro是个容器嘛,与termux原始环境是隔离的; 通过ln 建个链接可以把个目录在termux与proot-distro间联通 ## 这里是termux的~/目录,可以随便取名字 mkdir TheFiles cd TheFiles && pwd # >>得到地址,一般是 /data/data/com.termux/files/home/TheFiles ## 进入到了proot-distro容器的环境 # 目录是 /root/ # 当然如果你习惯用其他的目录位置也都可以 ln -s /data/data/com.termux/files/home/TheFiles ./ # 然后就可以在/root/目录下看到该目录了,这个目录是termux环境和proot-distro容器之间互通的,文件的更改也是同步的 # 在这里提醒一句,最好不要su获取root之后再进入proot-distro环境,这会导致很多文件的归属问题,不知道会有什么奇奇怪怪的bug 假域名 有这个需求是因为如果你的设备需要移动,接入不同的WiFi时自动分配的IP肯定会变,IP一变化很多东西都要重新绑定很麻烦 可以用python的工具 1. 用uv创建个python环境 # 先创建个目录,比如mdns啥的 # 然后再该目录下创建环境 uv venv # 进入python环境 source .venv/bin/activate 1.1 安装一下几个包 uv pip install ifaddr netifaces zeroconf 2. 创建文件 这里分成了两个, python文件是运行的,配置文件是config.json 文件你可以让AI给你改,很方便 2.1 mdns_auto.py import json import socket import netifaces from time import sleep from zeroconf import Zeroconf, ServiceInfo def get_interface_ip(interface_name="wlan2"): try: iface = netifaces.ifaddresses(interface_name) ipv4_info = iface[netifaces.AF_INET][0] return ipv4_info["addr"] except Exception as e: raise RuntimeError( f"Cannot get IP for interface: {interface_name}" ) from e # ============================================ # 加载配置文件 # ============================================ with open("config.json", "r") as f: config = json.load(f) # ============================================ # 获取 hostname # ============================================ hostname = config["hostname"] # ============================================ # 获取本机 IP # ============================================ #local_ip = get_local_ip() local_ip = get_interface_ip("wlan2") print(f"[INFO] Local IP: {local_ip}") # ============================================ # 启动 Zeroconf # ============================================ #zeroconf = Zeroconf() zeroconf = Zeroconf( interfaces=[local_ip] ) # ============================================ # 已注册服务列表 # ============================================ registered_services = [] # ============================================ # 遍历所有服务 # ============================================ for svc in config["services"]: name = svc["name"] service_type = svc["type"] port = svc["port"] path = svc["path"] description = svc["description"] full_service_name = f"{name}.{service_type}" print(f"[REGISTER] {name}:{port}") info = ServiceInfo( service_type, full_service_name, addresses=[socket.inet_aton(local_ip)], port=port, properties={ "path": path, "description": description, "server": hostname, "port": str(port) }, server=hostname, ) zeroconf.register_service(info) registered_services.append(info) print("[INFO] All services registered.") # ============================================ # 保持运行 # ============================================ try: while True: sleep(1) except KeyboardInterrupt: print("\n[INFO] Stopping services...") finally: # ======================================== # 注销所有服务 # ======================================== for svc in registered_services: zeroconf.unregister_service(svc) zeroconf.close() print("[INFO] Zeroconf stopped.") ~/AIFiles/mdns $ cat config.json 2.2 config.json示例如下 { "hostname": "your_self_doim.local.", "services": [ { "name": "WebPortal", "type": "_http._tcp.local.", "port": 8077, "path": "/", "description": "Web Navigation Portal" }, { "name": "CouchDB", "type": "_http._tcp.local.", "port": 5984, "path": "/", "description": "CouchDB Database" }, { "name": "LlamaCPP", "type": "_http._tcp.local.", "port": 8888, "path": "/v1/models", "description": "llama.cpp OpenAI API" }, { "name": "ComfyUI", "type": "_http._tcp.local.", "port": 7777, "path": "/", "description": "ComfyUI WebUI" }, { "name": "VSCodeServer", "type": "_http._tcp.local.", "port": 8080, "path": "/", "description": "VSCode Server" }, { "name": "ZeroClaw", "type": "_http._tcp.local.", "port": 8833, "path": "/", "description": "ZeroClaw WebUI" }, { "name": "SillyTavern", "type": "_http._tcp.local.", "port": 9988, "path": "/", "description": "SillyTavern UI" } ] } 运行 只需要开个tmux窗口然后在后台挂着就可以了 uv run python mdns_auto.py 这里的配置是假定你用该设备共享热点 llama.cpp部署并运行本地模型 1 编译安装 1.1 克隆项目 # 这里目录随意 git clone https://github.com/ggml-org/llama.cpp.git 1.2 编译 # 这里在llama.cpp项目目录下 cmake -B build cmake --build build/ --config Release -j4 1.3 llama-server等添加到环境变量 把这两行添加到环境变量 ## llama.cpp环境变量 export LD_LIBRARY_PATH=/data/data/com.termux/files/home/<your Path>/llama.cpp/build/bin:$LD_LIBRARY_PATH export PATH=/data/data/com.termux/files/home/<your-Path>/llama.cpp/build/bin:$PATH 刷新一下,然后就可以在termux随地使用 llama-cli | llama-server 等命令了 这里补充一下, llama-cli/llama-server 在运行时,可以用 -t1 -t2 -tn (n取小于你soc核心数的正整数) 等参数来选择调用的核心数量,手机的核心大小不一,只用大核心比全弄效果好很多。 nginx配置 nginx安装后,相关的配置文件在 $PREFIX/usr/etc/nginx/ 目录下; 配置和正常Linux一样,只是 include xxx.conf 引用配置的时候尽量用绝对目录,有时候容易出毛病,或者建个 conf.d 目录,直接用 include xxxxx/conf.d/*.conf 引用目录下文件,方便一点 可以用su获取root之后再运行,这样可以用1000以下的端口,如果不用root也能正常用,注意不要用1000以下的端口,因为没有权限 下面开始进入proot-distro容器部分 最好不要su获取root后进入proot-distro,会因为文件归属问题有时候会有奇奇怪怪的bug CouchDB数据库 1. couchdb数据库的安装 参照couchdb官方的示例 官方文档 1.1 添加官方的库 ## 其实我不太懂,不过官方文档是这么写的 # 1 apt update && apt install -y curl apt-transport-https gnupg # 2 curl https://couchdb.apache.org/repo/keys.asc | gpg --dearmor | tee /usr/share/keyrings/couchdb-archive-keyring.gpg >/dev/null 2>&1 # 3 source /etc/os-release # 4 echo "deb [signed-by=/usr/share/keyrings/couchdb-archive-keyring.gpg] https://apache.jfrog.io/artifactory/couchdb-deb/ ${VERSION_CODENAME} main" \ | tee /etc/apt/sources.list.d/couchdb.list >/dev/null 1.2 安装 # 先搜索一下确认库添加上了 apt search couchdb # 应该会新增这仨 couchdb/bookworm 3.5.2~bookworm arm64 RESTful document oriented database couchdb-dbgsym/bookworm 3.5.0~bookworm arm64 debug symbols for couchdb couchdb-nouveau/bookworm 3.5.2~bookworm arm64 Nouveau adds Lucene capabilities to CouchDB ## 用apt安装 apt install couchdb 1.3 couchdb初始化 这是紧接着安装之后,他会引导你初始化一些配置,安装好后在web界面都可以图形化配置 Configuring couchdb ------------------- Please select the CouchDB server configuration type that best meets your needs. For single-server configurations, select standalone mode. This will set up CouchDB to run as a single server. For clustered configuration, select clustered mode. This will prompt for additional parameters required to configure CouchDB in a clustered configuration. If you prefer to configure CouchDB yourself, select none. You will then need to edit /opt/couchdb/etc/vm.args and /opt/couchdb/etc/local.d/*.ini yourself. Be aware that this will bypass *all* configuration steps, including setup of a CouchDB admin user. You'll have to create one manually. 1. standalone 2. clustered 3. none ## 这里是问你1.单机运行 2.多机集群配置 3.等等再说 ,我是单机所以就直接1了 General type of CouchDB configuration: 1 A CouchDB node has an Erlang magic cookie value set at startup. This value must match for all nodes in the cluster. If they do not match, attempts to connect the node to the cluster will be rejected. ## 配置神奇曲奇(bushi)我理解是密码一类的东西,是不显示的,输入完成之后回车就可以 CouchDB Erlang magic cookie: [your password] A CouchDB node must bind to a specific network interface. This is done via IP address. Only a single address is supported at this time. The special value '0.0.0.0' binds CouchDB to all network interfaces. The default is 127.0.0.1 (loopback) for standalone nodes, and 0.0.0.0 (all interfaces) for clustered nodes. In clustered mode, it is not allowed to bind to 127.0.0.1. ## 这里问你是只允许127.0.0.1访问还是都允许,虽然说单个人的数据没什么太大的盗取价值,不过还是建议用127.0.0.1然后加个nginx反代 CouchDB interface bind address: 127.0.0.1 It is highly recommended that you create a CouchDB admin user, which takes CouchDB out of the insecure "admin party" mode. Entering a password here will take care of this step for you. If this field is left blank, an admin user will not be created. A pre-existing admin user will not be overwritten by this package. ## 这才是真正的账号-密码,当然初始化是管理员的账号密码,同样是隐藏的,直接输入完成后回车就可以,这里要多确认一次 Password for the CouchDB "admin" user: Repeat password for the CouchDB "admin" user: invoke-rc.d: could not determine current runlevel invoke-rc.d: policy-rc.d denied execution of start. WARNING: Unable to create standalone system databases. CouchDB may not have started correctly (no init?) Once CouchDB has started correctly, run the following: curl -X PUT --user '<admin-user>:<admin-pass>' http://127.0.0.1:5984/_users curl -X PUT --user '<admin-user>:<admin-pass>' http://127.0.0.1:5984/_replicator Processing triggers for libc-bin (2.36-9+deb12u14) ... 到这里就部署完成了,可以在 http://127.0.0.1:5984/_utls 页面查看后台管理页面 obsidian的设置可以在电脑端配置完后直接用二维码导入到手机,这样可以方便一点,毕竟手机的UI比较小,适配也不一定很合适,但是同步方面我在局域网下使用完全没有问题. 顺带说一下,我Obsidian使用的AI插件是站内大佬的YOLO,挺好用的; 同步使用的是self-host live sync插件 MySQL数据库 这个好装,在proot-distro里叫 mariadb 1. 安装 # 默认的源就带着,直接装就行 apt update && apt install mariadb-server -y 2. 使用 # 启动服务 service mariadb start # 然后就可以直接进入了 mysql 进入MySQL数据库环境之后的具体使用,小弟在这里就不班门弄斧了,只贴一下简单的常用命令 # 登录 MySQL # 登录指定name的账户,<CR>后输入密码 mysql -u name -p # 登录默认账户root mysql # 查看正在运行的数据库 SHOW DATABASES; # 查看特定数据库的信息 USE 数据库名; SHOW TABLES; # 确认当前使用的数据库 SELECT DATABASE(); # 查看正在使用的端口 SHOW VARIABLES LIKE 'port'; # 这个端口在`/etc/mysql/`下的 `my.cnf`中,默认是3306,可以改 # 改完之后重启运行就行 # 刷新MySQL权限相关的表 flush privileges; # 查看所有数据库用户要查看所有数据库用户,可以查询`mysql`数据库中的`user`表。 # 首先切换到`mysql`数据库: USE mysql; # 然后执行以下查询: SELECT User, Host FROM user; # 要查看用户`user`在数据库`nextcloud`上的权限 SHOW GRANTS FOR 'user'@'localhost' ON `nextcloud`.*; # 查询特定用户在特定主机上的权限 SHOW GRANTS FOR 'AIPING'@'localhost:7777'; # 更改指定用户的密码为 ALTER USER 'AIPING'@'localhost' IDENTIFIED BY 'new_password'; FLUSH PRIVILEGES; vaultwarden部署 1. 安装 下载地址 vaultwarden项目地址 web-vault项目地址 首先, vaultwarden 想要正常工作需要六个部分, vaultwarden web-vault nginx MySQL rustup vaultwarden 是bitwarden项目的社区版 Vaultwarden 是一个用于本地搭建 Bitwarden 服务器的第三方 Docker 项目。仅在部署的时候使用 Vaultwarden 镜像,桌面端、移动端、浏览器扩展等客户端均使用官方 Bitwarden 客户端。 web-vault 是web页面 nginx 提供代理服务 在局域网内还用 nginx 代理是因为不开https访问的话,网页只能在127.0.0.1上访问,在局域网内其他设备上使用的话会一直在首页转圈圈,当然,还是因为我只会用 nginx 搭建自签证书的https访问,所以就只介绍这个了; MySQL 用来提供数据库 当然官方也支持 Sqlite 等其他的类型,但是我之所以用 MySQL 只是因为我只会用这个,欸嘿,所以就介绍这一个了; rustup 是vaultwarden安装教程推荐的 rust table ,没怎么接触过,是按教程来的 1.1 安装依赖 apt install build-essential git pkg-config libssl-dev libmariadb-dev-compat libmariadb-dev -y # termux pkg install clang make pkg-config git openssl tur-repo -y 更新包列表并安装构建工具、Git、SSL 开发库 和 Certbot。 对于 MySQL,还需额外安装 MySQL 和开发库。 1.2 安装 Rust 使用官方 rustup 安装稳定版 Rust 和 Cargo。 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y source $HOME/.cargo/env rustc --version # 验证安装,应显示 rustc 1.x.x 2. 构建 Vaultwarden 2.1 克隆源代码 创建一个源代码目录并克隆仓库。 mkdir ~/source && cd ~/source git clone https://github.com/dani-garcia/vaultwarden.git cd vaultwarden 2.2 构建二进制文件 根据数据库选择构建特性。 SQLite(默认,简单) : cargo build --features sqlite --release MySQL(推荐生产环境) : cargo build --features mysql --release # 示例:MySQL 构建(替换为你的选择) cargo build --features mysql --release 提示 :如果内存不足,添加 --jobs 1 限制并行任务。构建后,二进制文件在 ~/source/vaultwarden/target/release/vaultwarden 。 3. 配置 Vaultwarden 3.1 创建数据目录 创建专用目录并设置权限。 sudo mkdir -p /var/lib/vaultwarden cd /var/lib/vaultwarden sudo mkdir -p data sudo useradd -m -d /var/lib/vaultwarden vaultwarden # 创建专用用户 sudo chown -R vaultwarden:vaultwarden /var/lib/vaultwarden 3.2 下载环境模板和 Web Vault 下载配置文件模板和 Web 界面(推荐最新版本,检查 GitHub Releases 获取当前版本,例如 v2026.4.1)。 ## 目录/var/lib/vaultwarden # 下载环境模板 wget https://raw.githubusercontent.com/dani-garcia/vaultwarden/main/.env.template mv .env.template .env # 下载 Web Vault(示例版本,替换为最新) wget https://github.com/dani-garcia/bw_web_builds/releases/download/v2026.4.1/bw_web_v2026.4.1.tar.gz tar -xvf bw_web_v2026.4.1.tar.gz --strip-components=1 -C data/ # 其实删不删都行,不删留着也没多大,还能方便看是安装的哪个版本 rm bw_web_v2026.4.1.tar.gz 3.3 配置数据库 3.3.1 SQLite(默认) 无需额外步骤,直接在 .env 中使用默认路径。 3.3.2 MySQL(新增) 登录 MySQL 并创建数据库和用户: mysql -u root -p # 输入 root 密码 在 MySQL 提示符下运行: CREATE DATABASE vaultwarden CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'vaultwarden'@'localhost' IDENTIFIED BY 'your_strong_password'; GRANT ALL PRIVILEGES ON vaultwarden.* TO 'vaultwarden'@'localhost'; FLUSH PRIVILEGES; EXIT; 替换 your_strong_password 为强密码。 在 .env 中配置 MySQL 连接(见下文 3.4)。 3.4 编辑 .env 文件 使用编辑器配置环境变量。关键设置包括域名、数据库、日志和通知。 vim /var/lib/vaultwarden/.env 示例配置(SQLite 版) : 不用复制这个示例,直接在官方的example示例里寻找需要改的部分,需要改的示例如下,其他配置我没有用到,不懂(哥们不是IT出身,主打一个能用就像) DATA_FOLDER=data DATABASE_URL=DATABASE_URL=sqlite://data/db.sqlite3 # SQLite 默认路径 LOG_FILE=data/vaultwarden.log LOG_LEVEL=info # 生产环境用 error DOMAIN=https://your-domain.com # 你的域名 ROCKET_ADDRESS=127.0.0.1 ROCKET_PORT=8000 示例配置(MySQL 版) :仅替换 DATABASE_URL 行: DATA_FOLDER=data DATABASE_URL=mysql://vaultwarden:your_strong_password@localhost/vaultwarden # MySQL 连接字符串 # 其余配置同上 提示 :完整模板见 Vaultwarden 文档 。测试配置:保存后,重启服务验证。 4. 部署和运行 4.1 复制二进制文件 cp ~/source/vaultwarden/target/release/vaultwarden /usr/local/bin/vaultwarden chmod +x /usr/local/bin/vaultwarden 4.2 运行 # 目录为/var/lib/vaultwarden vaultwarden # 长时间运行可以用nohup或者tmux,我喜欢用tmux # nohup nohup vaultwarden & # tmux就是直接开个新tmux窗口运行 vaultwarden NextCloud 1.安装一些用到的软件 官方教程里用到的软件(这里我把 sudo 去掉了,因为本来就是root用户而且termux好像取得root有点费劲儿) apt update && apt upgrade apt install apache2 mariadb-server libapache2-mod-php php-gd php-mysql \ php-curl php-mbstring php-intl php-gmp php-bcmath php-xml php-imagick php-zip 2. Mysql创建用户 这块在Next cloud的官网上有教程,可以按照自己需要去修改,不过localhost那个因为我自己不太会就没去改; # 启动MySQL客户端 root@localhost:~# mysql Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 52 Server version: 10.6.18-MariaDB-0ubuntu0.22.04.1 Ubuntu 22.04 Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. # 创建一个名为'username'的用户,该用户只能从'localhost'访问数据库,并设置密码为'password' MariaDB [(none)]> CREATE USER 'username'@'localhost' IDENTIFIED BY 'password'; Query OK, 0 rows affected (0.002 sec) # 创建一个名为'nextcloud2'的数据库,如果不存在,使用'utf8mb4'字符集和'utf8mb4_general_ci'排序规则 MariaDB [(none)]> CREATE DATABASE IF NOT EXISTS nextcloud2 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; Query OK, 1 row affected (0.002 sec) # 授予用户'username'在'nextcloud2'数据库上的所有权限 MariaDB [(none)]> GRANT ALL PRIVILEGES ON nextcloud2.* TO 'username'@'localhost'; Query OK, 0 rows affected (0.002 sec) # 刷新权限,使更改生效 MariaDB [(none)]> FLUSH PRIVILEGES; Query OK, 0 rows affected (0.002 sec) # 退出MySQL MariaDB [(none)]> \q Bye 3. 获取Next cloud 可以从这里找 Index of / (nextcloud.com) 需要的版本 如果是Linux服务端可以从这里找 Index of /server/releases (nextcloud.com) # 目前我用到的版本是30.0.0 wget https://download.nextcloud.com/server/releases/nextcloud-30.0.0.zip # 可以先解压再cp到指定位置 unzip nextcloud-30.0.0.zip # 会得到一个nextcloud文件夹,就是将这个文件夹cp到/var/wwww/目录下 cp -r nextcloud /var/wwww/ # 然后将 Nextcloud 目录的所有权更改为 HTTP 用户 # 但是其实哥们好像没有执行这个操作,目前倒是没遇到什么问题 sudo chown -R www-data:www-data /var/www/nextcloud 4. 配置Apache2 4.1 安装apache2 apt update && apt install apache2 -y 4.2 开启Apache2服务 service apache2 start 这个时候应该可以在内网环境的http服务上看到apache2的默认配置界面了 root@localhost:~# ifconfig Warning: cannot open /proc/net/dev (Permission denied). Limited output. lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 1000 (UNSPEC) rmnet_data2: flags=65<UP,RUNNING> mtu 1460 inet 这段个人ip哥们就暂时不展示了嗷 netmask 255.255.255.248 unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 1000 (UNSPEC) rndis0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.143.215 netmask 255.255.255.0 broadcast 192.168.143.255 unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 1000 (UNSPEC) # 这里这个192.168.143.215就是设备在内网的IP了,将他输入到浏览器应该就可以看到默认界面了 http://192.168.143.215 4.3 创建配置文件 到 /etc/apache2/sites-available/ 目录下,创建一个nextcloud.conf文件 cd /etc/apache2/sites-available/ vim nextcloud.conf 参考大佬的教程 ubuntu 22.04安装部署nextcloud最新版-笔记_netiii 将下列内容复制进去 <VirtualHost *:8080> # 这里我另一个机器用80端口会报错,所以就改成8080了 DocumentRoot /var/www/nextcloud/ ServerName 192.168.143.215 #修改自己的服务器IP或者域名 <Directory /var/www/nextcloud/> Require all granted AllowOverride All Options FollowSymLinks MultiViews <IfModule mod_dav.c> Dav off </IfModule> </Directory> </VirtualHost> 跟着教程走一遍,因为在termux中,和正常的Linux系统略有不同 a2ensite nextcloud.conf reload apache2 a2enmod rewrite headers env dir mime service apache2 restart # 如果有什么报错,我没管它,没看到有什么明显的影响 5. 网页配置 然后就可以访问( http://yourip :yourport)设定管理员账户和密码,配置MySQL数据库接口 这里IP就是 ifconfig 返回的那个内网地址; port就是在nextcloud.conf中配置的接口; 我的配置是 http://192.168.143.215:8080 如果完全和我的教程来,一字不改的话 MySQL用户名是 username 数据库名是 nextcloud2 数据库密钥是 password 数据库地址就是默认的 localhost ComfyUI运行Z-Image-Turbo 当然,这是在proot-distro容器环境中 1. 从GitHub拉取项目 git clone https://github.com/comfyanonymous/ComfyUI.git 2. 准备python环境 uv venv --python 3.11.9 source .venv/bin/activate ps:这里之所以用python3.11.9是因为我之前用3.12会在依赖安装那部分大量报错;这个版本是当前(2025.11)可用的最新版本了 3. 安装依赖 UV_LINK_MODE=copy uv pip install -r requirements.txt --no-cache-dir --force-reinstall 4. 添加一些插件,比如 comfy-gguf 来支持gguf格式的模型(不然可能炸) # 到custom目录下,用git clone拉取 git clone https://github.com/city96/ComfyUI-GGUF.git # cd到ComfyUI-GGUF目录下安装依赖 UV_LINK_MODE=copy uv pip install -r requirements.txt --no-cache-dir --force-reinstall uv简直是termux运行comfyui的神奇喵喵工具 UV_LINK_MODE=copy uv pip install -r requirements.txt --no-cache-dir --force-reinstall ,之前没接触过的时候用python3自带的venv,依赖完全用不了大片的error 5. 回到ComfyUI目录下,写个运行小脚本就可以部署了 touch run.sh \ chmod +x run.sh \ echo "python main.py --cpu --use-split-cross-attention --listen 0.0.0.0 --port 8888" >> run.sh \ ./run.sh ps:上边这个8888纯粹是顺手,而且没root的话,数字小于4000的端口基本用不来 IPv6 这里提一嘴IPv6,我用的是电信的流量卡,查了一下有IPv6 查询方式很多,这里写一下一种termux命令行里的方法 curl -6 ifconfig.me IPv6访问时是http://[xxxx]:port/;一定要加[ ] 实测可以远程访问网页,(导航页是让AI写的),ssh链接使用Termius也可以直连; 但是就像我白天发帖问的,Obsidian的self-hosted live sync插件不能正常使用IPv6,会报错说找不到; 在这里请教各位佬,有没有办法解决这个问题。 wireguard或者Tailsce之类的组网当然可以,不过直接用IPv6不是可以少开一个APP嘛 END 8 个帖子 - 4 位参与者 阅读完整话题

v2ex · 2026-05-20 14:29:53+08:00 · tech

基于 PHP + Redis + MySQL 开发的微信小程序订阅消息推送系统。 支持: 微信小程序一次性订阅消息 微信小程序长期订阅消息 Redis 高性能消息队列 Worker 多进程并发 QPS 限速 TXT 导入 openid 模板管理 模板测试发送 实时任务监控 失败日志 企业级后台 UI ✨ 功能特性 ✅ 微信小程序订阅消息推送 ✅ 一次性订阅 / 长期订阅 ✅ Redis 高性能队列 ✅ PHP Worker 多进程并发 ✅ 自定义 QPS ✅ 推送任务暂停 / 继续 / 停止 ✅ 推送实时进度 ✅ 企业级后台 UI ✅ TXT 批量导入 openid ✅ 模板可视化编辑 ✅ 模板测试发送 ✅ 失败日志记录 ✅ access_token Redis 缓存 ✅ 实时数据可视化 ✅ 登录权限验证 🖼️ 系统截图 控制台 模板管理 做这个东西也是满足自己的日常使用,有需要可以直接部署使用,适合做运营的,推广的,活跃用户! https://github.com/likeyun/WxMiniProSubMsgPush

LinuxDo 最新话题 · 2026-05-19 00:46:57+08:00 · tech

前言:后续补充 大晚上手挫给我搓爽了 这套方案用于Linux 环境: mysqld_exporter 采集 MySQL 指标。 Prometheus 抓取 mysqld_exporter:9104 。 使用 mysql_global_status_slow_queries 监控全局慢查询增长。 使用 mysql_perf_schema_events_statements_* 从 performance_schema.events_statements_summary_by_digest 发现具体慢SQL摘要。 1. MySQL 开启慢查询与 performance_schema 建议在 /etc/my.cnf 或 /etc/mysql/mysql.conf.d/mysqld.cnf 中配置: [mysqld] performance_schema=ON slow_query_log=ON slow_query_log_file=/var/log/mysql/mysql-slow.log long_query_time=1 log_queries_not_using_indexes=OFF 登录mysql执行授权 SQL: mysql -uroot -p > CREATE USER IF NOT EXISTS 'exporter'@'127.0.0.1' IDENTIFIED BY 'change_me' WITH MAX_USER_CONNECTIONS 3; >GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'127.0.0.1'; >FLUSH PRIVILEGES; 运行命令检查: SHOW VARIABLES LIKE 'performance_schema'; SHOW VARIABLES LIKE 'slow_query_log'; SHOW VARIABLES LIKE 'long_query_time'; SHOW GLOBAL STATUS LIKE 'Slow_queries'; 2. 部署 mysqld_exporter 下载并安装二进制: sudo useradd --no-create-home --shell /usr/sbin/nologin prometheus curl -LO https://github.com/prometheus/mysqld_exporter/releases/download/v0.19.0/mysqld_exporter-0.19.0.linux-amd64.tar.gz tar -xzf mysqld_exporter-0.19.0.linux-amd64.tar.gz sudo install -m 0755 mysqld_exporter-0.19.0.linux-amd64/mysqld_exporter /usr/local/bin/mysqld_exporter sudo mkdir -p /etc/mysqld_exporter sudo install -m 0640 mysqld_exporter/.my.cnf.example /etc/mysqld_exporter/.my.cnf sudo chown -R prometheus:prometheus /etc/mysqld_exporter sudo install -m 0644 mysqld_exporter/mysqld_exporter.service /etc/systemd/system/mysqld_exporter.service sudo systemctl daemon-reload sudo systemctl enable --now mysqld_exporter mysqld_exporter.service配置: [Unit] Description=Prometheus MySQL Exporter After=network-online.target Wants=network-online.target [Service] User=prometheus Group=prometheus Type=simple ExecStart=/usr/local/bin/mysqld_exporter \ --config.my-cnf=/etc/mysqld_exporter/.my.cnf \ --web.listen-address=:9104 \ --collect.global_status \ --collect.global_variables \ --collect.perf_schema.eventsstatements \ --collect.perf_schema.eventsstatements.limit=250 \ --collect.perf_schema.eventsstatements.timelimit=86400 \ --collect.perf_schema.eventsstatements.digest_text_limit=160 Restart=always RestartSec=5s NoNewPrivileges=true ProtectSystem=strict ProtectHome=true PrivateTmp=true [Install] WantedBy=multi-user.target 修改 /etc/mysqld_exporter/.my.cnf 中密码后再启动服务。 .my.cnf内容: [client] user=exporter password=change_me host=127.0.0.1 port=3306 验证: curl -s http://127.0.0.1:9104/metrics | grep -E 'mysql_global_status_slow_queries|mysql_perf_schema_events_statements' 3. 配置 Prometheus 把 prometheus.yml 和 rules/mysql-slow-query.rules.yml 合并到 Prometheus 配置目录。 prometheus.yml内容配置: global: scrape_interval: 15s evaluation_interval: 15s rule_files: - /etc/prometheus/rules/mysql-slow-query.rules.yml scrape_configs: - job_name: mysql static_configs: - targets: - 127.0.0.1:9104 labels: service: mysql env: prod mysql-slow-query.rules.yml内容配置: groups: - name: mysql-slow-query interval: 30s rules: - record: mysql:slow_queries:rate5m expr: rate(mysql_global_status_slow_queries[5m]) - record: mysql:statement_digest:avg_latency_seconds5m expr: | rate(mysql_perf_schema_events_statements_seconds_total[5m]) / clamp_min(rate(mysql_perf_schema_events_statements_total[5m]), 0.001) - record: mysql:statement_digest:rows_examined_rate5m expr: rate(mysql_perf_schema_events_statements_rows_examined_total[5m]) - alert: MySQLSlowQueriesIncreasing expr: mysql:slow_queries:rate5m > 0.1 for: 10m labels: severity: warning annotations: summary: "MySQL slow queries are increasing on {{ $labels.instance }}" description: "Slow query rate is {{ $value | printf \"%.3f\" }}/s for 10m. Check MySQL slow log and statement digest metrics." - alert: MySQLSlowSQLDigestHighAvgLatency expr: mysql:statement_digest:avg_latency_seconds5m > 1 for: 10m labels: severity: warning annotations: summary: "Slow SQL digest on {{ $labels.instance }}" description: "schema={{ $labels.schema }}, avg_latency={{ $value | printf \"%.3f\" }}s, digest={{ $labels.digest }}, sql={{ $labels.digest_text }}" - alert: MySQLSQLDigestExaminesTooManyRows expr: mysql:statement_digest:rows_examined_rate5m > 100000 for: 10m labels: severity: warning annotations: summary: "MySQL SQL digest examines too many rows on {{ $labels.instance }}" description: "schema={{ $labels.schema }}, rows_examined_rate={{ $value | printf \"%.0f\" }}/s, digest={{ $labels.digest }}, sql={{ $labels.digest_text }}" - alert: MySQLSQLDigestNoIndexUsed expr: increase(mysql_perf_schema_events_statements_no_index_used_total[15m]) > 0 for: 5m labels: severity: info annotations: summary: "MySQL SQL digest used no index on {{ $labels.instance }}" description: "schema={{ $labels.schema }}, no_index_count={{ $value | printf \"%.0f\" }} in 15m, digest={{ $labels.digest }}, sql={{ $labels.digest_text }}" 检查配置并重载: promtool check config /etc/prometheus/prometheus.yml sudo systemctl reload prometheus 4. 导入 Grafana dashboard 可以直观看到慢 SQL。导入 grafana/mysql-slow-sql-dashboard.json 后,选择 Prometheus 数据源即可看到: 慢查询增长速率趋势。 Top 慢 SQL digest 表格,按 5 分钟平均耗时排序。 Top 扫描行数 SQL digest。 未使用索引 SQL digest。 导入路径: Grafana -> Dashboards -> New -> Import -> Upload JSON file mysql-slow-sql-dashboard.json配置: { "__inputs": [ { "name": "DS_PROMETHEUS", "label": "Prometheus", "description": "Prometheus datasource used by this dashboard", "type": "datasource", "pluginId": "prometheus", "pluginName": "Prometheus" } ], "__requires": [ { "type": "grafana", "id": "grafana", "name": "Grafana", "version": "10.0.0" }, { "type": "datasource", "id": "prometheus", "name": "Prometheus", "version": "1.0.0" }, { "type": "panel", "id": "timeseries", "name": "Time series", "version": "" }, { "type": "panel", "id": "table", "name": "Table", "version": "" } ], "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "target": { "limit": 100, "matchAny": false, "tags": [], "type": "dashboard" }, "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": null, "links": [], "liveNow": false, "panels": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "lineInterpolation": "linear", "lineWidth": 2, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 0.1 } ] }, "unit": "qps" }, "overrides": [] }, "gridPos": { "h": 8, "w": 24, "x": 0, "y": 0 }, "id": 1, "options": { "legend": { "calcs": [ "lastNotNull" ], "displayMode": "table", "placement": "right", "showLegend": true }, "tooltip": { "mode": "single", "sort": "none" } }, "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "rate(mysql_global_status_slow_queries{instance=~\"$instance\"}[5m])", "legendFormat": "{{instance}}", "range": true, "refId": "A" } ], "title": "Slow Query Rate", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "custom": { "align": "auto", "cellOptions": { "type": "auto" }, "filterable": true, "inspect": false }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 1 } ] }, "unit": "s" }, "overrides": [ { "matcher": { "id": "byName", "options": "SQL" }, "properties": [ { "id": "custom.width", "value": 720 } ] }, { "matcher": { "id": "byName", "options": "Schema" }, "properties": [ { "id": "custom.width", "value": 120 } ] }, { "matcher": { "id": "byName", "options": "Avg Latency" }, "properties": [ { "id": "unit", "value": "s" }, { "id": "decimals", "value": 3 }, { "id": "custom.cellOptions", "value": { "mode": "gradient", "type": "color-background" } } ] } ] }, "gridPos": { "h": 10, "w": 24, "x": 0, "y": 8 }, "id": 2, "options": { "cellHeight": "sm", "footer": { "countRows": false, "fields": "", "reducer": [ "sum" ], "show": false }, "showHeader": true, "sortBy": [ { "desc": true, "displayName": "Avg Latency" } ] }, "pluginVersion": "10.0.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "exemplar": false, "expr": "topk($topk, rate(mysql_perf_schema_events_statements_seconds_total{instance=~\"$instance\",schema=~\"$schema\"}[5m]) / clamp_min(rate(mysql_perf_schema_events_statements_total{instance=~\"$instance\",schema=~\"$schema\"}[5m]), 0.001))", "format": "table", "instant": true, "legendFormat": "__auto", "range": false, "refId": "A" } ], "title": "Top Slow SQL Digests by Avg Latency", "transformations": [ { "id": "organize", "options": { "excludeByName": { "Time": true, "digest": true, "instance": true, "job": true, "service": true }, "indexByName": { "Value": 3, "digest_text": 2, "schema": 1 }, "renameByName": { "Value": "Avg Latency", "digest_text": "SQL", "schema": "Schema" } } } ], "type": "table" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "custom": { "align": "auto", "cellOptions": { "type": "auto" }, "filterable": true, "inspect": false }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 100000 } ] }, "unit": "rows" }, "overrides": [ { "matcher": { "id": "byName", "options": "SQL" }, "properties": [ { "id": "custom.width", "value": 720 } ] }, { "matcher": { "id": "byName", "options": "Rows Examined/s" }, "properties": [ { "id": "decimals", "value": 0 } ] } ] }, "gridPos": { "h": 10, "w": 12, "x": 0, "y": 18 }, "id": 3, "options": { "cellHeight": "sm", "footer": { "countRows": false, "fields": "", "reducer": [ "sum" ], "show": false }, "showHeader": true, "sortBy": [ { "desc": true, "displayName": "Rows Examined/s" } ] }, "pluginVersion": "10.0.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "topk($topk, rate(mysql_perf_schema_events_statements_rows_examined_total{instance=~\"$instance\",schema=~\"$schema\"}[5m]))", "format": "table", "instant": true, "range": false, "refId": "A" } ], "title": "Top SQL Digests by Rows Examined", "transformations": [ { "id": "organize", "options": { "excludeByName": { "Time": true, "digest": true, "instance": true, "job": true, "service": true }, "indexByName": { "Value": 3, "digest_text": 2, "schema": 1 }, "renameByName": { "Value": "Rows Examined/s", "digest_text": "SQL", "schema": "Schema" } } } ], "type": "table" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "custom": { "align": "auto", "cellOptions": { "type": "auto" }, "filterable": true, "inspect": false }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 1 } ] }, "unit": "short" }, "overrides": [ { "matcher": { "id": "byName", "options": "SQL" }, "properties": [ { "id": "custom.width", "value": 720 } ] }, { "matcher": { "id": "byName", "options": "No Index Used" }, "properties": [ { "id": "decimals", "value": 0 } ] } ] }, "gridPos": { "h": 10, "w": 12, "x": 12, "y": 18 }, "id": 4, "options": { "cellHeight": "sm", "footer": { "countRows": false, "fields": "", "reducer": [ "sum" ], "show": false }, "showHeader": true, "sortBy": [ { "desc": true, "displayName": "No Index Used" } ] }, "pluginVersion": "10.0.0", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "topk($topk, increase(mysql_perf_schema_events_statements_no_index_used_total{instance=~\"$instance\",schema=~\"$schema\"}[15m]))", "format": "table", "instant": true, "range": false, "refId": "A" } ], "title": "SQL Digests Without Index", "transformations": [ { "id": "organize", "options": { "excludeByName": { "Time": true, "digest": true, "instance": true, "job": true, "service": true }, "indexByName": { "Value": 3, "digest_text": 2, "schema": 1 }, "renameByName": { "Value": "No Index Used", "digest_text": "SQL", "schema": "Schema" } } } ], "type": "table" } ], "refresh": "30s", "schemaVersion": 38, "style": "dark", "tags": [ "mysql", "slow-sql", "prometheus" ], "templating": { "list": [ { "current": {}, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "definition": "label_values(mysql_up, instance)", "hide": 0, "includeAll": true, "label": "Instance", "multi": true, "name": "instance", "options": [], "query": { "query": "label_values(mysql_up, instance)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "skipUrlSync": false, "sort": 1, "type": "query" }, { "current": {}, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "definition": "label_values(mysql_perf_schema_events_statements_total{instance=~\"$instance\"}, schema)", "hide": 0, "includeAll": true, "label": "Schema", "multi": true, "name": "schema", "options": [], "query": { "query": "label_values(mysql_perf_schema_events_statements_total{instance=~\"$instance\"}, schema)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "skipUrlSync": false, "sort": 1, "type": "query" }, { "current": { "selected": false, "text": "10", "value": "10" }, "hide": 0, "label": "TopK", "name": "topk", "options": [ { "selected": true, "text": "10", "value": "10" }, { "selected": false, "text": "20", "value": "20" }, { "selected": false, "text": "50", "value": "50" } ], "query": "10,20,50", "queryValue": "", "skipUrlSync": false, "type": "custom" } ] }, "time": { "from": "now-1h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "MySQL Slow SQL Monitoring", "uid": "mysql-slow-sql", "version": 1, "weekStart": "" } 如果 Grafana 显示 No data 或面板左上角有告警图标,按下面顺序排查: mysql_up 如果没有结果,说明 Prometheus 没抓到 mysqld_exporter ,检查 prometheus.yml 里的 targets 和 Prometheus Status -> Targets 页面。 mysql_global_status_slow_queries 如果 mysql_up 有结果但这个没有,检查 mysqld_exporter 是否能连接 MySQL,以及 exporter 用户权限。 mysql_perf_schema_events_statements_total 如果前两个有结果但这个没有,说明 SQL digest 采集没打开或 MySQL performance_schema 没有可用数据,检查 systemd 服务里是否包含: --collect.perf_schema.eventsstatements 同时在 MySQL 执行: SHOW VARIABLES LIKE 'performance_schema'; SELECT COUNT(*) FROM performance_schema.events_statements_summary_by_digest; 如果 events_statements_summary_by_digest 为空,先执行几条业务 SQL,等待 Prometheus 下一个抓取周期后刷新 Grafana。 5. 常用 PromQL 全局慢查询增长速度: rate(mysql_global_status_slow_queries[5m]) 过去 5 分钟平均耗时最高的 SQL digest: topk( 10, rate(mysql_perf_schema_events_statements_seconds_total[5m]) / clamp_min(rate(mysql_perf_schema_events_statements_total[5m]), 0.001) ) 过去 5 分钟扫描行数明显高的 SQL digest: topk( 10, rate(mysql_perf_schema_events_statements_rows_examined_total[5m]) ) 未使用索引的 SQL digest: topk( 10, increase(mysql_perf_schema_events_statements_no_index_used_total[15m]) ) 6. 告警说明 规则文件已实现这些告警: MySQLSlowQueriesIncreasing :全局慢查询持续增加。 MySQLSlowSQLDigestHighAvgLatency :某条 SQL digest 平均耗时超过 1 秒。 MySQLSQLDigestExaminesTooManyRows :某条 SQL digest 扫描行数过高。 MySQLSQLDigestNoIndexUsed :某条 SQL digest 发生未使用索引。 赶紧睡了,明天上班摸鱼再继续施工 1 个帖子 - 1 位参与者 阅读完整话题