[{"content":"前置条件 拼多多购买一张小白卡(20元左右)，到手就可以直接插到手机sim卡槽里面 安卓手机、已 Root 并安装 LSPosed (非Root也行，可以搜一下其他教程) giffgaff 官方 App，截至目前我使用的20.16.0版本 HookEuicc，用于让 giffgaff App 进入 eSIM 安装流程，并复制 eSIM 激活串 EasyEUICC，用于把 eSIM 写入小白卡 一张可以完成英镑付款的银行卡，我使用的是 Visa HookEuicc-v2.0.0.apk\nEasyEUICC-v1.6.2.apk\n配置 HookEuicc 进入 LSPosed，启用 HookEuicc 模块。在模块作用域里勾选 giffgaff，我同时勾选了 phone 和 se，但不确定是否必须。\n进入 HookEuicc 模块后勾选第一个选项，然后重启手机。\n购买 giffgaff 卡号 打开 giffgaff App，注册账号并登录。\n选择 eSIM，点击 Choose your plan 拉到最下面，选择 I don't want a plan 选择最低的 10 英镑余额充值 点击 Add a payment method，添加银行卡，我使用的是 Visa 按付款卡信息填写姓名和账单地址 付款完成后选择 Install eSIM，稍等一会就会收到消息提示已复制激活码\n检查剪贴板，如果能看到形如下面这样的字符串，就说明已经拿到了 eSIM 激活串：1$cel.prod.ondemandconnectivity.com$2A3B4C5D6E7F8G9H\n在前面补全 LPA: 后重新复制：\nLPA:1$cel.prod.ondemandconnectivity.com$2A3B4C5D6E7F8G9H\n写入小白卡 打开 EasyEUICC，授予它需要的权限 点击右下角 + 号 点击下一步 选择从剪贴板读取 稍等一会就能看到写入成功，操作步骤6前请注意，小米手机在启用手机卡后，会发送一个定位短信，会被扣除 0.3 英镑的短信费，如果在意的话自行想办法搜索解决这个问题 点击 giffgaff 卡片右侧三个点，选择启用 等待网络注册完成，成功后会收到开通提醒的短信，里面会带上你的英国手机号 注意事项 流量消耗的费用极高，请注意不要使用流量 每6个月需要消费一次余额进行保号，否则手机号会被收回 ","date":"2026-06-08T00:00:00Z","permalink":"https://izumi.pub/Android/android-giffgaff-esim/","title":"国产安卓手机开通 giffgaff eSIM 写入小白卡"},{"content":"问题背景 硬件环境：\n本地机器： Windows 11 远程开发机： J1900 (蜗牛星际)，Debian 13 一直使用的 Claude Code 和 Codex，今天想感受一下 Gemini Pro 模型的前端能力，结果安装完 Antigravity IDE 后连接物理机进行 Remote-SSH 开发时，直接报错无法打开AI侧栏。\n核心的报错信息：\nAntigravity server crashed unexpectedly. Please restart to fully restore AI features. 日志显示：FATAL ERROR: This binary was compiled with aes enabled, but this feature is not available on this processor (go/sigill-fail-fast). 解决方案参考：https://github.com/devanshug2307/antigravity-discussions/discussions/18\n修改完成后，IDE的启动会异常耗时(1~2分钟)，这个是正常现象\n准备工作 前往 Intel® Software Development Emulator\n下载 sde-external-10.7.0-2026-02-18-lin.tar.xz\n1 2 3 4 5 # 在服务器上创建目录并解压 mkdir -p ~/intel-sde tar -xf sde-external-10.7.0-2026-02-18-lin.tar.xz -C ~/intel-sde --strip-components=1 # 确保 64 位模拟器拥有执行权限 chmod +x ~/intel-sde/sde64 修改 Linux 内核参数允许进程注入 Intel SDE 需要通过 ptrace 机制来拦截二进制文件。Debian 等系统的默认安全策略会拦截这一行为，需要将其开放：\n1 2 echo \u0026#39;kernel.yama.ptrace_scope = 0\u0026#39; | sudo tee /etc/sysctl.d/99-ptrace.conf sudo sysctl -p /etc/sysctl.d/99-ptrace.conf 定位远程服务端目录 Antigravity Server 会在你的用户目录下生成一个带有随机哈希值的文件夹。使用 find 命令找到它：\n1 2 3 find ~/.antigravity-server -type f -name \u0026#34;language_server_linux_x64\u0026#34; 2\u0026gt;/dev/null # 大概率就是类似的文件夹: ~/.antigravity-server/bin/1.xx.xx-xxx/extensions/antigravity/bin 劫持并替换语言服务器进程 1 2 3 4 5 6 7 8 9 10 11 # 1. 进入你刚才找到的 language_server_linux_x64 所在的目录： cd /home/izumi/.antigravity-server/bin/1.xx.xx-xxx/extensions/antigravity/bin/ # 2. 修复目录权限，确保 SDE 能生成日志 sudo chown -R $USER:$USER . # 3. 隐藏原版二进制文件 mv language_server_linux_x64 language_server_linux_x64.real # 4. 创建拦截 Wrapper 脚本 vim language_server_linux_x64 将以下脚本粘到 language_server_linux_x64 文件里\n1 2 3 4 5 6 7 8 9 10 11 12 #!/bin/bash SDE_BIN=\u0026#34;$HOME/intel-sde/sde64\u0026#34; DIR=\u0026#34;$(cd \u0026#34;$(dirname \u0026#34;${BASH_SOURCE[0]}\u0026#34;)\u0026#34; \u0026amp;\u0026amp; pwd)\u0026#34; # 核心优化：限制为单核运行，防止 SDE 模拟耗尽 J1900 的所有算力导致 Debian 直接死机 export GOMAXPROCS=1 export GODEBUG=http2client=0,tls13=0 # 核心参数 -skx：由 Kai-972 补充。 # 不要用 -aes，因为 Go 语言底层会先通过 CPUID 检查芯片架构。 # -skx 会模拟一个完整的 Skylake-X 架构（包含伪造的 CPUID 和 AES-NI 指令集）骗过底层校验。 exec \u0026#34;$SDE_BIN\u0026#34; -skx -- \u0026#34;$DIR/language_server_linux_x64.real\u0026#34; \u0026#34;$@\u0026#34; \u0026gt;\u0026gt; \u0026#34;$HOME/wrapper-debug.log\u0026#34; 2\u0026gt;\u0026amp;1 赋予脚本权限:\n1 chmod +x language_server_linux_x64 延长 Antigravity IDE 的硬编码超时限制 这一步可以看情况做，如果你启动发现超时报错，就需要调整它的超时时间\n1 2 3 4 5 2026-03-21 15:47:51.619 [info] (Antigravity) 2026-03-21 15:47:51.615 [ERROR]: Failed to start language server: Error: Timed out waiting for language server start 2026-03-21 15:47:51.623 [info] (Antigravity) 2026-03-21 15:47:51.623 [ERROR]: LS startLanguageServer error: Timed out waiting for language server start 2026-03-21 15:48:12.943 [info] (Antigravity) 2026-03-21 15:48:12.942 [INFO]: Language server exited with code 0 首先找到控制超时时间的代码位置\n1 grep -rl \u0026#34;Timed out waiting for language server start\u0026#34; ~/.antigravity-server/bin/ --include=\u0026#34;*.js\u0026#34; 能看到输出: /home/izumi/.antigravity-server/bin/1.xx.xx-xx/extensions/antigravity/dist/extension.js\n打开文件后搜索找到类似这样的代码：\n1 setTimeout(()=\u0026gt;{... t(new Error(\u0026#34;Timed out waiting for language server start\u0026#34;)))}, 6e4) 将 6e4 改大，我这里是直接改成 30e4\n重启 IDE 耐心等待即可发现成功连接\n最后注意 IDE 更新后，整个 Server 目录的路径可能会被，需要重走上述步骤，或者你可以让 AI 写一个一键脚本，因为我只是临时用用，就不做后续处理了。\n","date":"2026-03-21T00:00:00Z","permalink":"https://izumi.pub/NAS/run-antigravity-without-aes/","title":"Antigravity 远程开发 Server Crashed: 非 AES 架构 CPU 适配教程"},{"content":"前置条件 1.解锁会清空手机信息，如果是已经使用的手机，注意备份手机数据\n2.需要设备的安全更新时间在 2026-02-01 以前\n查看路径：设置 -\u0026gt; 我的设备 -\u0026gt; 全部参数与信息\n我的手机包装盒日期 2025.12，大概率就是可以的\n解锁 打开手机：\n设置 -\u0026gt; 我的设备 -\u0026gt; 全部参数与信息 -\u0026gt; 连点 OS版本 5次进入开发者模式 回到设置 搜索 开发者选项 进入 打开 OEM解锁 打开 USB调试 手机连接电脑，弹出 允许USB调试 选择确定 打开电脑(Windows)：\n下载：小米骁龙8E5一键解锁BL.zip 后解压\n直接双击运行 小米骁龙8E5一键解锁BL.bat 按 回车键 运行下一步，等待脚本运行\n此时手机会重启，清空数据，耐心等待，开机时能看见开机界面上面有一把小锁解开，就是解锁成功了，进入系统后也能在 设置 -\u0026gt; 更多设置 -\u0026gt; 开发者选项 -\u0026gt; 设备解锁状态 中看到已解锁的页面提示\nROOT 未完待续\n","date":"2026-03-18T00:00:00Z","permalink":"https://izumi.pub/Android/xiaomi-17-pro-unlock-root/","title":"小米 17 Pro 秒解锁 + Root + 常用软件"},{"content":"硬件环境：\nCPU / 主板： J1900 (蜗牛星际) 内存： 8GB DDR3L 1600 系统盘： 256GB mSATA SSD 数据盘： 3 x 4TB HDD（其中两块组 RAID 1，一块做普通独立存储） 操作系统： Debian 13 (Netinst, 纯命令行无桌面环境) 额外准备：\n一个 U 盘： 用于制作启动盘。 键盘、鼠标、HDMI线、显示器： 用于连接操作机器。 数据备份： 务必从黑群晖备份你的所有数据到别处，包括机械硬盘（组建 RAID 会格式化硬盘）。 Debian 13 镜像： debian-13.3.0-amd64-netinst.iso(点击下载) 或 www.debian.org Rufus 写入工具： rufus-4.13p.exe(点击下载) or rufus.ie 1. 启动盘制作 准备镜像： debian-13.3.0-amd64-netinst.iso。 Rufus 写入： 插入 U 盘，打开 Rufus。 设备： 选中 U 盘（核对盘符）。 引导类型选择： 选中下载好的 Debian 13 ISO 文件。 分区类型： 默认选 GPT（若后续无法引导，可尝试重写并改为 MBR）。 点击“开始”。弹窗询问时，推荐默认的 ISO 镜像模式；若老款主板兼容性差，可尝试 DD 镜像模式。 2. BIOS 设置与引导 物理接口： 建议将 U 盘插在主板背面的黑色 USB 2.0 接口上。前面板或蓝色的 USB 3.0 接口在 BIOS 阶段可能无法识别。 进入 BIOS： 开机狂按 F11 或 Del 键。 调整启动顺序： 在 Boot 选项卡中找到 Boot Option #1，选中你的U盘并回车，设置U盘为第一启动项 (系统盘可以放在第二位) 兼容性设置 (备选)： 如果识别不到 U 盘： 尝试将 Boot 菜单中 Fast Boot（快速启动）设置为 Disabled（关闭）。 保存并重启\n3. Debian 系统安装核心选项 通过 U 盘引导后，选择 Install 或 Graphical install。\n基础设置： 依次设置语言、时区、Root 密码及普通用户。 磁盘分区 (关键步骤)： 选择 向导 - 使用整个磁盘 (Guided - use entire disk)。 务必选中你的固态硬盘 (SSD) 作为系统盘。 选择 将所有文件放在同一分区中 (All files in one partition)。 确认并写入更改 (Write changes to disks)。 软件选择 (Software selection)： 取消勾选 Debian桌面环境(Debian desktop environment) 及所有桌面选项（如 GNOME 等）。 仅保留勾选 SSH server 和 标准系统工具 (standard system utilities)。 安装完成后，拔出 U 盘并重启。\n4. 存储盘配置 (RAID 1 与单盘) 在同局域网的电脑上，通过 SSH 连接到 Debian，并切换到 root 用户 (su -)。\n4.1 识别磁盘 1 2 # 查看磁盘列表 lsblk 根据容量确认盘符。本文假设：sda 为系统盘，sdb 和 sdc 为组 RAID 的磁盘，sdd 为单块数据盘。\n4.2 创建 RAID 1 阵列 注意：此操作会格式化硬盘，请确保数据已备份！\n安装阵列管理工具并创建 md0 阵列：\n1 2 3 apt update \u0026amp;\u0026amp; apt install mdadm -y # 使用 sdb 和 sdc 创建名为 md0 的 RAID 1 阵列 mdadm --create --verbose /dev/md0 --level=1 --raid-devices=2 /dev/sdb /dev/sdc (若提示磁盘有残留数据，输入 y 确认。)\n重要提示： 4TB 硬盘同步需要数小时。你可以继续后续操作，同步会在后台进行。查看进度：\n1 cat /proc/mdstat 保存配置，防止重启失效：\n1 2 mdadm --detail --scan \u0026gt;\u0026gt; /etc/mdadm/mdadm.conf update-initramfs -u 4.3 格式化磁盘与空间优化 将 RAID 1 阵列 (/dev/md0) 和单块硬盘 (/dev/sdd) 格式化为 ext4。\n优化提示： ext4 默认会为 root 用户预留 5% 的磁盘空间（在 4TB 硬盘上约 200GB），对于纯数据存储盘来说这通常没有必要。我们可以将其设为 0% 以释放这部分空间：\n1 2 3 4 5 6 mkfs.ext4 /dev/md0 mkfs.ext4 /dev/sdd # 将预留空间比例设为 0% tune2fs -m 0 /dev/md0 tune2fs -m 0 /dev/sdd 4.4 挂载磁盘与开机自启 创建挂载点目录：\n1 mkdir -p /mnt/raid1_4t /mnt/single_4t 查看并记录磁盘的 UUID：\n1 2 blkid /dev/md0 blkid /dev/sdd 编辑 /etc/fstab 文件（使用 nano）：\n1 nano /etc/fstab 在文件末尾添加以下配置（请替换为你实际的 UUID）：\n1 2 UUID=你的md0的UUID /mnt/raid1_4t ext4 defaults 0 0 UUID=你的sdd的UUID /mnt/single_4t ext4 defaults 0 0 测试挂载：\n1 2 mount -a df -h 4.5 自动休眠 如果你的硬盘读写次数较低，可以配置自动休眠\n1 2 3 4 5 # 查看硬盘ID、找到对应你的机械硬盘的 ID（通常以 ata- 开头）。 ls -l /dev/disk/by-id/ | grep -v part # 编辑配置文件 vim /etc/hdparm.conf 在文件末尾为你的硬盘添加如下配置（替换为你自己的硬盘 ID）：\n1 2 3 4 5 6 7 8 9 10 11 /dev/disk/by-id/ata-WDC_WD40EFZX-xxxxx_WD-WCCxxxxx { spindown_time = 241 } /dev/disk/by-id/ata-Seagate_ST4000VN008-xxxxx_Zxxxxx { spindown_time = 241 } /dev/disk/by-id/ata-Seagate_ST4000VN008-xxxxx_Zyyyyy { spindown_time = 241 } 至此，底层基础架构搭建完毕。数据盘已全部就绪，可以直接在此基础上部署 Docker 或各类服务。\n5. CasaOS CasaOS是一款基于Docker、简单易用且优雅的开源家庭云系统，旨在为家庭用户提供一站式、轻量级的NAS存储及应用管理解决方案。\n但我个人使用下来感觉没什么必要，功能比较简单，管理 docker 我更喜欢命令行。\n1 curl -fsSL https://get.casaos.io | sudo bash 6.qBittorrent 我们通过 docker 安装 qBittorrent\n1 2 3 4 5 6 7 8 # 先创建配置文件夹 mkdir -p /opt/qbittorrent/config # 进入qbittorrent目录 cd qbittorrent # 创建 compose.yml vim compose.yml compose.yml 配置文件, 可自行更改\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 services: qbittorrent: image: lscr.io/linuxserver/qbittorrent:latest container_name: qbittorrent environment: - PUID=0 # 请确保这个 UID 对挂载盘有读写权限 - PGID=0 # 请确保这个 GID 对挂载盘有读写权限 - TZ=Asia/Shanghai - WEBUI_PORT=8085 volumes: - /opt/qbittorrent/config:/config # qBittorrent 的配置和种子文件 # 下面是新增的两个挂载盘映射 - /mnt/raid1_4t/PT:/downloads/raid1_4t # 将 raid1_4t 盘的 PT 文件夹映射进去 - /mnt/single_4t/PT:/downloads/single_4t # 将 single_4t 盘的 PT 文件夹映射进去 ports: - 8085:8085 - 52000:52000 - 52000:52000/udp restart: unless-stopped 准备启动 qBittorrent\n1 2 3 4 5 6 7 8 docker compose up -d # 查看启动日志，并获取随机生成的登录密码 docker logs qbittorrent # 可以看到输出： # The WebUI administrator username is: admin # The WebUI administrator password was not set. A temporary password is provided for this session: X9JAJQQr7 在浏览器访问 http://nas_ip:8085 输入账号: admin 密码: X9JAJQQr7 (你自己日志里面的)\n语言切换: Tools -\u0026gt; Options -\u0026gt; User interface language -\u0026gt; 简体中文 点击 Save 保存\n密码修改: Tools -\u0026gt; Options -\u0026gt; WebUI -\u0026gt; 用户名/密码 点击 Save 保存\n7. jellyfin Jellyfin是一款免费、开源的自托管媒体服务器软件，用于集中管理和流式传输视频、音乐、照片等个人媒体文件。它是Emby的自由分支，用户可完全控制数据。Jellyfin可在Windows、Linux、Docker等多种平台运行，支持硬解转码，并在电视、手机、网页端同步播放记录。\n1 2 3 4 5 6 7 8 # 创建文件夹 mkdir -p /opt/jellyfin/config # 进入文件夹 cd /opt/jellyfin # 创建 compose.yml 文件 vim compose.yml 数据挂载目录根据自己的需要填写\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 services: jellyfin: image: lscr.io/linuxserver/jellyfin:latest container_name: jellyfin environment: - PUID=0 - PGID=0 - TZ=Asia/Shanghai volumes: # 左边是你宿主机的路径，右边是容器内的路径。请确保左边的路径真实存在！ - /opt/jellyfin/config:/config # 存放 Jellyfin 配置文件和海报缓存 - /mnt/raid1_4t/PT/MT:/media/raid1_4t/PT/MT # 存放你电影/电视剧的根目录 - /mnt/raid1_4t/归档/video:/media/归档 # 存放你电影/电视剧的根目录 ports: - 8096:8096 # 默认的 Web 访问端口 devices: - /dev/dri:/dev/dri # 将 J1900 的核显直通给容器，用于硬件加速 restart: unless-stopped 然后启动容器 docker compose up -d 浏览器访问: http://NAS_IP:8096 即可进入初始化页面\n","date":"2026-03-01T00:00:00Z","permalink":"https://izumi.pub/NAS/nas-debian13/","title":"蜗牛星际 (J1900) Debian 13 安装与 RAID 1 配置"},{"content":"想要快速查询磁盘数据，本质就是尽可能高效的在内存中过滤不需要扫描的内容。\n存储原理 此处仅讨论MergeTree 系列表引擎\nClickHouse 底层数据通过文件夹分区，当数据到达一定量后，在分区文件夹下，每一个列字段都拥有独立的、以列字段名称命名的.bin数据文件和.mrk索引文件；同时还存在一个主键索引primary.idx(稀疏索引)。\nidx、mrk 文件如果是压缩格式，后缀会变为.cidx、.cmrk；同时.mrk 还有多种格式比如：.mrk2、.mrk3\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ├── 202504_1_1_0\t# 分区，按照分区键配置生成 │ ├── ..... │ ├── ..... ├── 202505_1_1_0 │ ├── checksums.txt │ ├── columns.txt\t# 表结构 │ ├── count.txt # 表数据量 │ ├── default_compression_codec.txt # 默认压缩编码：CODEC(LZ4) │ ├── metadata_version.txt │ ├── primary.idx\t# 主键索引 │ ├── id.bin\t# ID列数据 │ ├── id.mrk\t# ID列索引(标记)文件、用于标记数据在bin文件中的偏移量 │ ├── trans_id.bin │ ├── trans_id.mrk │ ├── price.bin │ ├── price.mrk │ ├── product_id.bin │ ├── product_id.mrk │ ├── create_date.bin │ ├── create_date.mrk │ ├── serialization.json ├── detached └── format_version.txt 分区 可以通过 PARTITION BY 指定一张表的分区键，通常推荐toYYYYMM(Date)按月分区\n此时底层数据会按月存放在文件夹中，方便查询时快速过滤。\n分区名称的格式为：202405_minBlock_maxBlock_level，每次数据插入都会生成一个新分区，并在一段时间后，相同分区数据将进行合并，所以插入数据尽量使用批量插入。分区合并完成后，旧分区会标记为不活跃、不会立即删除，在一段时间后才会清理。\n1 2 3 分区新建时，会根据maxBlock自增并创建一个：202405_maxBlock_maxBlock_0 分区 合并时会生成新文件夹：202405_minBlock_maxBlock_level，并将level+1 例如 all_1_2_1 + all_3_3_0 =\u0026gt; all_1_3_2 注：只有在同一分区(文件夹)内查询，数据才是天然有序的，ReplacingMergeTree去重也只在同一分区内；对于合并前的分区亦是如此，在合并时会才进行排序、去重。\n手动触发分区合并 我们可以通过手动执行OPTIMIZE TABLE table_name PARTITION partition_name来进行分区合并\n或者直接执行OPTIMIZE TABLE table_name FINAL对整张表进行合并\n列式存储 排序 可以通过ORDER BY 指定多个排序字段，最终数据会按照排序字段依次排序存储；列式+排序存储有两个好处：\n顺序结构能够实现二分查找 排序后的数据能够获得更好的压缩率 我们称每一个.bin文件为一个数据片段，数据片段可以以 Wide 或 Compact 格式存储。在 Wide 格式下，每一列都会在文件系统中存储为单独的文件，在 Compact 格式下所有列都存储在一个文件中。Compact 格式可以提高插入量少插入频率频繁时的性能。\n数据存储格式由 min_bytes_for_wide_part 和 min_rows_for_wide_part 表引擎参数控制。如果数据片段中的字节数或行数少于相应的设置值，数据片段会以 Compact 格式存储，否则会以 Wide 格式存储。\n主键和排序字段的关系 主键 ∈ 排序字段\n如果不指定主键，则排序字段会被隐式指定为主键 数据存储是按照排序字段依次顺序存储（可以推导出、主键必须是排序字段的前缀 主键索引是常驻内存的 存储格式 每一列数据就是一个单独的bin文件，数据按主键顺序紧密的排列在一起。\n在物理层面数据划分为多个压缩块；在逻辑层面又划分为多个颗粒。这个压缩块和颗粒是一对多 or 多对一的关系。\n颗粒 颗粒是流进ClickHouse进行数据处理的最小的不可分割数据集，每个颗粒的数据行数由index_granularity参数配置，默认8192。\n对于具有自适应索引粒度的表（默认情况下索引粒度是自适应的），某些粒度的大小可以小于 8192 行，具体取决于行数据大小。具体大小由index_granularity_bytes参数控制。\n压缩块 一个压缩块大小为64K ~ 1Mb，如果一个颗粒的数据太小，一个压缩块就会包含多个颗粒内的数据；如果一个颗粒数据又太大，就会拆分一个颗粒的数据。查询时被定位的压缩文件块在读取时被解压到内存中。\n索引 主键索引 每个颗粒的第一条数据会被抽出形成主键(稀疏)索引，所以可以得出：主键索引数 == 颗粒数量\n标记文件 标记文件作为主键索引和压缩列数据的桥梁，保存了两个偏移量信息(位置)；同时主键索引数 == 标记文件行数\n第一列标识数据所在压缩块位于整个压缩列(.bin文件)的偏移量 第二列标识数据在解压数据块中的偏移量 因为标记文件的两列是定长字段，所以通过主键所在列序号就能找到对应的标记文件。\n再通过标记文件列的两个偏移量，就能定位到对应的颗粒\n跳数索引 二级索引\n总结 为什么需要标记文件？ 为什么主索引不直接包含与索引标记相对应的颗粒的物理位置？因为主索引文件需要放入内存中，如不用标记文件，则需要将每列的偏移量都存储在主键索引中，浪费内存资源。\n为什么会有压缩块？ 为什么颗粒和压缩块不一一对应 压缩块设计更多是为了存储和IO优化，而颗粒是为了调整查询效率；两者解耦更灵活\n.bin就是一个文件，为什么内部要分块压缩 因为压缩算法是需要结合数据上下文进行压缩，没办法根据偏移量单独取出一块读取，所以将原始数据先分段后压缩，能够减少每次查询所需读取解压的数据量\n多主键优化 本质上是全量复制一份数据，按照新主键的顺序重新生成底层文件系统。有如下三种方法\n新建一个不同主键的新表：需要双写手动维护，读取时需要手动选择读取的表 创建一个物化视图：数据表自动同步，但读取时仍需要手动选择读取的表 增加projection：数据表自动同步，读取时自动选取最合适的表 二进制索引文件分析 因为不懂C++，尝试从源码分析有点无从下手，所以直接针对二进制文件，通过猜测+验证找出索引排布规律，故本文只分析部分数字+字符串类型的索引。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 -- 准备一张测试表；为方便测试，进行一些 SETTINGS 配置 CREATE TABLE default.Order ( `id` UInt16, `trans_id` String, `product_id` String, `price` UInt16 ) ENGINE = MergeTree ORDER BY (id, trans_id, price) -- 即为主键索引 SETTINGS compress_marks = 0, -- 禁用压缩，方便分析二进制文件 compress_primary_key = 0, -- 禁用压缩，方便分析二进制文件 index_granularity = 2, -- 索引步长、即每两条记录生成一条主键索引，方便观测 index_granularity_bytes = 0; -- 禁用根据数据大小自适应索引 现在插入两条数据：\nid trans_id product_id price 1 abcdefg product 15 2 abcdefgh product 30 3 abcdefghijklmn\u0026hellip;\u0026hellip;opqrstuvwxyz (总长度260) product 3000 4 b product 3000 5 c product 3100 6 d product 3400 那么索引应该是类似这样的格式：UInt16-String-UInt16\n即： 1-abcdefg-15 3-abcdefg...xyz-3000 5-c-3100\nprimary.idx 接下来通过 hexed 打开primary.idx验证猜想，如下图所示：\n数字 会按照定义类型，占用固定大小 字符串 分为长度和数据两块，其中长度部分会根据数据大小占用一到多个字节 此时肯定会有一个疑问，当String长度大于一个字节能表示的范围(255)时，应该怎么储存？\n我给出一个个人猜测的在一定范围适用的公式，假设是两个字节表示长度，两字节转10进制分别为：A、B\n则字符串长度为 A + 128 * (B - 1)，带入上面第二行数据即为：132 + 128 * (2 - 1) = 260\n也可以通过JavaScript 直接计算：parseInt('84', 16) + 128 * (parseInt('02', 16) - 1) = 260\n注：这个B的值应该只会在[0, 一个小数]内，这样才能准确截断长度字段与数据字段。有懂哥愿意贴上C++源码层面的分析最好不过了。\ncolumn.mrk .mrk 是一个两列多行文件，行数和primary.idx 相当且按序一一对应\n比如id.mrk 一共三行，代表有三个颗粒；第一列都是0，代表全部属于同一压缩块；第二列是0、4、8正好就对应了解压后的id字段长度。\n而trans_id.mrk是不定长的字段，所以第二列分别为0、17、281\ncolumn.bin .bin文件就是最终数据存储的地方，是一个压缩的数据列，直接打开只能大概窥探一下数据样貌：\n每个压缩块由头文件+数据文件组成，头文件包括压缩协议、压缩前后大小信息\n向量引擎 可以很好的利用CPU的SIMD指令：单指令流多数据流（英语：Single Instruction Multiple Data）\nSIMD(Single instruction, multiple data)单指令多数据，是一种通过单个指令同时进行多个数据运算的方式，通过增加运算单元位宽、计算单元数量数量、寄存器位宽可以同时进行更多数据的运算，普通指令单个周期通常只能支持 2个数据的运算，SIMD指令单个周期可以同时几十个数据的运算。\n多线程 ClickHouse会使用服务器上一切可用的资源，从而以最自然的方式并行处理大型查询。\n因为数据通过分区、多列、颗粒等划分明显，所以很简单的利用多线程进行读取；单个查询就能消耗相当多的CPU资源，当然这带来的缺陷就是QPS较低。\n分布式、多副本 可以通过 zookeeper 进行数据协调，将数据存储在多机器上。\n","date":"2024-05-02T00:00:00Z","permalink":"https://izumi.pub/ClickHouse/principle/","title":"「为什么这么快」ClickHouse从索引文件详解到底层原理"},{"content":"注：以下内容仅为本人成功经验，电脑小白请三思而后行，操作不当可能导致文件丢失、无法进入系统等后果。请做好备份工作。\n本文环境 在本人两台电脑下测试均成功，使用的在线账户登录\nWindows 11 专业版 22H2 22621.1413\nWindows 11 专业版 Insider Preview 22H2 25309.1000\n正文 温馨提示：因操作涉及到电脑重启，请用别的设备打开本文后再进行接下来的操作。\n1.修改注册表 1.Win+R 快捷键打开 regedit\n2.依次进入：HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\n3.双击ProfileImagePath修改为你的新用户名\n2.重启进入临时账户 直接重启，输入密码登录；此时会因为找不到你的账户，系统会卡住转圈很久，耐心等待进入系统后会提示你是否注销，直接关闭弹窗进行下面步骤。\n3.修改文件夹名 按照路径进入后，将文件夹修改为你的新用户名\n如果遇到文件夹正在使用的提示\n请尝试按 Win + S 键搜索 “资源监视器” 并打开；点击窗口上的 CPU 标签；点击 “关联的句柄” 右侧的搜索框输入 C:\\Users\\izumi(记得改为你自己的路径)\n接着在下面的搜索结果列表中，会看到正在使用该文件夹的程序；然后全选右键选择结束进程后再执行重命名操作。\n4.创建新旧文件夹之间的链接 1.按图中路径找到并以管理员身份运行cmd.exe\n2.输入mklink /j C:\\Users\\pyrde C:\\Users\\izumi运行，前面路径是你的旧用户名，后面是新用户名。\n5.修改用户名 接着输入control userpasswords2，找到当前用户点击属性，然后修改用户名保存，重启电脑；此时基本就算修改完成了。\n后记 1.小问题 因为我们是通过建立的链接来使得访问旧路径的程序也能正常访问到新文件夹的内容，所以其实并不完美，比如OneDrive他还是显示指向原来的路径（我们在正文第4步进行了链接操作，所以程序本身是能正常使用的）\n当然我们可以在OneDrive设置中先取消链接，再重新添加，就可以更改为新用户名的位置。\n所以理论上我们可以不执行正文第4步，当遇到软件无法正常运行时再手动修改路径or环境变量。\n2.没敢尝试的办法 网上有通过搜索注册表将旧用户名批量替换为新用户名的操作，但我没敢尝试，有兴趣的可以自己试试。\n","date":"2023-03-19T00:00:00Z","permalink":"https://izumi.pub/Windows/change-windows-username/","title":"Windows 11修改C盘用户文件夹名称"},{"content":"\n最近入手了小新Pro 14 锐龙版，作为一台轻薄本，那么使用硕大的原装充电器显然不太合适。\n但在我购买了65w氮化镓后发现功率不能满载，只能跑到45w，于是干脆就做个简单的测评作为一个选购参考。\n本次选用的氮化镓是努比亚的65w/120w这两款。\n简单测试 本次通过Cinebench在极致性能模式下跑单核/多核测试时，观察CPU频率和整机功耗来大致判断充电器对性能的影响。\n单核 65w氮化镓 100w氮化镓 原装充电器 CPU频率 4.29GHz 4.29GHz 4.29GHz 整机功耗 32w 32.8w 31.3w 可见处理单核任务3款充电器并无差别\n多核 65w氮化镓 100w氮化镓 原装充电器 CPU频率 3.07GHz 3.38GHz 3.38GHz CPU温度 \u0026ndash; 89℃ 89℃ CPU功耗 35w 44.9w 44.9w 整机功耗 45w 69.5w 69.3w Cinebench R23多核跑分 \u0026ndash; 11075 11028 从原装充电器的测试结果来看，小新Pro 14满载整机功耗也就70w左右，65w氮化镓理应能够胜任，但是实际情况是65w氮化镓不能满载运行，功耗只能达到45w左右（未能测试其他品牌的氮化镓，如有能完美适配的麻烦告知）；而100w氮化镓就能和原装达到一样的效果。\n结论 1.如果能找到可以满负载运行的65w氮化镓，那么选用65w完全没问题\n2.如果只是轻度办公，想以轻便为主，可以选用65w\n3.如果电脑经常需要火力全开 or 不能忍受性能损失，选用100w氮化镓\n","date":"2021-06-03T00:00:00Z","permalink":"https://izumi.pub/PC/xiaoxin-pro14-GaN/","title":"小新Pro 14 锐龙版 65w/100w氮化镓 vs 原装充电器"},{"content":"0.接入推送 0.创建推送证书、创建极光推送应用 这部分不做赘述\n1.接入iOS SDK 在 Siging \u0026amp; Capabilities 中添加 Access WiFi Information 和 PushNotifications\n同时打开 Remote notifications\n在Podfile中加入\n1 2 pod \u0026#39;JCore\u0026#39; pod \u0026#39;JPush\u0026#39; 然后执行 pod install 导入SDK\n在桥接文件中加入\n1 2 3 4 5 6 // 引入 JPush 功能所需头文件 #import \u0026#34;JPUSHService.h\u0026#34; // iOS10 注册 APNs 所需头文件 #ifdef NSFoundationVersionNumber_iOS_9_x_Max #import \u0026lt;UserNotifications/UserNotifications.h\u0026gt; #endif 这部分会有版本选择以及IDFA等问题，详见：极光文档\n1.接收消息 1.注册极光推送 在AppDelegate中的didFinishLaunchingWithOptions中注册极光推送\n1 2 3 4 5 6 7 8 9 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -\u0026gt; Bool { // 推送代码 let entity = JPUSHRegisterEntity() entity.types = 1 \u0026lt;\u0026lt; 0 | 1 \u0026lt;\u0026lt; 1 | 1 \u0026lt;\u0026lt; 2 JPUSHService.register(forRemoteNotificationConfig: entity, delegate: self) // 填写自己的appKey JPUSHService.setup(withOption: launchOptions, appKey: \u0026#34;xxxxx\u0026#34;, channel: \u0026#34;App Store\u0026#34;, apsForProduction: false, advertisingIdentifier: nil) return true } 2.接收消息 编写上传deviceToken、接收推送消息的执行方法\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 // MARK: --推送代理 extension AppDelegate : JPUSHRegisterDelegate { func jpushNotificationCenter(_ center: UNUserNotificationCenter!, openSettingsFor notification: UNNotification!) { print(\u0026#34;jpushNotificationCenter\u0026#34;) } func jpushNotificationAuthorization(_ status: JPAuthorizationStatus, withInfo info: [AnyHashable : Any]!) { print(\u0026#34;jpushNotificationAuthorization\u0026#34;) } //系统获取Token func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { _ = deviceToken.reduce(\u0026#34;\u0026#34;,{$0 + String(format:\u0026#34;%02x\u0026#34;,$1)}) JPUSHService.registerDeviceToken(deviceToken) } //获取token 失败 func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { //可选 print(\u0026#34;did Fail To Register For Remote Notifications With Error: \\(error)\u0026#34;) } @available(iOS 10.0, *) func jpushNotificationCenter(_ center: UNUserNotificationCenter!, willPresent notification: UNNotification!, withCompletionHandler completionHandler: ((Int) -\u0026gt; Void)!) { let userInfo = notification.request.content.userInfo if notification.request.trigger is UNPushNotificationTrigger { JPUSHService.handleRemoteNotification(userInfo) UIApplication.shared.applicationIconBadgeNumber = notification.request.content.badge as! Int } // 需要执行这个方法，选择是否提醒用户，有Badge、Sound、Alert三种类型可以选择设置 print(\u0026#34;前台 收到推送 userInfo=\\(userInfo)\u0026#34;) completionHandler(Int(UNNotificationPresentationOptions.alert.rawValue)) } @available(iOS 10.0, *) func jpushNotificationCenter(_ center: UNUserNotificationCenter!, didReceive response: UNNotificationResponse!, withCompletionHandler completionHandler: (() -\u0026gt; Void)!) { print(\u0026#34;点击推送消息 content=\\(response.notification.request.content.userInfo)\u0026#34;) let userInfo = response.notification.request.content.userInfo if response.notification.request.trigger is UNPushNotificationTrigger { JPUSHService.handleRemoteNotification(userInfo) } completionHandler() } } 这时候我们已经能在jpushNotificationCenter 中接收到推送消息内容了\n3.为设备设置唯一标识 一般将用户的唯一id、手机号用作标识符，只需在用户登录后执行\n1 2 3 JPUSHService.setAlias(\u0026#34;手机号\u0026#34;, completion: { (iResCode, iAlias, seq) in print(\u0026#34;alias,\\(\u0026#34;手机号\u0026#34;) . completion,\\(iResCode),\\(String(describing: iAlias)),\\(seq)\u0026#34;) }, seq: 0) 4.测试消息推送 前往极光官网、找到发送通知，只需填写这几项就可以测试是否能收到推送。\n最后收到的推送大致格式为：\n1 2 3 4 5 6 7 8 { \u0026#34;alert\u0026#34;: { \u0026#34;body\u0026#34;: \u0026#34;test body\u0026#34;, \u0026#34;title\u0026#34;: \u0026#34;test title\u0026#34; }, \u0026#34;badge\u0026#34;: 1, \u0026#34;sound\u0026#34;: \u0026#34;default\u0026#34; } 2.跳转页面 首先在应用主页面(或其他页面)的ViewController里创建跳转页面相关代码：\n1 2 3 4 5 6 7 8 9 override func viewDidLoad() { NotificationCenter.default.addObserver(self, selector: #selector(jpush(noti:)), name: NSNotification.Name(rawValue: \u0026#34;jpush\u0026#34;), object: nil) } // MARK: - 直接跳转对应页面 @objc func jpush(noti: Notification) { let jpushViewController = mainStoryboard.instantiateViewController(withIdentifier: \u0026#34;jpushViewController\u0026#34;)as!JpushViewController self.present(jpushViewController, animated: true, completion: nil) } 然后在用户点击推送时执行：\n1 2 3 4 5 6 7 8 9 10 11 @available(iOS 10.0, *) func jpushNotificationCenter(_ center: UNUserNotificationCenter!, didReceive response: UNNotificationResponse!, withCompletionHandler completionHandler: (() -\u0026gt; Void)!) { print(\u0026#34;点击推送消息 content=\\(response.notification.request.content.userInfo)\u0026#34;) let userInfo = response.notification.request.content.userInfo if response.notification.request.trigger is UNPushNotificationTrigger { JPUSHService.handleRemoteNotification(userInfo) } let notification = NSNotification.Name(rawValue: \u0026#34;jpush\u0026#34;) NotificationCenter.default.post(name: notification, object: nil, userInfo: nil) completionHandler() } 跳转页面功能就完成了。如果需要推送消息携带额外信息，可以在后台将键值对添加到 extra 中，键值对会被添加在 response.notification.request.content.userInfo 里，直接取出就行。\n","date":"2020-07-27T00:00:00Z","permalink":"https://izumi.pub/iOS/ios-jpush/","title":"iOS(Swift)通过极光推送实现跳转自定义页面"},{"content":"1.拆 首先拆掉无线网卡和主板连接的两颗螺丝，就可以直接拔下来，可以看到原装的网卡型号是3168NGW\n然后拆掉那个黑色海绵垫、就可以直接拔下与网卡相连的两根天线。\n2.装 网卡选择的是DW 1820A\n直接拔掉网卡和外壳处的连线，然后将网卡替换上去固定。\n3.替换EFI EFI替换可以参考这篇文章，DW1820A单独的蓝牙驱动该文也有下载\n华擎 Asrock B365M-ITX/ac macOS Catalina 完美黑苹果 OC/Clover双版本\n4.大概测试 WIFI-电脑到NAS的上下行\n蓝牙也支持AirPods Pro的降噪控制，连接也比较稳定。\n","date":"2020-06-20T00:00:00Z","permalink":"https://izumi.pub/PC/b365m-itx-dw1820a-hackintosh/","title":"华擎B365M-ITX AC更换DW1820A网卡 支持黑苹果"},{"content":"1.纯图片替换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // 只需在以下函数中重设annotationView图片就行 func mapView(_ mapView: MAMapView!, viewFor annotation: MAAnnotation!) -\u0026gt; MAAnnotationView! { if annotation.isKind(of: MAPointAnnotation.self) { if annotation.title != \u0026#34;当前位置\u0026#34; { let pointReuseIndetifier = \u0026#34;pointReuseIndetifier\u0026#34; var annotationView: MAPinAnnotationView? = mapView.dequeueReusableAnnotationView(withIdentifier: pointReuseIndetifier) as! MAPinAnnotationView? if annotationView == nil { annotationView = MAPinAnnotationView(annotation: annotation, reuseIdentifier: pointReuseIndetifier) } annotationView!.canShowCallout = true annotationView!.image = UIImage(named: \u0026#34;公交站牌32\u0026#34;) // 图片偏移量，根据自己图片不同来设定 annotationView!.centerOffset = CGPoint(x: 0, y: -18); return annotationView! } } return nil } 点击以后弹出\n但我们如果是想同时显示图标和文字，则需要自己自定义标记\n2.自定义样式同时显示图片文字 1.自定义一个UIView组件 参考链接：Swift - XIB结合UIView制作自定义组件（xib加载绘制UIView）\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class CustomStationAnnotationView: UIView { @IBOutlet weak var stationImage: UIImageView! @IBOutlet weak var stationLabel: UILabel! //布局相关设置 override func layoutSubviews() { super.layoutSubviews() } /*** 下面的几个方法都是为了让这个自定义类能将xib里的view加载进来。这个是通用的，我们不需修改。 ****/ var contentView:UIView! //初始化时将xib中的view添加进来 override init(frame: CGRect) { super.init(frame: frame) contentView = loadViewFromNib() // 设置字体、背景颜色 stationLabel.backgroundColor = ViewUtility.UIColorFromRGB(color_vaule: \u0026#34;#485A73\u0026#34;) stationLabel.textColor = ViewUtility.UIColorFromRGB(color_vaule: \u0026#34;#EEDA1B\u0026#34;) addSubview(contentView) } //初始化时将xib中的view添加进来 required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) contentView = loadViewFromNib() addSubview(contentView) } //加载xib func loadViewFromNib() -\u0026gt; UIView { let className = type(of: self) let bundle = Bundle(for: className) let name = NSStringFromClass(className).components(separatedBy: \u0026#34;.\u0026#34;).last let nib = UINib(nibName: name!, bundle: bundle) let view = nib.instantiate(withOwner: self, options: nil).first as! UIView return view } } 站点样式暂时就画成下面这个样子，注意将Background设为Clear Color，即无背景色\n2.自定义CustomAnnotationView 新建一个类CustomAnnotationView，继承MAAnnotationView，重写init引入自定义的UIView\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import UIKit class CustomAnnotationView: MAAnnotationView { override init!(annotation: MAAnnotation!, reuseIdentifier: String!) { super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) // 引入自定义UIView let station = CustomStationAnnotationView.init(frame: CGRect.init(x: 0, y: 0, width: 80, height: 60)) // 设置样式里的站点名称 let title:String = annotation.title as! String station.stationLabel.text = title self.addSubview(station) } required init?(coder: NSCoder) { fatalError(\u0026#34;init(coder:) has not been implemented\u0026#34;) } } 3.添加站点信息 首先创建一个mapview(这部分不作讲解)，然后添加自定义点标记\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 // Station为一个自定义的对象，包含 名称经纬度 就行 var stationArray=Array\u0026lt;Station\u0026gt;() var showStationAnnotationArray=Array\u0026lt;MAPointAnnotation\u0026gt;() // 本地添加一个站点数据 func addStationArray() { let station = Station.init() station.name = \u0026#34;公交站\u0026#34; station.lat = 2253242 station.lng = 11395239 stationArray.append(station) self.showStationPoint() } // MARK: - 添加站点信息 func showStationPoint() { for station in stationArray { let pointAnnotation = MAPointAnnotation() pointAnnotation.coordinate = CLLocationCoordinate2D(latitude: Double(station.lat)/100000.0, longitude: Double(station.lng)/100000.0) pointAnnotation.title = station.name pointAnnotation.subtitle = station.note showStationAnnotationArray.append(pointAnnotation) } mapview.addAnnotations(showStationAnnotationArray) } //MARK: - 设置地图站点样式 func mapView(_ mapView: MAMapView!, viewFor annotation: MAAnnotation!) -\u0026gt; MAAnnotationView! { if annotation.isKind(of: MAPointAnnotation.self) { if annotation.title != \u0026#34;当前位置\u0026#34; { let customReuseIndetifier: String = \u0026#34;customReuseIndetifier\u0026#34; var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: customReuseIndetifier) as? CustomAnnotationView if annotationView == nil { annotationView = CustomAnnotationView.init(annotation: annotation, reuseIdentifier: customReuseIndetifier) } annotationView?.canShowCallout = false annotationView?.isDraggable = true annotationView!.centerOffset = CGPoint(x: -40, y: -30) return annotationView! } } return nil } 最终展示效果如图：\n","date":"2020-04-28T00:00:00Z","permalink":"https://izumi.pub/iOS/iOS-amap-sdk/","title":"iOS 高德SDK Swift自定义地图点标记样式"},{"content":"0.基础知识 概念 解释 仓库（Repository） 用于存放各种镜像的地方 镜像（Image） 可以理解为一个安装包，用它创建容器。\n一个镜像可以创建多个实例同时运行，互不干扰。 容器（Container） 由镜像创建的，可以运行的实例 1.镜像 1.下载 1 2 3 docker search redis # 查找镜像 docker pull redis\t# 下载镜像, 通常直接下载就好了 docker pull redis:5.0.8 # 也可以带上版本号下载指定版本 2.查看、删除 1 2 docker images\t# 查看本地已下载镜像 docker rmi \u0026#39;IMAGE ID\u0026#39;\t# 删除本地镜像 2.容器 1.创建运行容器 镜像下载完毕后用其创建一个容器\n1 2 docker run -itd --name my_redis -p 6480:6379 redis --requirepass \u0026#34;password\u0026#34; # 创建运行容器 docker ps -a\t# 查看当前所有的容器 这时我们就成功的安装好了redis，下面来稍微讲解下创建容器那条命令\n首先是 -itd 按照我个人的理解\ni 是启用input(输入)功能，容器开启后能后直接输入命令操作容器\nt 是连接容器内部的terminal(终端)\n这两个参数一般是连在一起使用，想要详细了解可以尝试分别用 -i -t -it 创建3个centos容器进行操作，立马就能理解这两个参数的不同与作用。\nd 是容器创建后直接后台运行（之后可以通过其他命令进入容器内部）\n--name my_redis 就是给容器起个名字\n-p 6480:6379 是将容器内的6379(redis默认端口) 映射到本机的6480端口，这样我们就可以通过访问127.0.0.1:6480 连接redis\n--requirepass \u0026quot;password\u0026quot; 是给redis设置一个访问密码，如果你redis只是本地使用，那么不设密码也可以\n2.重启、停止容器 1 2 3 4 docker stop \u0026#39;CONTAINER ID\u0026#39;\t# 停止容器 docker start \u0026#39;CONTAINER ID\u0026#39;\t# 启动容器 docker restart \u0026#39;CONTAINER ID\u0026#39;\t# 重启容器 # 其中\u0026#39;CONTAINER ID\u0026#39; 可以用容器名称(\u0026#34;my_redis\u0026#34;)代替 3.删除容器 1 docker rm -f \u0026#39;CONTAINER ID\u0026#39;\t# 删除时需要先停掉容器 ","date":"2020-04-03T00:00:00Z","permalink":"https://izumi.pub/Docker/docker-use/","title":"Docker的使用笔记"},{"content":"1.什么时候使用构建器（Builder） 通常我们只需要使用 new User() 就可以创建一个对象，那么为什么还需要使用构建器呢。\n假设我们有这么一个类\n1 2 3 4 5 6 7 public class User { private int id; private String name; private String phone; private String sex; private String IDCard; } 如果创建一个User对象只有id和name是必须的，其他参数可以选填，也可以用下面两种方法创建该对象\n1.构造函数 1 2 3 4 5 6 7 8 9 10 11 12 public class User { private int id; private String name; private String phone; private String sex; private String IDCard; public User(int id, String name) { this.id = id; this.name = name; } } User user = new User(1,\u0026quot;izumi\u0026quot;); 就能创建一个对象。那这时如果想用三个、五个参数，就只能依次增加多个构造函数\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public User(int id, String name) { this.id = id; this.name = name; } public User(int id, String name, String phone) { this.id = id; this.name = name; this.phone = phone; } public User(int id, String name, String phone, String sex, String IDCard) { this.id = id; this.name = name; this.phone = phone; this.sex = sex; this.IDCard = IDCard; } 这显然就有些不那么优雅\n2.set方法 我们可以使用JavaBeans的set方法来实现多个参数赋值（这里为方便直接用了lombok）\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import lombok.Data; @Data public class User { private int id; private String name; private String phone; private String sex; private String IDCard; public User(int id, String name) { this.id = id; this.name = name; } } 1 2 3 User user = new User(1,\u0026#34;izumi\u0026#34;); user.setSex(\u0026#34;男\u0026#34;); user.setPhone(\u0026#34;xxxxx\u0026#34;); 这种做法简单易懂，但存在一些缺陷\n因为构造过程被分到了几个调用中，在构造过程中JavaBean可能处于不一致的状态。\n\u0026mdash;《Effective Java》\n这时我们就可以尝试使用构建器（Builder）\n2.怎样使用构建器 直接看示例代码\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package pub.izumi.coolqs; public class User { private int id; private String name; private String phone; private String sex; private String IDCard; public static class Builder { private int id; private String name; private String phone; private String sex; private String IDCard; public Builder(int id, String name) { this.id = id; this.name = name; } public Builder phone(String val) { this.phone = val; return this; } public Builder sex(String val) { this.sex = val; return this; } public Builder IDCard(String val) { this.IDCard = val; return this; } public User build() { return new User(this); } } private User(Builder builder) { id = builder.id; name = builder.name; phone = builder.phone; sex = builder.sex; IDCard = builder.IDCard; } } builder方法在设值之后返回其本身，以便于把调用形成链接。创建对象示例：\n1 2 User user1 = new User.Builder(1, \u0026#34;izumi\u0026#34;).build(); User user2 = new User.Builder(1, \u0026#34;izumi\u0026#34;).sex(\u0026#34;男\u0026#34;).phone(\u0026#34;**\u0026#34;).build(); ","date":"2020-04-02T00:00:00Z","permalink":"https://izumi.pub/Java/java-object-builder/","title":"《Effective Java》之使用构建器创建对象"},{"content":"基本概述 当我们想要在外地连接家里的网络服务，而家里又没有公网 ip，此时就需要用到内网穿透。\n当用户无法直接连上家里的服务时，借助一台带有公网 ip 的服务器就可以使其通信。\n接下来我们要做的就是在服务器和家里分别安上 frp 的服务端和客户端软件。\n软件安装地 frp 软件 服务器 frp 服务端-frps 本地(NAS、Windows 等) frp 客户端-frpc 软件下载 [button color=\u0026ldquo;success\u0026rdquo; icon=\u0026ldquo;iconfont icon-fork\u0026rdquo; url=\u0026ldquo;https://github.com/fatedier/frp/releases\u0026rdquo;]github[/button]\n1 wget https://github.com/fatedier/frp/releases/download/v0.32.0/frp_0.32.0_linux_amd64.tar.gz 安装 1.服务端(带公网 ip 的主机) 1 2 3 tar -xvf frp_0.32.0_linux_amd64.tar.gz rm -f frpc*\t# 删除客户端多余文件,可以不执行 vim frps.ini\t# 编辑配置文件 输入以下字段后保存就行，其中bind_port = 7000就是 frp 之间通信所用端口。\n1 2 3 4 5 6 7 [common] bind_port = 7000 vhost_http_port = 8080 max_pool_count = 5 authentication_timeout = 900 authentication_method = token token = password 然后就可以开启服务端等待客户端的连接\n1 ./frps -c frps.ini 2.客户端(本地 NAS) 同样解压刚才的压缩包（如果客户端是 windows/mac 的话前往原 git 下载）\n1 2 3 tar -xvf frp_0.32.0_linux_amd64.tar.gz rm -f frps*\t# 删除服务端多余文件,可以不执行 vim frpc.ini\t# 编辑配置文件 输入以下字段后保存就行，其中bind_port = 7000是 frp 之间通信所用端口\n可以配置多个应用，比如你想在外连接 NAS，那么就把NAS本地的22端口映射到服务器的6000端口(或其他任意未占用端口)。\n这样访问 服务器ip:6000就等同于本地访问127.0.0.1:22\n[scode type=\u0026ldquo;red\u0026rdquo;] 注：服务器有防火墙的话需要将用到的 remote_port 端口全部打开 [/scode]\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 [common] server_addr = 服务器的公网ip server_port = 7000 token = password pool_count = 1 [ssh] type = tcp local_ip = 127.0.0.1 local_port = 22 remote_port = 6000 use_compression = true [nas] type = tcp local_ip = 127.0.0.1 local_port = 5000 remote_port = 5000 use_compression = true [mysql] type = tcp local_ip = 127.0.0.1 local_port = 3306 remote_port = 3306 use_compression = true 1 2 # 同样方式打开客户端 ./frpc -c frpc.ini 这时，内网穿透就算完成了，但想要长期稳定使用的话还得加上后台运行和开机自启。\n3.开机自启 这里提供两种自启方式，如果客户端/服务端是 windows/mac 的话自行搜索一下\n群晖 1 2 cd /usr/syno/etc.defaults/rc.sysv/ vim frpc.sh\t# 创建启动脚本 脚本中填上这两行\n1 2 cd /usr/frp_0.32.0_linux_amd64\t# 进入frpc路径 nohup ./frpc -c ./frpc.ini \u0026amp;\t# 后台启动frpc 设置脚本权限并编辑启动项\n1 2 chmod +x frpc.sh vim /etc/rc 输入 :$ 跳转至最后一行，在exit 0之前添加：\n1 /usr/syno/etc.defaults/rc.sysv/frpc.sh 这时可以重启系统查看 frp 是否启动成功\ncentos 1.创建自启服务\n1 vim /etc/systemd/system/frps.service 填入:(记得将 ExecStart 路径换成自己 frps 的路径)\n1 2 3 4 5 6 7 8 9 10 11 12 13 [Unit] Description=Frp Server Service After=network.target [Service] Type=simple User=nobody Restart=on-failure RestartSec=5s ExecStart=/home/izumi/frp_0.32.0_linux_amd64/frps -c /home/izumi/frp_0.32.0_linux_amd64/frps.ini [Install] WantedBy=multi-user.target 2.开启服务 \u0026ndash; (只需要执行 1、3)\n1 2 3 4 sudo systemctl enable frps\t#打开自启 sudo systemctl disable frps\t#关闭自启 sudo systemctl start frps\t#开启frps sudo systemctl status frps\t#查看frps运行状态 官方文档 本文只是介绍基本使用，更多功能和详细使用方式见： [button color=\u0026ldquo;success\u0026rdquo; icon=\u0026ldquo;iconfont icon-fork\u0026rdquo; url=\u0026ldquo;https://github.com/fatedier/frp/blob/master/README_zh.md\u0026rdquo;]官方文档[/button]\n","date":"2020-03-21T00:00:00Z","permalink":"https://izumi.pub/Linux/frp/","title":"Frp实现内网穿透-以群辉NAS为例"},{"content":"0.总览 我组装便携屏其实并不是为了便携，只是想办公时多添加一块显示器，单显示器有时确实有些不方便，而普通的1080p在mac下的显示效果实属辣眼睛，再买一台4k又嫌太贵，于是就萌生出组装一块小尺寸2k屏的想法。\n组装便携屏主要是分为三步：买屏幕，买驱动板，买/制作一个合适的外壳。\n本次组装的是15.6 2k的便携显示器，大约花费700元。\n1.屏幕 首先上屏库网(www.panelook.cn)挑选一块合适的屏幕，主要关注的就是大小和分辨率。\n在搜出来的型号中进行二次筛选，需要注意下屏幕的针脚类型eDP分为30针和40针，等下和驱动板要配套。具体怎么挑选一块显示效果好的屏幕，可能需要你自己再去其他地方学习一下了。\n[post url=\u0026ldquo;http://zhihu.com/question/63374877/answer/762539660\u0026rdquo; title=\u0026ldquo;如何知道一台笔记本的屏幕素质？\u0026rdquo; intro=\u0026ldquo;知乎-xyPROBLEM9x\u0026rdquo; cover=\u0026ldquo;https://pic3.zhimg.com/v2-f0b1c72c4f38fea4246768cc3f3c233f_xll.jpg\u0026rdquo; /]\n我这里选的屏幕型号是 LP140QH2-SPB1\n然后根据型号去淘宝搜索购买就行，买的时候多问问店家，因为有的型号不会在商品标题标出来，一定不要买错型号，如果有配套驱动板一起购买了也是很好的。\n2.驱动板 驱动板其实各家都差不多是那几种，轻薄的板子一般是两种接口 mini HDMI 或type c(非一体的，一个供电一个视频传输)。最开始我是买的type c接口的板子，但到手之后用了两天发现屏幕偶尔会抖屏（轻微上下抖动，影响使用）于是退货。在反复确认是否支持2k后，又买了一块mini HDMI接口的（还便宜了些）。\n购买时要注意询问卖家送的驱动板和屏幕的连接线针脚是否对应。\n将驱动板和屏幕相连，此时就可以测试屏幕是否能正常工作了。\n3.外壳 1.定制外壳 可以去淘宝找驱动板+金属外壳一体那种套装直接购买。其实我是推荐这种的，一是方便，二是结实实用。\n2.积木 积木拼外壳也就是图一乐，其实并不实用（放包里容易弯曲变形）\n淘宝搜微积木就能看到很多，基本都是用来拼显示器外壳和键盘的。一般店家也会送外形图纸，也可以自己去网上找图生成像素画。\n","date":"2020-03-01T00:00:00Z","permalink":"https://izumi.pub/PC/lp140qh2-spb1-screen/","title":"从零开始制作一块便携屏幕"},{"content":"0.前言 疫情在家呆着总是想找事做，于是就准备买个机箱来装着玩，配置还是以前那一堆配置。详见下方链接\n[post cid=\u0026ldquo;412\u0026rdquo; cover=\u0026ldquo;https://izumi.pub/usr/uploads/2020/03/3882402064.jpg\u0026rdquo;/]\n新买了一条显卡延长线和一个120的水冷。 ps:为C24准备的电源定制线,在这个机箱竟然也很好的适配.\n1.外观 和c24的对比，我还是更喜欢c24，装机过后c24的内部也更加整洁。\n2.装机 0.拆开全部外壳 1.铜柱 先把中间挡板上的4(主板)+2(显卡延长线)颗铜柱装好。图中两颗短的就是用来固定显卡延长线的。\n2.主板和电源 这时候就可以把主板和电源都固定上去\n3.显卡延长线 延长线是买的185mm双反向的，需要从主板和中间挡板之间穿过，不然长度会不够，然后将其固定。\n4.显卡 这时候直接插上显卡，定制线因为不是照着这个机箱定制的，所以看着偏长了点。\n5.冷排 冷排固定在上方的架子上，注意不要顶到头，要预留一个螺丝的位置。剩下的位置还可以安一个12cm出风风扇。\n6.连线 接下来就是把该连的线连上，最后扣上冷排支架，基本就齐活了。\n3.总结 这款机箱总的来说非常好装，不过明明叫水冷快乐盒，唯一的安装难点却是在扭曲的塞下水冷管，如果用下压风冷（比如axp90）的话，整个机箱装完内部应该也是可以做到十分整洁的。\n","date":"2020-02-29T00:00:00Z","permalink":"https://izumi.pub/PC/itx-water-cooling-box/","title":"ITX主机-水冷快乐盒L-装机"},{"content":"\n1.拆机 1.后盖 拆除后盖6颗螺丝缓慢打开后盖，注意后盖和主机会有wifi信号连接线，拆掉信号线后拿下后盖。\n注：将主板上的1颗螺丝取下后、稍用力即可拿掉信号线\n2.散热风扇 拆掉风扇周围四颗螺丝，风扇和主板之间也有一根连线，稍用力即可拆除。\n3.解除主板和外壳固定 首先拆掉主板上两颗黑大粗的螺丝，然后直接拆除上方两根线。\n注：小的连接线轻点拆，大的那个供电线需要大力出奇迹。\n4.主板和外壳分类 用两个大拇指顶住散热器这里，稍微用力往外推，即可将主板推出去。\n一定注意是要在拔掉主板上那两根线之后再推\n5.更换内存 拆掉白色金属网上的四颗螺丝，划开金属网就能看见内存条了\n将红框中的卡扣分别向上下掰开，内存就会弹出，此时更换上新内存就好了。\n2.装机 1.倒 2.着 3.看 4.回 5.去 赶紧开几个软件爽爽。\n#3.小插曲\n##1.猜猜我为啥要拆掉这个！\n在装内存金属网的时候，螺丝滑到了这个(应该是扬声器吧)下面，然后它里面有磁铁螺丝就抖不出来。。最后只能拆了。\n2.内存没装好 第一次开机内存没装好，只识别了一根，没办法只有又拆了重装一次。\n","date":"2019-08-04T00:00:00Z","permalink":"https://izumi.pub/PC/mac-mini-2018-replace-the-ram/","title":"Mac mini 2018更换内存条全记录"},{"content":"1.前言 首先是机箱外观：\n再放两张官图：\n然后配置如下：\n类型 型号 价格 来源(2019年) CPU i5 9400f 散片 947 淘宝 6-1 主板 华擎B365M ITX/AC 623 淘宝 6-1 散热 ID-COOLING IS-50 99 京东 6-1 内存 金士顿DDR4 3000 16GB RGB灯条 金士顿DDR4 2400 8GB FURY 479\n0 京东 6-1 秒杀\n原来电脑拆下来的 固态 三星PM981 M.2 NVME 512G 450 淘宝 6-1 显卡 耕升 GTX1660Ti 追风 6G 1949 京东 3-12 电源 全汉MS450(SFX 450W) 371 京东 6-1秒杀 定制线 黄色硅胶线 90 7-30 机箱 C24 小方糖 470 闲鱼 风扇 ARCTIC 双滚珠温控8cm(可串联) *4\nARCTIC 双滚珠温控9cm(可串联) 45.9*4\n45.9 京东 6-1 5,743.4 2.装机 0.机箱 先把机箱大卸八块，然后放在一边闲置。\n1.cpu和内存条 打开CPU盖，直接放上CPU再盖上就好。\n内存条插的时候需要用点力，不然没插紧亮不了机。\n2.固态 M.2的固态直接插在M.2接口上用螺丝固定就好。\n3.cpu散热器 涂抹硅脂、不用太多。安装散热器。\n1.改风扇方向 注：风扇方向修改后无法安装后置风扇，所以有需要的话还是另行购买其他散热器\n我的散热器是is 50，之前安装的时候会挡住内存条，这次拆机时发现扇子可以单独拆下改变方向，先取下侧面四颗螺丝，然后自行调整风扇位置使其不遮挡内存。\n2.调整散热器线 在风扇外侧贴上双面胶，将过长的风扇线绕在周围仅留出最后一小段，然后调整好位置将其固定在散热器上，并将线接在CPU_FAN口上。\n4.主板 这个时候的主板就剩下显卡和各种连线了，将其安装在机箱背板上等待后续操作。\n5.机箱风扇 机箱按照底部进风，尾部、顶部出风来装风扇就行了，也可以适当调整为尾部进风。(注意分清哪一块在顶上哪一块在底下)\n6.定制线 于是为了配合外壳，我就搞一套黄色线。\n7.后板和上板 先安上后板和上板，并在主板上插好CPU线和主板线，为以后走线整理出个轮廓。此时注意将上板风扇线整理清楚并插在主板上，同时调整电源延长线。\n注：此时最好只上少量螺丝大概固定一下\n8.电源和前板 这个电源安装有点困难，那条粗粗的延长线怎么摆放都很怪异。最后整理完如图，同时也安装好了前板。\n将前面板的USB3.0线接在主板上，同时将CPU和主板线接在电源上，此时注意线的整洁。\n9.下板 此时将下面板的风扇接在主板上，但注意不要固定下板（注意走线）\n同时可以将显卡线插在电源上\n10.显卡 将后板稍微打开一些，方便放入显卡，显卡插入后将其固定在后板上，并连上电源线。\n11.下板 此时顺势扣上下板就好，注意不要让机箱风扇线碰到显卡风扇。\n3.完成 1.总结 这一次拆完重装收获挺大的，线基本理清了，而且装的速度也快了不少。\n最重要的是拆完装完后额外收获了三颗螺丝。\n接下来就是一些完成图：\n换线前： 换线后： 可以很明显看到机箱空间空出来很多，气流可以流动更快加快散热\n2.不足 不足当然就是风扇旁边那根又黑又硬的电源延长线了，以后有机会在换成其他线藏在散热器下就OK了。\n3.烤鸡 比换线前各个部件都少了5℃左右，除了显卡涨了3℃。\n","date":"2019-08-01T00:00:00Z","permalink":"https://izumi.pub/PC/itx-c24/","title":"itx主机C24小方糖——详细组装记录"},{"content":"一、安装 0.准备工作以及使用manjaro的感受 1.一个电脑（本人使用surface pro 4和一台普通2k屏的台式装过）\n2.一个U盘\n3.镜像文件：https://manjaro.org/download/ 链接给出的是gnome桌面的，也可以自行选择其他的。\n4.写入软件，这里推荐Rufus。网盘下载 , 密码: 1096\n1.写入U盘 插上U盘、打开Rufus、写入镜像。记得要用DD镜像模式\n2.安装 在win10上通过压缩卷的功能分出一块空余的磁盘\n电脑设置U盘启动后插上U盘重启，这里特别讲一下surface的步骤。\n[collapse status=\u0026ldquo;false\u0026rdquo; title=\u0026ldquo;点击查看surface设置U盘启动\u0026rdquo;] 开机时按电源 + 音量+ 关闭TPM、并选择Change configuration、然后选None 然后这里记得把USB Storage排到第一位 插上U盘重启surface就能进入安装Manjaro的界面 [/collapse]\n自己设置时区等，注意driver设为nonfree ，最后选择Boot安装。\n左上角可以选择语言、然后点击启动安装程序\n前面自己设一下就好，这里选择手动分区。注：我对这一块也不是很了解，可以自行搜索怎样分区比较好 首先选择文件系统为FAT32的盘，挂载上**/boot/efi**，注：内容一定要选保留\n如果没有，则自己分100M挂载为 /boot/efi\n选择最开始我们分出来的空闲磁盘，直接挂载为 / 就行。\n这时候下一步直接安装就行\n二、配置 0.防瞎眼 surface会自动设为200%，基本就能正常使用了。\n对于其他分辨率较高的屏幕（比如2K 25寸的）100%太小、200%又太大\n这里推荐一篇，可以照着设置：https://www.jianshu.com/p/ad7452239bc5\n1.源镜像和系统更新 #排列源 并选择一个（本文选择的是清华源）\n1 sudo pacman-mirrors -i -c China -m rank #增加archlinuxcn库和antergos库\n1 echo -e \u0026#34;\\n[archlinuxcn]\\nSigLevel = TrustAll\\nServer = https://mirrors.tuna.tsinghua.edu.cn/archlinuxcn/\\$arch\\n\\n[antergos]\\nSigLevel = TrustAll\\nServer = https://mirrors.tuna.tsinghua.edu.cn/antergos/\\$repo/\\$arch\\n\u0026#34;|sudo tee -a /etc/pacman.conf 1 2 sudo pacman -Syyu\t#升级系统 sudo pacman -S --noconfirm archlinuxcn-keyring antergos-keyring\t#安装archlinuxcn签名钥匙\u0026amp;antergos签名钥匙 2.AUR助手 1 sudo pacman -S --noconfirm yaourt 最后记得去软件商店打开\n3.安装中文输入法 安装与配置 1 2 3 4 5 sudo pacman -S fcitx-im #默认全部安装 sudo pacman -S fcitx-configtool yaourt -S fcitx-sogoupinyin sudo vim ~/.xprofile\t#配置输入法 加上下面几句后保存，然后重启电脑。\n1 2 3 4 5 export LANG=zh_CN.UTF-8 export LC_ALL=zh_CN.UTF-8 export GTK_IM_MODULE=fcitx export QT_IM_MODULE=fcitx export XMODIFIERS=”@im=fcitx” 在输入法配置里加入搜狗就可以用了。\n后记： 1 2 3 4 #如果输入法不能用，可以尝试以下几个步骤 sudo pacman -S --noconfirm firefox-i18n-zh-cn thunderbird-i18n-zh-cn gimp-help-zh_cn man-pages-zh_cn\t#中文汉化 yaourt -S fcitx-qt4\t对于surface这样的高分屏，可以尝试调大输入法字体以防止瞎眼。 三、安装常用软件 manjaro自带有软件商店，不喜欢命令行的可以直接使用软件商店安装软件。 1.OneDrive OneDrive用了多年，文档照片基本都在里面，现在用Linux肯定也要用来至少同步个文档。\n1.安装 软件地址：https://github.com/skilion/onedrive\n依次执行：\n1 2 3 4 5 sudo pacman -S curl sqlite dlang # 安装依赖 git clone https://github.com/skilion/onedrive.git cd onedrive make sudo make install 2.配置 这里的配置是指配置OneDrive需要同步的目录，否则将会将整个OneDrive下载下来。\n1 2 3 mkdir -p ~/.config/onedrive cp ./config ~/.config/onedrive/config vim ~/.config/onedrive/config\t#默认的内容可以不做修改 对于需要同步的文件夹，将其填写sync_list里\n1 sudo vim ~/.config/onedrive/sync_list #文件里每一行代表一个需要同步的文件、文件夹 例如： 其中路径是相对于你的OneDrive根目录的相对路径。\n最后同步分为手动同步和自动同步：\n1 2 3 4 5 # 手动同步 onedrive --resync --verbose # 自动同步---建议直接执行下面两步，以后OneDrive云端有更新会帮你自动下载，本地更新会帮你自动上传 systemctl --user enable onedrive systemctl --user start onedrive 3.授权 在终端执行onedrive，他返回你一个链接，复制进浏览器登录你的账号，成功后将浏览器地址栏的链接输入终端，就算授权成功了。\n最后提供一个参考链接：https://www.moerats.com/archives/740/\n2.WPS 1 2 sudo pacman -S wps-office\t#安装软件 sudo pacman -S ttf-wps-fonts\t#安装缺失字体 对于不能输入中文：\n1 2 3 4 sudo vim /usr/bin/wps #在第一行下面添加： export XMODIFIERS=\u0026#34;@im=fcitx\u0026#34; export QT_IM_MODULE=\u0026#34;fcitx\u0026#34; ","date":"2019-05-23T00:00:00Z","permalink":"https://izumi.pub/Linux/surface-manjaro-windows/","title":"Surface Pro 4安装Linux(Manjaro)、Windows10双系统"},{"content":"1.下载配置TomCat 1.下载 官网下载\n2.配置 windows用户下载exe安装包会比较方便配置，并且需要java环境\n打开一路默认配置就行，中间会选择一下你的jdk路径\n安装完成后的路径大概是这样的：\n3.使用 其中bin里面用于启动Tomcat，webapps用于存放我们的Servlet程序。\n我们打开 bin\\Tomcat7.exe 在浏览器输入http://localhost:8080\n如果能看到Tomcat的网页，就说明安装成功。\n2.编写Servlet程序 1.编写java 1 import javax.servlet.http.HttpServlet; 鼠标选中错误地点，按 Alt+Enter ，选择Add Java EE 6 \u0026hellip;\u0026hellip; 等待下载\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package cn.lzumi; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; @WebServlet(name = \u0026#34;HttpServletTest\u0026#34;, urlPatterns = {\u0026#34;/time\u0026#34;}) public class HttpServletTest extends HttpServlet { @Override public void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException { httpServletResponse.setContentType(\u0026#34;text/html\u0026#34;); PrintWriter writer = httpServletResponse.getWriter(); //展示当前时间 writer.print(\u0026#34;\u0026lt;html\u0026gt;\u0026lt;head\u0026gt;\u0026lt;/head\u0026gt;\u0026lt;body\u0026gt;\u0026#34; + new SimpleDateFormat(\u0026#34;yyyy-MM-dd HH:mm:ss\u0026#34;).format(new Date()) + \u0026#34;\u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt;\u0026#34;); } } 然后编译\n2.将class文件放入Tomcat路径下 在out路径下找到生成的 class文件复制到webapps\\ROOT\\WEB-INF\\classes下\n注意：如果有包名需要将整个路径完整复制过去，如图所示 编辑webapps\\ROOT\\WEB-INF\\web.xml 文件，将其中的metadata-complete属性改为false\n1 metadata-complete=\u0026#34;false\u0026#34; 我们再一次打开 bin\\Tomcat7.exe 在浏览器输入http://localhost:8080/time\n就可以看见返回的时间\n","date":"2019-05-05T00:00:00Z","permalink":"https://izumi.pub/Java/new-servlet/","title":"编写一个入门的Servlet程序"},{"content":"表结构 现有一个列表，存放了3个手机号码： [1311, 1451, 2334] ，输入以下即可得结果\n1 SELECT * FROM user_info where phone in (1311,1451,2334) 现在需求是我们有一个列表，其中存放电话号码，且个数不定，由此查出对应的个人信息。\n代码部分 首先编写xml部分\n1 2 3 4 5 6 7 \u0026lt;!-- 接收一个list，返回一个list\u0026lt;map\u0026gt;--\u0026gt; \u0026lt;select id=\u0026#34;getUserInfoByPhone\u0026#34; parameterType=\u0026#34;list\u0026#34; resultType=\u0026#34;map\u0026#34;\u0026gt; SELECT * FROM user_info where phone in \u0026lt;foreach collection=\u0026#34;list\u0026#34; item=\u0026#34;item\u0026#34; open=\u0026#34;(\u0026#34; separator=\u0026#34;,\u0026#34; close=\u0026#34;)\u0026#34;\u0026gt; #{item} \u0026lt;/foreach\u0026gt; \u0026lt;/select\u0026gt; 将mybatis的xml文件和mapper接口，对应起来。如果对应不起来的话,会报找不到方法的错误\n1 2 3 #application.yml mybatis: mapper-locations: classpath:mapper/**/*.xml 然后在Mapper部分\n1 2 3 4 5 6 7 8 9 10 import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; @Mapper public interface ListInSqlMapper { List\u0026lt;Map\u0026lt;String, Object\u0026gt;\u0026gt; getUserInfoByPhone(@Param(\u0026#34;list\u0026#34;) List\u0026lt;String\u0026gt; phoneNum); } 然后写Controller\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @RestController public class ListInSqlController { @Autowired private ListInSqlMapper listInSqlMapper; @GetMapping(value = \u0026#34;/userbyphone\u0026#34;) public List\u0026lt;Map\u0026lt;String, Object\u0026gt;\u0026gt; getUserInfoByPhone() { //其中为了测试，直接在本地指定几个号码 List\u0026lt;String\u0026gt; phoneNum = new ArrayList\u0026lt;\u0026gt;(); phoneNum.add(\u0026#34;1311\u0026#34;); phoneNum.add(\u0026#34;1451\u0026#34;); phoneNum.add(\u0026#34;2334\u0026#34;); phoneNum.add(\u0026#34;2333\u0026#34;); return listInSqlMapper.getUserInfoByPhone(phoneNum); } } 查询结果 接下来用postman测试一下\n测试成功。\n","date":"2019-05-01T00:00:00Z","permalink":"https://izumi.pub/Spring/spring-mybatis-list/","title":"Spring Boot + Mybatis SQL语句中使用List"},{"content":"1.下载java 1.官网下载\n2.解压 1 2 tar -zxvf jdk-8u211-linux-x64.tar.gz\t#解压 mv jdk1.8.0_211/ /usr/local/\t#将jdk移动到/usr/local下 3.配置环境变量 1 vim /etc/profile 按下i编辑当前页面，在末尾添加(/usr/local/jdk1.8.0_211 需要修改为自己的存放路径)：\n1 2 3 export JAVA_HOME=/usr/local/jdk1.8.0_211 export PATH=.:$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar 在执行下面语句：\n1 2 source /etc/profile #刷新环境变量 java -version #查看java版本 ","date":"2019-04-25T00:00:00Z","permalink":"https://izumi.pub/Linux/linux-java-environment/","title":"Linux配置Java环境变量"}]