ES 机器宕机,最后查到是 dentry 缓存太高
前段时间碰到一个很烦的事:同一个 ES 集群里,4 台物理机几乎同时挂了,3 台自动重启,1 台直接起不来,还得手动介入。 这事最开始看起来很像“机器突然抽风”,但我后面拿到两份 vmcore 去看,发现主线其实很清楚:不是 ES 进程自己崩了,也不是普通的内存不够,而是 dentry 缓存堆得太离谱,回收时又卡太久,最后把内核直接拖进了 panic。 ...
前段时间碰到一个很烦的事:同一个 ES 集群里,4 台物理机几乎同时挂了,3 台自动重启,1 台直接起不来,还得手动介入。 这事最开始看起来很像“机器突然抽风”,但我后面拿到两份 vmcore 去看,发现主线其实很清楚:不是 ES 进程自己崩了,也不是普通的内存不够,而是 dentry 缓存堆得太离谱,回收时又卡太久,最后把内核直接拖进了 panic。 ...
最近遇到一个很典型的云原生问题:业务明明已经完成了 Pod 重建,结果实例变更还是被平台拦住了。继续往下看,发现不是 Operator 逻辑有问题,也不是业务没重启成功,而是 StatefulSet 在 OnDelete 策略下,一个存在很多年的状态收敛问题被 PaaS 的检测逻辑放大了。 ...
我在 macOS 上一直用一条 alias 启动 Chrome,用来绕过公司网络里一些自签证书场景: alias ge='nohup "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --ignore-certificate-errors --ignore-urlfetcher-cert-requests &>/dev/null &' 这条命令以前一直能用,最近却突然开始报错: 系统无法读取您的偏好设置。某些功能可能无法使用,并且对偏好设置所做的更改不会保存。 第一反应很自然:是不是 profile 坏了,或者权限被系统/安全软件动过。实际排下来,根因不在这里。 ...
我前几天折腾了一个自己挺喜欢的小东西:在 macOS 上按一个快捷键,框选一块区域,直接把图里的字识别出来,然后塞进剪贴板。 ...
什么是 Lease? Lease 对象的核心作用是表示某个实体(通常是一个 Pod 或进程)对某项资源或角色的“持有权”,并且这种持有权是有时间限制的。通过定期续约(renew),持有者可以保持其控制权。如果持有者未能续约,租约到期后,其他实体可以接管。 常见的应用场景包括: 领导选举:在分布式系统中,确保只有一个实例(Leader)执行特定任务,其他实例作为 Follower。 资源协调:跟踪和管理资源的临时所有权。 心跳机制:通过续约时间(RenewTime)检测持有者是否仍然活跃。 Kubernetes 内部的一些组件(如 kube-controller-manager 和 kube-scheduler)就使用 Lease 来实现高可用性和领导选举。 Lease 的工作原理 创建租约: 一个进程(如你的代码)创建一个 Lease 对象,并声明自己为持有者(HolderIdentity)。 续约: 持有者需要定期更新 RenewTime,证明自己仍然活跃。通常通过客户端(如 kubectl 或 Go 客户端)调用 Kubernetes API 来更新。 失效与接管: 如果持有者未能及时续约(例如进程崩溃),其他进程可以通过检查 RenewTime 和 LeaseDurationSeconds 判断租约是否过期,并尝试接管。 领导选举: 多个实例竞争同一 Lease 对象时,只有成功创建或更新它的实例成为 Leader。 示例场景 假设你用这个 Lease 来实现领导选举: 你有一个分布式应用,有 3 个 Pod:pod-1、pod-2、pod-3。 它们都尝试创建或更新同一个 Lease 对象(例如 my-leader-lease)。 pod-1 成功创建,设置 HolderIdentity: “pod-1”,成为 Leader。 pod-1 每 10 秒更新 RenewTime,保持领导地位。 如果 pod-1 崩溃,RenewTime 未更新,pod-2 检测到租约过期,接管并更新 HolderIdentity: “pod-2”。 现有逻辑 // Copyright 2018 The Operator-SDK Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "context" "time" "github.com/operator-framework/operator-sdk/pkg/k8sutil" // Operator-SDK 提供的工具包,用于获取运行环境信息(如命名空间、Pod 信息) corev1 "k8s.io/api/core/v1" // Kubernetes Core v1 API,包含 Pod、ConfigMap 等资源定义 apierrors "k8s.io/apimachinery/pkg/api/errors" // Kubernetes API 错误处理包 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // Kubernetes 元数据定义包,包含 ObjectMeta 等 "k8s.io/apimachinery/pkg/util/wait" // 提供等待和退避逻辑的工具 crclient "sigs.k8s.io/controller-runtime/pkg/client" // controller-runtime 提供的通用客户端,用于操作 Kubernetes 资源 "sigs.k8s.io/controller-runtime/pkg/client/config" // 获取 Kubernetes 配置(如 kubeconfig) ) // maxBackoffInterval 定义了尝试成为 Leader 时,最大退避等待时间(16秒)。 // 在多次尝试失败后,等待时间会逐渐增加,但不会超过这个值。 const maxBackoffInterval = time.Second * 16 // leaderBecome 函数确保当前 Pod 成为其命名空间内的 Leader。 // 如果代码运行在集群外(本地),会跳过领导选举直接返回 nil。 // 它通过创建一个带有当前 Pod 作为 OwnerReference 的 ConfigMap 来实现领导选举。 // ConfigMap 的名称是 lockName,同一时间只能有一个同名 ConfigMap 存在,成功创建者即为 Leader。 // 当 Leader Pod 终止时,垃圾回收机制会删除 ConfigMap,其他 Pod 可以接管。 func leaderBecome(ctx context.Context, lockName string) error { log.Info("Trying to become the leader.") // 日志:尝试成为 Leader // 获取当前 Operator 运行的命名空间 ns, err := k8sutil.GetOperatorNamespace() if err != nil { // 如果无法获取命名空间(可能是本地运行或无权限) if err == k8sutil.ErrNoNamespace || err == k8sutil.ErrRunLocal { log.Info("Skipping leader election; not running in a cluster.") // 日志:不在集群中,跳过选举 return nil } return err // 其他错误,返回 } // 获取 Kubernetes 配置(通常来自 kubeconfig 或 ServiceAccount) config, err := config.GetConfig() if err != nil { return err // 配置获取失败,返回错误 } // 创建 controller-runtime 的客户端,用于操作 Kubernetes 资源 client, err := crclient.New(config, crclient.Options{}) if err != nil { return err // 客户端创建失败,返回错误 } // 获取当前 Pod 的 OwnerReference,表示当前 Pod 是谁 owner, err := myOwnerRef(ctx, client, ns) if err != nil { return err // 获取 OwnerReference 失败,返回错误 } // 检查是否已存在由当前 Pod 拥有的 ConfigMap(可能是进程重启的情况) existing := &corev1.ConfigMap{} key := crclient.ObjectKey{Namespace: ns, Name: lockName} // ConfigMap 的键(命名空间+名称) err = client.Get(ctx, key, existing) switch { case err == nil: // ConfigMap 已存在 // 检查现有 ConfigMap 的 OwnerReferences for _, existingOwner := range existing.GetOwnerReferences() { if existingOwner.Name == owner.Name { // 如果 Owner 是当前 Pod log.Info("Found existing lock with my name. I was likely restarted.") // 日志:发现已有锁,可能是重启 log.Info("Continuing as the leader.") // 日志:继续作为 Leader return nil // 直接返回,当前 Pod 已是 Leader } log.Info("Found existing lock", "LockOwner", existingOwner.Name) // 日志:发现其他 Pod 持有的锁 } case apierrors.IsNotFound(err): // ConfigMap 不存在 log.Info("No pre-existing lock was found.") // 日志:没有预先存在的锁 default: // 其他错误 log.Error(err, "Unknown error trying to get ConfigMap") // 日志:获取 ConfigMap 时发生未知错误 return err } // 创建一个新的 ConfigMap,用作领导锁 cm := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: lockName, // ConfigMap 名称,由 lockName 指定 Namespace: ns, // 命名空间 OwnerReferences: []metav1.OwnerReference{*owner}, // 设置当前 Pod 为 Owner }, } // 尝试创建 ConfigMap,成为 Leader backoff := time.Second // 初始退避时间为 1 秒 for { // 无限循环,直到成功或上下文取消 err := client.Create(ctx, cm) // 尝试创建 ConfigMap switch { case err == nil: // 创建成功 log.Info("Became the leader.") // 日志:成功成为 Leader return nil // 返回,当前 Pod 成为 Leader case apierrors.IsAlreadyExists(err): // ConfigMap 已存在(其他 Pod 是 Leader) existingOwners := existing.GetOwnerReferences() // 获取当前 ConfigMap 的 Owner switch { case len(existingOwners) != 1: // ConfigMap 应该只有一个 Owner log.Info("Leader lock configmap must have exactly one owner reference.", "ConfigMap", existing) case existingOwners[0].Kind != "Pod": // Owner 必须是 Pod log.Info("Leader lock configmap owner reference must be a pod.", "OwnerReference", existingOwners[0]) default: // 正常情况:ConfigMap 由一个 Pod 拥有 leaderPod := &corev1.Pod{} key = crclient.ObjectKey{Namespace: ns, Name: existingOwners[0].Name} // 当前 Leader Pod 的键 err = client.Get(ctx, key, leaderPod) // 获取 Leader Pod 信息 switch { case apierrors.IsNotFound(err): // Leader Pod 已删除 log.Info("Leader pod has been deleted, waiting for garbage collection do remove the lock.") // 日志:等待垃圾回收删除 ConfigMap case err != nil: // 获取 Pod 失败 return err case isPodEvicted(*leaderPod) && leaderPod.GetDeletionTimestamp() == nil: // Leader Pod 被驱逐但未标记删除 log.Info("Operator pod with leader lock has been evicted.", "leader", leaderPod.Name) // 日志:Leader 被驱逐 log.Info("Deleting evicted leader.") // 日志:删除被驱逐的 Leader err := client.Delete(ctx, leaderPod) // 删除 Leader Pod if err != nil { log.Error(err, "Leader pod could not be deleted.") // 日志:删除失败 } case leaderPod.GetDeletionTimestamp() != nil: // Leader Pod 已被标记删除(新增逻辑) log.Info("Operator pod with leader lock has been terminating/completed.", "leader", leaderPod.Name) // 日志:Leader Pod 正在终止 log.Info("Deleting a-lock cm.") // 日志:删除 ConfigMap errCm := client.Delete(ctx, cm) // 删除 ConfigMap if errCm != nil { log.Error(errCm, "Leader pod could not be deleted.") // 日志:删除 ConfigMap 失败 } err := client.Delete(ctx, leaderPod) // 删除 Leader Pod if err != nil { log.Error(err, "Leader pod could not be deleted.") // 日志:删除 Pod 失败 } default: // Leader Pod 仍然活跃 log.Info("Not the leader. Waiting.") // 日志:当前不是 Leader,继续等待 } } // 等待一段时间后重试,带有随机抖动(Jitter)避免所有 Follower 同时竞争 select { case <-time.After(wait.Jitter(backoff, .2)): // 等待 backoff 时间(带 20% 随机抖动) if backoff < maxBackoffInterval { // 如果退避时间未达到最大值 backoff *= 2 // 退避时间翻倍 } continue // 继续下一次循环 case <-ctx.Done(): // 上下文取消 return ctx.Err() // 返回上下文错误 } default: // 创建 ConfigMap 时发生其他错误 log.Error(err, "Unknown error creating ConfigMap") // 日志:未知错误 return err } } } // myOwnerRef 返回当前运行 Pod 的 OwnerReference。 // 它依赖环境变量 POD_NAME(通过 downwards API 设置)来识别当前 Pod。 func myOwnerRef(ctx context.Context, client crclient.Client, ns string) (*metav1.OwnerReference, error) { // 获取当前 Pod 的信息 myPod, err := k8sutil.GetPod(ctx, client, ns) if err != nil { return nil, err // 获取失败,返回错误 } // 构造 OwnerReference owner := &metav1.OwnerReference{ APIVersion: "v1", // API 版本 Kind: "Pod", // 资源类型 Name: myPod.ObjectMeta.Name, // Pod 名称 UID: myPod.ObjectMeta.UID, // Pod 的唯一标识符 } return owner, nil } // isPodEvicted 检查 Pod 是否被驱逐(Evicted)。 // Pod 被驱逐时,Phase 为 Failed,且 Reason 为 "Evicted"。 func isPodEvicted(pod corev1.Pod) bool { podFailed := pod.Status.Phase == corev1.PodFailed // Pod 状态为 Failed podEvicted := pod.Status.Reason == "Evicted" // 原因是 Evicted return podFailed && podEvicted // 两者都满足则为被驱逐 } 现有逻辑总结 目标: 通过创建一个 ConfigMap(名称为 a-lock)来实现领导选举,成功创建者成为 Leader。 ConfigMap 的 OwnerReference 指向当前 Pod,当 Pod 删除时,ConfigMap 会被垃圾回收。 流程: 检查环境:如果不在集群中运行,跳过领导选举。 检查现有锁:如果已有由当前 Pod 拥有的 ConfigMap,直接恢复为 Leader。 创建锁:尝试创建新的 ConfigMap,如果失败(已有其他 Leader),检查当前 Leader 状态。 处理失效: 如果 Leader Pod 已删除,等待垃圾回收。 如果 Leader Pod 被驱逐或标记删除,主动删除 Pod 和 ConfigMap。 否则,等待并重试(带有退避机制)。 与 Lease 的对比 相似点:都用于领导选举,依赖 Kubernetes API 的原子性操作。 不同点: Lease 是专门为领导选举设计的轻量资源,内置续约机制(RenewTime)。 ConfigMap 是通用资源,通过 OwnerReference 和垃圾回收间接实现接管,逻辑更复杂。 请求开销: Lease 需要定期续约和查询。 此代码使用轮询和退避,Follower 需反复尝试创建 ConfigMap,也会产生多次请求。 为什么基于 ConfigMap 的方案在主机房断电后表现不佳?(另一个 Operator 没能及时成为 Leader) 1. ConfigMap 依赖垃圾回收机制 机制: 在现有代码中,ConfigMap 的 OwnerReference 指向 Leader Pod。当 Leader Pod 被删除时,Kubernetes 的垃圾回收器(Garbage Collector, GC)会自动删除 ConfigMap,从而允许新的 Pod 成为 Leader。 主机房断电的影响: 如果主机房断电,运行 Leader Pod 的节点会突然下线。此时,Kubernetes 集群需要检测到节点不可用,并将该节点上的 Pod 标记为删除(DeletionTimestamp)。 问题:节点失联后,Kubernetes 不会立即认为 Pod 已删除,而是依赖 Node Controller 和 Taint-based Eviction 等机制来更新状态,这个过程可能需要几分钟甚至更久。 延迟来源: Node NotReady 检测:Master 检测节点失联通常依赖 node.kubernetes.io/not-ready 和 node.kubernetes.io/unreachable 污点,默认超时(node-monitor-grace-period)是 40 秒。 Pod Eviction 超时:Pod 被驱逐的默认超时(eviction-hard 或 eviction-soft)可能配置为几分钟(常见默认值如 5 分钟)。 垃圾回收延迟:即使 Pod 被标记删除,GC 删除 ConfigMap 也有一定延迟(通常几秒到几分钟,取决于集群负载和 GC 调度)。 2. 代码中的轮询和退避逻辑 机制: 现有代码使用轮询方式尝试创建 ConfigMap,如果失败(IsAlreadyExists),会检查现有 Leader Pod 状态,并以指数退避(backoff)等待。 退避时间从 1 秒开始,每次翻倍,直到最大值 maxBackoffInterval = 16 秒。 主机房断电的影响: 当 Leader Pod 的节点断电后,Follower Pod 检测到 ConfigMap 仍存在,但无法立即接管。它会进入等待循环,每次等待时间逐渐增加(1s → 2s → 4s → 8s → 16s)。 问题:如果 GC 迟迟未删除 ConfigMap,Follower 的等待时间会累积。例如,等待 5 次退避的总时间是 1 + 2 + 4 + 8 + 16 = 31 秒,但如果需要更多次(因为 GC 延迟),时间会更长。 延迟来源: 退避累积:退避逻辑本身会拖慢接管速度。 未优化检测:代码依赖主动轮询,而非实时监听(Watch),无法立即感知 ConfigMap 删除。 3. Kubernetes 集群的容错机制 机制: Kubernetes 在设计时倾向于“谨慎”处理节点失联,避免误判(false positive)。这意味着在确认节点和 Pod 真正失效前,会有较长的宽限期。 主机房断电的影响: 如果整个主机房断电,多个节点可能同时失联,Master 的 Node Controller 和 API Server 负载会激增,导致状态更新变慢。 问题:Pod 和 ConfigMap 的状态更新可能被推迟,进一步延长新 Leader 上任时间。 延迟来源: Node Controller 过载:大规模节点失效可能导致状态同步延迟。 API Server 压力:大量资源状态变更可能使 API 调用变慢。 4. 代码中的改进逻辑(DeletionTimestamp) 机制: 现有代码新增了检查 leaderPod.GetDeletionTimestamp() != nil,当 Leader Pod 被标记删除时,主动删除 ConfigMap 和 Pod。 主机房断电的影响: 这个逻辑依赖 Pod 被标记删除,但断电后 Pod 状态更新依赖 Node Controller,可能需要几分钟。 问题:如果节点失联时间过长,DeletionTimestamp 不会立即设置,主动删除 ConfigMap 的逻辑无法触发。 延迟来源: 状态更新延迟:节点失联到 Pod 被标记删除的时间(默认可能 5-10 分钟)。 断电场景:15 分钟的延迟虽然不常见,但并非完全不合理,尤其在以下条件下: Node Eviction 超时较长:如果集群配置了较长的宽限期(如 kube-controller-manager 的 --pod-eviction-timeout=5m 或更高),Pod 状态更新可能延迟 5-10 分钟。 GC 延迟:大规模断电后,GC 处理大量资源,调度延迟可能累加到几分钟。 退避累积:Follower 的退避逻辑在极端情况下可能执行多次(比如 10-20 次),每次 16 秒,总计 160-320 秒(约 3-5 分钟)。 集群负载:如果 Master 或 API Server 因断电事件过载,状态同步可能进一步推迟。 估算: Node 失联检测:5 分钟(默认宽限期)。 Pod 驱逐 + GC:2-5 分钟。 Follower 退避:3-5 分钟。 与 Lease 方案的对比 Lease 的优势: 主动续约:Leader 定期更新 RenewTime,Follower 可通过时间戳判断租约是否过期,无需依赖 GC。 更快接管:租约过期后(通常 15-30 秒),Follower 可立即尝试接管。 典型延迟:在断电场景下,接管时间通常在几十秒到 1-2 分钟(取决于 LeaseDurationSeconds 和网络恢复)。 现有 ConfigMap 方案: 接管时间受限于 Node 状态更新和 GC,异常场景下可能高达数十分钟。 Lease 资源在 Kubernetes 中的版本局限性分析 背景 Kubernetes 中的 Lease 资源(隶属 coordination.k8s.io API 组)用于节点心跳和领导选举等协调机制,是一个不算核心但也不可忽视的功能。然而,官方文档(https://kubernetes.io/docs/reference/)对 Lease 的版本演进记录极为有限: ...
Mac生产力软件推荐:提升效率的实用工具 Mac 系统以其流畅和优雅著称,但搭配一些生产力工具,能让你的工作效率更上一层楼。下面推荐几款精心挑选的软件,涵盖截图、窗口管理、系统优化等场景,助你充分发挥 Mac 的潜力。 Snipaste:高效截图与贴图利器 Snipaste 是一款将截图与贴图功能完美结合的工具。它能快速捕捉屏幕内容,并将截图固定在桌面,方便随时参考或对比,提升工作效率。 核心亮点:像素级精确截图、丰富的标注功能、可悬浮的贴图模式。 适用场景:设计师对比设计稿、开发者调试界面、内容创作者整理素材。 下载地址:Snipaste 官网 BetterAndBetter:操作效率的增强器 BetterAndBetter 是一款轻量化的 macOS 操作优化工具,类似 BetterTouchTool,但更简洁。它支持自定义手势和快捷键,让你的鼠标和触控板操作更得心应手。 核心亮点:鼠标手势、触控板快捷操作、窗口管理功能。 适用场景:从 Windows 转换到 Mac 的用户,或追求个性化操作体验的人。 下载地址:BetterAndBetter 官网 Rectangle:窗口管理的免费助手 Rectangle 是一款开源的窗口管理工具,通过快捷键快速调整窗口大小和位置,让多任务处理变得井然有序。 核心亮点:多样化的窗口布局、灵活的快捷键设置。 适用场景:程序员同时查看代码和文档、设计师处理多屏设计。 下载地址:Rectangle GitHub BetterDisplay:外接显示器的最佳伴侣 BetterDisplay 专注于优化显示器设置,解锁 macOS 的隐藏选项,提供更高的分辨率和刷新率,显著改善多屏工作体验。 核心亮点:支持 HiDPI 分辨率、HDR 模式、多显示器管理。 适用场景:视频编辑师和设计师使用外接显示器时的画面优化。 下载地址:BetterDisplay 官网 AltTab:程序切换的顺畅体验 AltTab 模仿 Windows 的“Alt+Tab”切换功能,为 macOS 用户提供更直观的应用切换方式,效率大幅提升。 核心亮点:全屏窗口预览、支持自定义快捷键。 适用场景:Windows 转 Mac 用户,或需要频繁切换应用的人。 下载地址:AltTab 官网 OwlOCR:图片文字提取神器 OwlOCR 是一款高效的 OCR 工具,能快速识别图片或屏幕中的文字,准确率高且操作简单。 核心亮点:支持多语言识别、批量处理功能。 适用场景:学生整理笔记、研究人员提取文档内容。 下载地址:OwlOCR 官网 Maccy:剪贴板管理的得力助手 Maccy 是一款轻量剪贴板工具,能保存复制历史并支持快速调用,极大方便频繁复制粘贴的用户。 核心亮点:支持多种格式(文本、图片等)、历史记录搜索。 适用场景:程序员处理代码片段、文案编辑整理素材。 下载地址:Maccy GitHub Mos:鼠标滚动的优化专家 Mos 专为第三方鼠标设计,提供平滑的滚动体验,并允许用户自定义滚动方向和速度。 ...
收到工单 用户提工单到 L2 说写入消息堆积了,第一时间检查 Elasticsearch 这边的日志和监控,把一百多个节点的日志都遍历看了一遍,并没有看到任何异常的关键字。再把 Grafana 监控看了一个遍,也没有任何明显异常的指标,只有少数几个 data 节点超出 85% 水位。没办法,只能从写入端的日志去着手了。 刚开始就看到每分钟内多次的 HTTP 429 报错,还有 socket timeout 的报错。从下午排查到晚上,实在 Elasticsearch 这边没有任何异常。客户那边也上会了,问了下:“你们这个重试是怎样的?” 回答是等待十秒重试一次。另外,客户是通过 Bulk API 来批量发送请求的,但是每次 Bulk 发送的大小不固定:小于 2000 直接发送,大于 2000 截断发送(这个肯定是不合理的,后面再讲)。 这个等待重试策略,如果 Elasticsearch 因为写入请求太多导致阻塞,那么简单的等待重试肯定是解决不了任何问题的。当天就推荐用户修改为退避式重试。修改完后的写入端日志里,不再有 429 的报错了,但还是会有 socket timeout 的报错。 结合之前注意到的情况,猜想是请求发送的次数太多了。然而单个 Bulk 的数据量明显不到推荐值,没有被充分利用。官方推荐的 Bulk 大小是 5–15 MB,而目前的情况是,几十条甚至一两条的写入请求也会被直接发送出去。 推荐用户修改 Bulk 单次提交数据量后,仍然没有任何缓解效果,消息队列消费不完,还是经常性出现堆积。 data 节点的配置是 16C64G,检查了写入线程数也就是默认是 16,这个没法改,最大也就改到 17。写入线程队列是 6.x Elasticsearch 的默认值 200,这个改大也不能解决消费慢的问题,只能让更多的线程在排队。 其他方面,不同索引的刷新配置 refresh_interval 设置的 30–60 s,这也是一个合理的值。translog 落盘配置检查过也都是默认值。唯一一处疑点就是 mapping 的 max_field 设置的是 10000,max_depth 设置的是 20,这个不是正常值,但也不会导致写入慢吧?用户反映他们的 mapping 并不复杂,也不存在 20 层嵌套。逻辑上分析也没啥问题——我定义一个瓶子最大能装 1 吨水,也不是说我的瓶子真的要装 1 吨水。 硬着头皮 按理说,云厂商提供云产品服务,只提供服务和运维,调优能调的肯定在售卖给客户时已经是最优配置,不可能让用户买到的实例再去关闭 swap、调最大文件句柄这种东西。 ...
2024年开始,我一直在支付宝上买纳斯达克和标普的指数基金。另外,由于上次讲到的,X 被我养成了,所以经常能看到很多牛人在谈论美股,我也开始了解了一些。之前也看有人在玩美股,据说最简单的办法就是办一张港卡,所以就有了这次旅行。 不得不说,在查攻略这方面还是挺不错的。开始申请港澳通行证办理签注,然后花费两天年假,提前去广东深圳,带上爸妈一起去旅游,虽然对我来说不仅仅是旅游。 办理通行证各个地方各个省份的入口不一样,但流程大多相同:在网上预约,然后去线下填表、拍照、录指纹,之后会有工作人员问你去干什么。至于签注,可以根据自己的需要选择不同的类型。另外,团队签和个人签其实没什么区别,不用太在意。 到达深圳以后,在酒店休息一晚。考虑如何去到香港,可以选择轮渡、高铁或公交,我选择的是高铁——速度最快,通关最便捷。从深圳北站前往香港西九龙站,只需要不到 20 分钟,人民币 ¥75。我没有体验普通的公交和口岸的过关方式,据说排队的人会很多,通关耗时更久。像坐普通的高铁一样,从深圳北出发,到达香港西九龙入关,然后你录入的指纹就可以发挥作用了。按下指纹后,校验通过会给你一张过关小票,这个小票一定要留好,后面办理港卡的时候会用到。 从西九龙站出来就能看到维多利亚港,对面的楼认识的不多,但牛逼就完事了。我是六点多从深圳出发,在香港的交通方面可以直接使用支付宝,需要提前在支付宝的左上角切换地址为香港,然后领取交通卡。大部分的公交地铁都可以直接刷码,只在三天的旅途中遇到了一辆司机说“请去下一辆,不支持支付宝”。乘坐地铁前往葵芳,从地铁站出来大概走不到 500 米就到达汇丰葵芳分行。因为老人出站时刷了码但反应慢了没出来,联系站务人员花费了一些时间。到达时已经 8:30 多了,这时候银行门口已经排满了人,大概有 20 多人。本着“既然来了”的原则,只能开始排队了。9:00 准时开门,这里的银行不像内地那么气派,面积不大,也没有前台,只有门口一个业务员问你要办理什么业务,然后他帮你取对应的号码。他讲粤语,我回答普通话:“办卡。”他没有回答,直接给了我一张号码:A17。等待的人比较多,座位比较少,老年人也比较多。 ...
用字符画描述git的合并方法 1. 合并(Merge) main 分支 A---B---C \ dev 分支 D---E---F 在 main 分支上执行 `git merge dev` 后: main 分支 A---B---C-----------G <- (G 是合并提交) \ / dev 分支 D---E---F 2. 变基(Rebase) main 分支 A---B---C \ dev 分支 D---E---F 在 dev 分支上执行 `git rebase main` 后,再在 main 分支上执行 `git merge dev`: main 分支 A---B---C---D'---E'---F' 3. 压缩合并(Squash and Merge) main 分支 A---B---C \ dev 分支 D---E---F 在 main 分支上执行 `git merge --squash dev` 后,再执行提交: main 分支 A---B---C-----------G' <- (G' 是一个新的提交,合并来自 D、E 和 F 的变更) 4. 拣选提交(Cherry-pick)(不是标准合并所有更改的方法) main 分支 A---B---C \ dev 分支 D---E---F 在 main 分支上对 dev 分支的每个提交执行 `git cherry-pick <提交哈希值>`: main 分支 A---B---C---D'---E'---F' <- (每个提交被逐一复制) 合并方法的对比表格 方法 特点 适用场景 结果 Merge 保留所有分支的历史 标准的合并操作,适用于大多数场景 创建一个新的合并提交 Rebase 使历史线性化 个人分支上的工作或清洁历史 不保留原始分支提交的顺序 Squash and Merge 将所有更改压缩为一个提交 当你想要简化复杂分支的历史时 一个新的单一提交 Cherry-pick 手动选择特定提交 只合并某些特定的更改 对选择的每个提交创建新的提交 请注意,这些方法中,Merge 和 Rebase 是最常用的策略来整合一个开发分支(如 dev)到主分支(如 main)。Squash and Merge 通常用于在合并之前压缩多个提交以保持清洁的历史。Cherry-pick 方法并不是一个真正的分支合并策略,它只是用于将选定的提交从一个分支复制到另一个分支,因此它不应用于将一个完整的功能分支合并到主分支的场景。
好久没有更新了,借此机会更新一下。老早就看了天气预报,说今天会下雨,于是就做好了不出门的准备,一觉睡到十点多。一看窗外也没有下雨啊,但还是照计划行事吧,开启懒狗模式,饿了点了个外卖。 想起几天前就是 315,又爆了什么猛料呢?梅菜扣肉、某奶茶……触目惊心。可是仔细一想,央视那些记者真是习得东亚小岛传统技能忍者术啊,那叫一个能忍,暗中调查一年,然后在第二年的 315 憋个大招?已经不知道用什么语言来表达了。 说回点外卖,自从去年在北京住青旅找工作的那段时间,那时候在蓝鸟上看到一句话说“此时北上广的青旅里,挤满了找工作的年轻人。”我已经很少很少点外卖了。这次也是懒狗模式的缘故,美团送了个 30-10 的券,点了个杨国福麻辣烫。上一次点外卖也是这家,而且内容也都类似(千张、莴苣片、油豆皮、青菜……),我从不或者很少选肉、肉丸子,因为自从我听了一句话叫做“好肉不做馅”,就一直把这句话放在心里。 外卖到了,收到外卖员的电话了,打电话说外卖到了,我说好,出门去拿。打开门,他在门口,是一个看起来三十多不到四十的中年人。按照我点外卖的经历,有时候你接到电话,他其实才到楼下;等你想起来再去拿,外卖已经在门口,人早就走了。要么就是你收到电话时,外卖已经给你扔门口了,他也走远了。但今天不一样,他就在门口站着。我打开门接过外卖,说了声谢谢,就回屋了。其实到此为止,也不至于让我今天下午想这么多东西,以至于想写点东西。 下午,闲得蛋疼并不是卷的我,出门骑车去公司的路上,我收到一个电话。我一看是浙江杭州的号码,接过来,对面说:“大哥好,我是给你送麻辣烫的那个外卖员,麻烦您能给我一个五星好评吗?帮我过一下新手的任务,祝大哥今年发大财日进斗金。”我说好。其实我知道,当他说是过新手任务的时候,我就会决定帮他这个忙了。后面那句直接让我楞了一秒,我才憋出一句:“你也一样。” 虽然平时工作中,和外部一些初入职场的年轻人对接时,他们也会用一些谦辞——“大佬、老师、大哥、老哥”——但是这次不一样。他曾经就站在我面前,一个三四十的中年人,为了生计,为了通过美团外卖员的新人任务,把我一个二十多岁的年轻人叫大哥。他可能是某个小孩的爸爸,可能是某个女人的丈夫,可能是别人的儿子。 ...