阅读视图

发现新文章,点击刷新页面。

黑屏之后

在小米Civi 1S又出现了在解锁的时候黑屏,必须插上充电头才能重启之后,我又去研究了一番之前我已经研究过,但是隔了好长一段时间都没有结果的事情。那时我觉得已经等不到结果了,或许有一天,我希望HyperOS那边会说,已经修复的那个指纹解锁不知道为什么就会出现黑屏的问题。但实际上,我感觉这个东西从来都没有出现过,因为谁都没办法重现那个黑屏,当你拿着手机去检查,最后出来的结果就是什么问题都查不到,但就是会随机黑屏。通常情况下,维修的人会给你刷一个系统,然后完事,但说不准那个系统什么时候又挂了。我遇到的情况是根本就没有等到HyperOS那边说有修复的补丁。出现指纹解锁的时候出现黑屏,这个状态不仅仅是小米Civi 1S的专属 ,红米K系列或者小米数字系列也有很多同样的遭遇。

这一次当我又去查找黑屏这个问题的时候。发现不久前在小米社区上。小米13Ultra的用户很多都遇到了这个问题,最后都必须重启才能解决。有些人已经把这个重启当作家常便饭,不仅仅是小米13Ultra,小米15,刚刚出的新机也有这种问题。一开始我以为那只是大家在抖音上抹黑小米15所以才这么说的,后来发现,在小米社区上也有很多,如果他们觉得这是抹黑的话,估计管理员会主动删帖,实际上没有。小米15的黑屏更加无语,那是任何场景都可能触发。小米13Ultra可能不插充电线也能长按电源键重启,但小米15就只能先插上充电线,然后才能重启。我不确定小米Civi 1S是不是长按电源键时间足够长了就能重启。以前当我的指纹解锁导致黑屏以后,一开始我是用插充电线重启的,后来某一次我发现,只要长按时间足够长,也可以不插充电线重启。所以这些黑屏到底是什么情况呢?为什么手机的某个核心部件突然就挂掉了?怎么那么脆弱呢?我自己用过7台小米或者红米的手机或平板。除了小米Civi 1S出现黑屏这个状况以外,其它从开始使用到寿终正寝都不会出现在解锁的时候突然黑屏,又或者用着用着就黑屏。红米Note7的确在最后的时候会用着用着就自动关机了,那是因为电池跟主板都已经不行了,不是现在我所遇到的黑屏问题,现在遇到的黑屏问题是手机重启之后发现电池是正常的,手机也没有问题,但你就说不准那个黑屏什么时候会来。

再次遇到黑屏这个问题的时候。我的考虑要不要把我的备用手机跟主用主力手机换一下,把红米Note11 5G换成我的主力手机,那个机子跟小米Civi 1S相比,的确性能不足,但稳定,而且电池的续航也比较好,因为它的CPU很一般,因为它的天线很阉割。慢是会慢一点,但起码不用我每天都为这个黑屏问题担惊受怕。但我真的要放弃小米Civi 1S作为主力手机吗?其实除了这个以外,我还有第二个选择,就是直接换一台新机,然后用换机的方式把所有资料都从小米Civi 1S上挪到新机。小米Civi 1S一旦屏幕点亮,就不会有黑屏的问题,所以哪怕给他们去检查,也不可能检查得出有毛病,所以那台机子理论上如果以旧换新,能换个好价钱,但是我真的要以这种方式告别小米Civi 1S吗?最重要的是,现在的小米手机里,我还没有一款特别心仪的。

说起黑屏问题,是不是都做过小米换机这个操作呢?所以是不是在换机的时候带进了某些信息导致跟系统本身不匹配,所以出现黑屏呢?

小米15黑屏的用户有的说可能是自动光亮度导致的bug,所以我直接关掉了小米Civi 1S的自动光亮度调节。我不知道这个能挺多久,我不知道下一次黑屏会什么时候袭来。

一波未停一波又起

周五晚上赶公交回家的时候急着走路甚至跑的时候,完全感觉不到腰痛,但是当我在地铁坐下之后,那种作死的感觉又慢慢地涌上来,幸好从长椅上起来出站的时候并没有太糟糕。家里没有很便宜的那些膏药。我妈上次腰痛的时候,回他们的医疗室的确开了一些,但我觉得那些太贵了,所以没有贴。周六出门的时候路过大参林药房,进去买了两小包,一共20贴,才3.7块钱,其中一款特价的恒健才9毛钱。先开始用的是广药白云山的那款。之前在宿舍翻出两包药膏小药膏贴,一看日期,发现已经过期4年,但我还是贴了其中一块,结果贴了不到半天我就感觉痒了,所以不得不撕掉。新买的那个白云山的药膏,贴一整天都基本上没有不适感,所以有效期以内与否以及膏药是什么牌子还是有点讲究的。新的膏药贴一整天撕下来的时候,手上还会闻出一点味道,但已经过期4年的那个,虽然那个粘贴功能还没有失效,但是可以这么说,几乎没什么药力可言了。

周六出门,穿了条牛仔裤,春节的时候我也穿那一条,感觉还行,但现在再穿那条裤子,就莫名的觉得紧绷。紧绷不知道为什么又会引起一些说不准为什么的腰痛,所以是因为腰痛人体僵硬,导致紧绷,还是反过来呢?我个人感觉,腰痛好像一天比一天好。起码我坐在床上看一部接近两个小时电影的之后才活动,也没有感觉转换姿势的时候非常作死,虽然痛还是会有的。

当我以为一切都在向好的方向发展的时候,周一早上跟往常一样,闹钟响起,我把闹钟要按掉,准备做支付宝的任务,接着发现我的小米Civi 1S屏幕点亮不了了。那个时候我很慌,才周一,我还有5个工作日需要扛,没有手机,我该怎么办?虽然我也不能说没有手机,因为我还有个备用机,但是备用机上没有微信。我的主力机跟备用机最大的区别就是微信。因为其它APP还能装上去。我确认这个黑屏不是因为手机坏了,因为当我短按长按电源键的时候,我能感受到手机的震动,而且如果手机是坏的,闹钟怎么会响起呢?这种屏幕点亮不了,立刻让我联想到之前小米Civi 1S, 在某次升级HyperOS之后就出现了息屏状态下,没有点亮屏幕之前就使用指纹解锁,非常有可能会让手机进入黑屏。一开始频率还比较低,但后来发生频率越来越高。我知道有这么回事,所以我已经跟自己说,一定要把屏幕点亮了再去解锁,但有时还是控制不住,虽然那种黑屏的事件不会每次都发生,但可以肯定的是,你越害怕的事情就越容易发生。我没办法每一次都控制住自己点亮屏幕再解锁,所以在某个中午,我自己搭公交车离开单位回家的时候,我选择了关闭手机的屏下指纹解锁,让一台明明可以屏幕解锁的手机变成一台只能用密码或者图案解锁的手机。就这样我扛了一年。一直以来都相安无事,直到大概两天前,我又更新了一个HyperOS。这一次的更新是一个安卓补丁。在装完这个补丁之后,又发生了这种事情,而这一次,不是因为解指纹解锁,只是因为闹钟响起的时候我按了一下电源键而已。闹钟响起的时候进行这种操作是再正常不过的。发生这种事以后,我肯定不能再睡了,长按电源键好像没反应,所以我只能爬起来,找出充电器,插上手机,然后长按开关,手机重启后点亮了。但我不确定什么时候我按电源键的时候又会出现那种问题。如果出现这种问题,直接长按重启就能解决,也都还好,但现在的问题是好像在那种情况下,我不插上充电线就不能重启。

我觉得我已经很让步了,但是小米Civi 1S这个解除锁屏就出现黑屏的问题,好像老是不放过我。

又搬办公室了

周一的早上,上班没多久就被告知这周之内就得搬办公室,因为下周3楼就有施工队进场开始装修。这一次搬走,我感觉再搬回去估计得2026年,最早也得2025年最后一个季度,但因为领导的办公室也在3楼,所以他们肯定宁愿在其他地方多呆,也不会提前到新的办公室里吸甲醛。

2024年最后一个季度,我就已经把可以挪走的凭证资料全部都挪走了,剩下的那些就是办公室里面的东西还有一些我不打算挪走的资料。上午说要搬办公室,下午就告诉我,要把大件的都先搬走,这就让人很崩溃了,虽然实际上哪怕他们不说,我也会把那些这几天都用不着的大柜子先搬走。问题只是办公室提出下午要搬大件的是因为他们找了外协单位过来。之所以外协单位可以过来,因为刚好单位没有什么太多现场业务。如果现场卸车卸船都很多,外协单位就不可能派那么多人过来帮忙。外协单位不仅人多,小拖车也多。如果不是外协单位过来帮忙,估计办公室会叫保安过来,但是相对于保安来说,外协单位干那些力气活可能更在行。

新的那个办公室下午我就去整某个柜子的锁,因为上午发现那个锁根本锁不上,锁不上也就算了,卡在那里开不了也关不了,这个就很被动。那些东西明明就只是用螺丝搞起来,但关键是一些螺丝螺母到最后的时刻居然拆不下来,这就让人很崩溃,所以下午我拿了尖嘴钳、螺丝刀上去对付它们。在对付他们的同时,因为上午我把新的那个办公室门锁用不了的问题反映给了办公室,所以下午他们就派人去整那个门锁。那个门锁坏得很彻底,首先是有舌弹出的那个地方那个舌根本弹不出来,其次就是锁门的那个位置无论如何也扭不出来,所以简单来说,有门等于没有门,那个门完全是废掉的。换个锁芯我还是会的,但是舌弹不出来,锁的那个东西也弹不出来,这个玩意我就不会修了,只能等专业人来干专业的事情。换完门锁之后,顺便就叫办公室把那些灯光管全部都换掉。因为肉眼可见那都是非常经典的光管了,说不准什么时候就会坏。如果坏的那些光管,不在人坐的那个上方也就算了,因为换也比较容易,自己也能完成,但如果坏的光管在工位上方,换起来就比较麻烦,主要是拆光管掉下来的那些脏东西你还得收拾一番。

专业的人换光管速度果然不一样。接下来我们就是把所有东西都从旧的办公室搬到新的那里去,虽然实际上一开始我们根本没有这个打算,我们只是想把柜子搬上去,但结果我们所有桌子、椅子,还有其它杂七杂八的东西,一气呵成,全部都搬上去了。当我们忙碌了一番以后发现原来最先完成搬办公室的居然是我们。办公室那边也在搬,但好像他们的东西搬来搬去都没什么很大的进度。我们不一样,到下班为止,基本上所有东西都已经搬好了到位了,从外观上看,新的办公室复刻了旧的办公室。唯一剩下的就只是搞卫生,以及等待其它科室把我们的网络开通。

折腾了整整一个下午,接近三个小时,但总算把这件事情做完了。

谈谈分布式锁

不要使用分布式锁

就像 Martin Fowler 说的那样,“分布式调用的第一原则就是不要分布式”,谈分布式锁也要先说,不要使用分布式锁。原因很简单,分布式系统是软件系统中复杂的一种形式,而分布式锁是分布式系统中复杂的一种形式,没有必要的复杂性就不要引入。

有的逻辑是没有副作用的(纯函数代码),那就可以无锁执行;有的数据经过合理的 sharding 之后,可以使用单线程(单节点)执行,那就单线程执行。

比如一种常见的模式就是使用 queue(比如 Kafka),任务全部放到队列中,然后根据 sharding 的逻辑,不同的 consumer 来处理不同的任务,互相之间不会干扰冲突。

还有一个例子是 Kotlin Coroutine,通过指定一个单线程的 dispatcher,也可以保证它执行的操作之间互相不会有多线程的冲突问题。

有了这样的原则以后,再来谈谈几种分布式锁。

数据库锁

分布式系统中,我觉得我们最常见的锁就是使用一个中心数据库来做的。

一种是悲观锁,就是 “select xxx … for update” 这样的,相应的数据行会被锁上,直到 commit/rollback 操作发生。如果被别人锁了,当前线程没得到锁的话就会等着。

还有一种是乐观锁,就是使用版本号,“update … where … version=A” 这样的。如果 update 成功,表示获取锁成功,并且操作也成功;否则就是 update 失败,需要重新获取状态再来操作一遍。

大多数情况下,后者要更高效一些,因为阻塞的时间通常更短,不过在锁竞争比较激烈的情况下,反而效率会反过来。另外一个,悲观锁代码写起来会容易一些,因为 select 语句执行和 commit/rollback 是两步操作,因此二者之间可以放置任意逻辑;而乐观锁则是需要把数据的写操作和 version 的比较放在一条语句里面。

这两种都很常见,基本上我接触过的一半以上的项目都用过两者。这个数据库不一定非得是关系数据库,但是强一致性必须是保证的。

S3

使用 S3 来创建文件,让创建成功的节点得到锁,文件里面也可以放自定义的内容。我们去年的项目用到这个机制。这种方式是建立在 S3 2020 年 12 月 1 日,上线的 strong consistency 的 feature

大致上,有这样两种思路:

  1. 使用 S3 versioning,就是说,在 versioning 打开的情况下,文件的写入不会有 “覆盖” 的情况发生,所有内容都会保留。在创建文件的时候,response 种会有一个 x-amz-version-id header。节点写入文件后,再 list 一下所有的 version,默认这些 version 会根据创建的时间顺序递减排列,后创建的在前,因此比较其中最早的那个 version 和自己创建文件后得到的 version,如果两者相等,说明自己得到了锁。
  2. 使用 S3 Object Lock,这个可以控制让第一次写成功,后面的操作全部失败,所以第一次写入成功的节点得到锁。

使用这种方式,对于那些本来就需要使用 S3 文件系统来共享任意信息的情况很方便,但是需要自己处理超时的问题,还有 retention 策略(该不该/什么时候删掉文件)。

Redlock

Redlock 就是 Redis 的锁机制。Martin Kleppmann(就是那个写《Design Data-Intensive Applications》的作者)几年前写过一篇文章,来吐槽 Redlock 在几种情况下是有问题的:

  1. Clock jump:Redlock 依赖于物理时钟,而物理时钟有可能会跳(jump),并且这种状况是无法预测的。Clock jump 就是说,始终会不断进行同步,而同步回来的时间,是有可能不等于当前时间的,那么系统就会设置当前时间到这个新同步回来的时间。在这种情况下,依赖于物理时间的锁逻辑(比如超时的判断等等)就是不正确的。
  2. Process pause:得到锁的节点,它的运行是有可能被阻塞的。比如 GC,下面这个图说的就是这个情况——client 1 一开始得到锁了,执行过程中有一个超长时间的 pause,这个 pause 导致锁超时并被强制释放,client 2 就得到锁了,之后 client 1 GC 结束,缓过来后恢复执行,它却并没有意识到,它的锁已经被剥夺了,于是 client 1 和 client 2 都得到了锁,对于数据的修改就会发生冲突。
  3. Network delay:其实原理和上面差不多,网络延迟+锁超时被强制剥夺和重分配的逻辑,在特定情况下就是不正确的。

问题可以理解,可是仔细想想这个问题的本质是什么?它的本质其实就是消息延迟+重排序的问题,或者更本质地说,就是分布式系统不同节点保持 consistency 的问题,因为 lock service 和 client 就是不同的节点,lock service 认为之前的锁过期了,并重分配锁给了 client 2,并且 client 2 也是这样认为的,可是 client 1 却不是,它在 GC 之后认为它还持有者锁呢。

如果我们把数据的写操作和锁管理的操作彻底分开,这个问题就很难解决,因为两个节点不可能 “一直” 在通信,在不通信的时间段内,就可能会发生这种理解不一致的情况。但是如果我们把写操作和锁管理以某种方式联系上,那么这个问题还是可以被解决的。简单说,就是物理时钟不可靠,逻辑时钟可以解决这个问题

之后 Martin Kleppmann 提出了解决方案,他的解决方案也就是按照这个思路进行的。他的方法很简单,就是在获取锁的时候,得到一个永远递增的 token(可以被称作 “fencing token”),在执行写操作的时候,必须带上这个 token。如果 storage 看到了比当前 token 更小的 token,那么那个写操作就要被丢弃掉。

Chubby

Chubby 是 Google 的分布式锁系统,论文在这里可以找到,还有这个胶片,对于进一步理解论文很有帮助。从时间上看,它是比较早的。

Chubby 被设计成用于粗粒度的(coarse-grained)锁需求,而非细粒度(fine-grained,比如几秒钟以内的)的锁需求。对于这样一个系统,文中开始就提到 consistency 和 availablity 重要性要大过 performance,后面再次提到首要目标包括 reliability,能够应对较多数量的 clients,和易于理解的语义,而吞吐量和存储容量被列在了第二位。

Chubby 暴露一个文件系统接口,每一个文件或者文件夹都可以视作一个读写锁,文件系统和 Unix 的设计思路一致,包括命名、权限等等的设计都是基于它。这是一个很有意思的设计。

对于一致性的达成,它使用 Paxos,客户端寻找 master 和写数据都使用 quorum 的机制,保证写的时候大部分节点成功,而读的时候则是至少成功读取大部分节点(R+W>N,这个思路最早我记得是 Dynamo 的论文里面有写);如果 lock 有了变化,它还提供有通知机制,因为 poll 的成本太高。

内部实现上面,每一个 Chubby 的 cell 都是由作为 replica 的 5 个服务节点组成,它们使用 Paxos 来选举 master 和达成一致,读写都只在 master 上进行(这个看起来还是挺奢侈的,一个干活,四个看戏)。如果 master 挂掉了,在 master lease 过了以后,会重新选举。Client 根据 DNS 的解析,会访问到该 cell 里面的某一个节点,它不一定是 master,但是它会告知谁是 master。

分布式锁里面比较难处理的问题不是失败,而是无响应或者响应慢的超时问题。Chubby 采用一种租约的机制,在租约期内,不会轻易变动当前的 master 节点决定。在响应超时的时期,客户端的策略就是 “不轻举妄动”,耐心等待一段时间等服务端恢复,再不行才宣告失败:

这个图的大致意思是,第一次租约 C1 续订没有问题;第二次租约续订 C2 了之后,原来的 master 挂了,心跳请求无响应,这种情况客户端不清楚服务端的状况,就比较难处理,于是它只能暂时先阻塞所有的操作,等到 C2 过期了之后,有一个 grace period;接着再 grace period 之内,新的 master 被选举出来了,心跳就恢复了,之后租约续订的 C3 顺利进行。

这显然是一个异常情形,但是一旦这种情况发生,系统是被 block 住的,会有非常大的延迟问题。思考一下,这种情况其实就是从原来的 master 到新的 master 转换的选举和交接期间,锁服务是 “暂停” 的。再进一步,这个事情的本质,其实就是在分布式系统中,CAP 原理告诉我们,为了保证 Consistency 和 Partition Tolerance,这里的情形下牺牲掉了 Availability;同时,为了保证 consistency,很难去兼顾 performance(latency 和 throughput)。

此外,有一个有点反直觉的设计是,Chubby 客户端是设计有缓存的。通常来讲,我们设计一个锁机制,第一印象就是使用缓存会带来复杂性,因为缓存会带来一致性的难题。不过它的解决办法是,使用租约。在租约期内,服务端的锁数据不可以被修改,如果要修改,那么就要同步阻塞操作通知所有的客户端,以让缓存全部失效(如果联系不上客户端那就要等过期了)。很多分布式系统都是采用 poll 的方案——一堆 client 去 poll 一个核心服务(资源),但是 Chubby 彻底反过来了,其中一个原因也是低 throughput 的考虑,毕竟只有一个 master 在干活。

对于前面提到的 Martin Kleppmann 谈到的那个问题,Chubby 给了两个解决方法:

  1. 一个是锁延迟,就是说,如果一切正常,那么持有锁的客户端在释放掉锁之后,另外的客户端可以立即获取锁。但是如果出现超时等等异常,这个锁必须被空置一段时间才可以被分配。这个方法可以降低这个问题出现的概率,但是不能彻底规避问题。
  2. 第二个就是使用序列号了,对于写入操作来说,如果请求携带的序列号要小于前一次写入的序列号,那就丢弃请求,返回失败。

回过头思考 Chubby 的实现机制,我觉得有这样几个启发:

  1. 不要相信任何 “人”(节点),必须询问到多数人(quorum),多数人的结论才可以认为是正确的。这个方式发生在很多操作上,比如寻找 master,比如选举 master,比如写数据。
  2. 超时是很难处理的,它采用了租约的机制保证节点丢失时间的上限,使用 grace period 来容忍 master 选举发生的时延,使用序列号来保证正确性。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

被归纳迭代统治的世界

在这个AI快速改变世界的时代,园主已经从一开始被GPT表现出来的潜在智力的震撼,到现在对于层出不穷的图像视频音频AI工具有点审美疲劳了。去年的时候和朋友们感慨,在归纳和演绎之间,这个阶段归纳的力量远远超越了演绎。算力的突破仿佛像《三体》中描述的突破智子封锁一样,让可以被计算改进的模型都深深享受着巨量数据带来的断层优势,层出不穷地展现着未来的可能性。

AI模型之外,很多产业也都被基于数据的归纳和快速迭代逐渐颠覆着。shein在快时尚的成功,是千千万万的时尚元素排列组合迭代出来的。时尚爆款可能是玄学,但只要样本量足够大、选择足够多,就一定会出现几个爆款,然后只要快速跟进就可以吃到一波流量红利。类似的玩法不仅仅局限于快时尚,但凡是“义乌制造”可以连夜复制出来的消费品,都可以用这个打法。譬如手机壳,原型相对固定,考验的是设计师的创意和流行元素变化。看一个纪录片说,人们平均一个月换一个手机壳(可能是北上广的消费数据),那么消费者对于新意的渴望就成为显而易见的需求。

从文字,到图像,到视频,到落地成为一件工业制造品,快速迭代的可能性充分地考验着人们的贪心。行业之中的人们各司其职, 努力地优化着每一个可以减少成本或者提高效率的环节。那些看似玄学的艺术和非理性,最后也没敌过归纳和迭代的降维打击。

毁灭吧,消费主义快点变回极简主义吧,要不园主实在是跟不上这个光怪陆离的世界了。毕竟这人脑子还是习惯基于演绎的思考,重新训练到归纳的角度有点超出人脑算力和记忆存储的局限了。以有限对抗无限,怪不得庄子说,

吾生也有涯,而知也无涯。以有涯随无涯,殆已;已而为知者,殆而已矣!

附录:看到一个纪录片《这货哪来的》(B站的?)来的灵感,把这些观察串联了起来。

辞旧迎新(其二)

这其实是一篇迟到的更新,因为距离事情发生已经过去了一个月。小王子的房间虽然有书桌,可他有时还是喜欢在客厅看书和写作业。然而,之前装修时为了追求美观,客厅装的是水晶灯,好看是好看,可光线实在不怎么亮。再加上小王子在学校体检时被查出有轻度近视,于是我决定在暑假前把客厅灯换掉。
❌