V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  kuanat  ›  全部回复第 8 页 / 共 10 页
回复总数  186
1  2  3  4  5  6  7  8  9  10  
我大概试了一下网站,没感觉查询有什么特别慢的地方。如果要定位慢查询的问题,最好贴上语句和配置信息什么的看一下。

在不改变技术框架的情况下,优化方案就是加索引。再具体一点,这个索引是要能通过用户输入的关键词,快速定位到特定字段包含关键词的数据集。

这里涉及到两个主要的技术点,一个是对文本字段进行分词,提取出关键词。另一个是通过关键词反查数据集,这个要构建的索引叫倒排索引( inverted index )。剩下的事情就是更改查询逻辑,前端查询请求走索引,后端直接取索引筛选出的数据集。(这个索引可以做到后端应用程序里,也可以保存到数据库里)

考虑到楼主非专业开发,我是建议手动做上面的事情。用其他的成熟方案,需要学习的东西其实更多,原理理解不到位的话很可能达不到效果,而且没有必要。


如果是我来做这个事情的话,可能会采取完全不同的方案,楼主可以参考一下。

因为这个数据库基本上是只读不写的,写场景基本只发生在数据修正或者批量更新的时候。所以优化的核心思路就是数据集(数据库)为查询优化。

虽然楼主没有透露这个数据库的表结构,考虑到万智牌大概就几万张,而数据集大概有 10 万条,这个数据库应该是冗余很低,符合范式的设计方式。

这一类数据库是为写操作和存储优化的,冗余很低,提取数据需要对数据表做组合查询。为读操作和查询优化的数据库正好相反,冗余很高。既然最终查询结果是卡牌集合,那就以某种数据结构来记录每张卡牌包含的所有信息,一次读取即可获得完整数据,省去了数据组合的过程。

即便有大量的冗余,内存中以这种数据结构存储卡牌信息,可能不会超过 4GB ,即使再翻个倍也不是问题。那对于查询系统来说,有没有 sql 数据库都不重要了。

当然 sql 对于数据录入还是很有意义的,一方面可能本身数据来源导入比较方便,另一方面修改低冗余数据远比高冗余数据方便。所以这里需要编写个脚本,在每次 sql 数据库更新之后,重新生成一份通用格式的(比如 csv 或者 json )的数据集,后端应用程序反序列化之后即可获得上述的内存数据库。

上面这些如果用技术语言描述,就是 sql 转 kv/nosql ,没有多少技术难度,就是个经验问题。

剩下的事情都差不多,分词、建索引。主要是一些经验层面的优化,这里就随便列举几个。

比如请求是可以缓存的,一般用在高频查询和分页上。再比如具体到这个查询场景说,一个请求如果需要返回几百个数据,要么不合理要么没必要。(人一般不需要这么多,返回太多都是在给爬虫服务了。)
楼主这个是非常简单的 cwd 的问题。

也许是现在大家写代码都过于依赖自动化工具,基础的东西反倒不知道了。不是说楼主,这种情况非常常见。我举几个例子:

- 即便大厂的项目,搞不清外部依赖的比比皆是,特别是 python 类的机器学习项目,鲜有能做好工程化的

- 各种所谓的跨平台工具,处理不好路径的正反斜杠,即使用心处理了,很大概率也是硬编码而不是用系统级的 path 方法

- 外表绚丽的 i18n 结果内部实现竟然不是 rune

反正大家都是草台班子……
189 天前
回复了 yzqdm 创建的主题 Java 请教一下 Java 写物联网项目监控设备上下线方案
我觉得这个事情是个工程问题而非技术问题。


“监控设备掉线是用定时任务来做的”:
说明设备本身就没有心跳机制,只有数据上报功能。工程上这个数据上报行为如果是固定间隔的就能当心跳来用,如果不是,那上面的判定逻辑是没问题的。
后者情况下,不管设备是不是真下线,超过一定时间没有数据上报都认为设备已经下线。对于判定在线的设备,你也只能说截至最后一条消息的时刻,该设备还在线。再换句话,判断是否在线这个事情不需要那么准确。


“隔一段时大批量更改设备状态,并且实时性不高”:
理论上监控系统的实时性不可能强于数据上报的最小间隔,只要定时任务周期和数据上报间隔保持一致就可以了。


“mqtt 那边就推送这个设备掉线的信息到 java 程序”:
主动轮训变事件监听,很标准的做法。技术上说,数据上报消息处理流程不仅写数据库,同时转发给 java 程序让它自己维护一份在线状态表就可以了。


“EMQX 的遗嘱消息存在内存”:
这个消息丢了很重要吗,进程重启很频繁吗?


以我的经验来说,物联网业务里对于单设备状态监控的实时性和准确性要求不高的,你能根据这个数据,给出判定“设备损坏”或者“区域网络失效”之类的经验参数才是真正的目的。这个事情可以容忍非常高的 false positive ,没有必要纠结微观细节。
我不写 java 但我经常需要阅读 java 代码,这个真的挺好用的。
192 天前
回复了 tracymcladdy 创建的主题 程序员 用脚本帮朋友抢个专家号居然没抢到
@tracymcladdy #72

如果服务方不愿意改变抢模式,这个事情就会演变成脚本大战。

脚本大战一旦有人不讲武德,大量暴力发请求,结果就会变成另一个意义上的抽签。最终所有人的最优策略就是加资源提高中签率,尽管大家都知道这么做边际收益很低,但不得不这么做。

另一方面你可以看到提供这种服务的主要模式都变成了“代抢”,甚至承诺抢不到加倍退款。对于商家来说,投入达到某个阈值之后中签率是比较稳定的,成本也是稳定的,只要代抢客户足够多就能赚。因为他们可以保证总有账号可以抢到,但没法保证某个特定账号能抢到。
192 天前
回复了 tracymcladdy 创建的主题 程序员 用脚本帮朋友抢个专家号居然没抢到
鉴于楼上说的手动 tcp 方案太扯淡了,本来想简单吐槽一句,结果没忍住还是把这个事情说清楚吧。

我这么说是有理论和实践经验做支撑的,tcp 手动发包几乎没有统计学上的有效性。这类需求前些年几乎是外包市场仅次于网站开发的业务,钱多门槛低。我自己做得很少,基本上是给别人指导一下,不做的原因主要是业务容易擦边,再就是现在多数大平台的都从抢的逻辑变成抽签逻辑了。

先解释一下为什么有人认为“手动发包”是个有意义的方案。

因为按照一般的“抢”的逻辑,以某个时刻为分界线,在此时刻之后某个特定资源就生效了。在此时刻之前到达服务器的请求是无效的,你希望你的请求恰好在这个时刻之后到达服务器。

那么我们有能力确定服务器的时间吗?绝大多数时间不能。(少数情况下可以通过触发服务器错误等等获得一个相对时间差,然后加上网络延迟做一个粗略估计。)即使能够确定服务器时间,实际应用里也没有太大意义。“上线”这个事情 99% 不是自动化的而是人为的。

因此所有人的策略都做出调整了,用大量链接去覆盖资源上线的可能时段。只要保证资源上线之后有一部分请求以尽量低的延迟到达服务器即可。

这个时候 tcp 手动发包好像就体现出优势了?提前建立好(大量)连接,然后看情况开始发数据。注意这里的潜台词是,网站卡的原因在于 tcp 连接数不够,而且新建立连接会多 1RTT 的延时。

但是这个思路是不是有点眼熟?如果很多人都是这样做,或者某个人控制机器人程序这样做,在服务器看来,客户端只连接不发数据,这连接还不会主动关闭,那它是不是就是 DoS 攻击?(这一类攻击有个专门的名字叫 Slowloris )

这里设定个非常简单也非常普遍的模型,来看看这个做法有没有可行性。或者说“连接数不够”这个假设是不是真的符合现实。

就非常普通的单服务器 nginx 做 ssl offloading 和反代,配置近似默认,upstream 大概是某种动态语言的服务端。网络请求在闲时 10 QPS 忙时 10000 QPS 这样。(意思就是,规模不大,用户量有限。服务器网络带宽不是瓶颈,nginx 也大概率不是瓶颈,瓶颈在后端应用,挂号服务基本上就是这个水平。)

这里套不套应用层防火墙差别非常大。一旦套一层 cf 之类的服务,手动 tcp 发包就没意义了。因为 cf 之类的服务会缓存请求然后回源,不可能在这个层面预先抢占 tcp 连接。

如果退一步讲,服务器裸跑。这个时候你通过手动 tcp 发包,那能够获得的时间窗口有多长?

在 tcp 层面,理论上 keepalive 是无限长的。但是实际上以国内的网络环境来说,经过 NAT 之后中间设备的映射缓存大概就是 150 秒这个水平。实践里可以找公网服务器、代理来绕开这个限制。提这个限制的意思是,利用 keepalive 机制几乎不需要你在程序层面上做很大的改动。

但是在 nginx 服务器层面上,时间窗口就小得多。这类服务器默认对慢速攻击有应对,tcp 连接建立后,headers/body 的默认容忍延迟基本在 60 秒。(但凡看过一点点所谓的优化经验,这个值应该不会超过 10 秒)

那就又回到之前的问题上了,你本来打算预先抢占 tcp 连接,结果时间窗口太小,当所有人的策略都是提前覆盖可能的时段,你的连接会被淹没在海量连接里面。

这里有个非常极端的情况,这个 tcp 手动发包,利用 tcp 的机制,强行把 headers 分成多个包发送,然后每个发送间隔卡在目标服务器的限制之内。(这里就不提这个程序的复杂性了,要么魔改底层网络库,要么纯手写然后一直处理到应用层协议)

但问题是 nginx 这类服务器的反代,一样会先缓存请求,在收到完整请求之后,再转发给 upstream 服务器。也就是说,你抢占了本地到 nginx 的连接,而没有能力抢占 nginx 的 upstream 的连接。

现在回到核心的问题上,服务器在高并发的时候变卡的核心原因是什么?

核心是服务端应用要对特定资源加锁,业务逻辑在高并发的情况下只能等。

由于一般网站的瓶颈都在后端应用服务器上,nginx 能承载的客户端连接数远大于 nginx 到 upstream 的连接数。

当应用服务器不需要处理锁事务的时候,平均响应可以做到很低。但是一旦遇到加锁资源的时候,这个响应时间会成倍增加,理论 QPS 就大幅下降。而此时所有人会更加疯狂地反复刷新,这时候 nginx 到 upstream 的连接就会长期占满。

所以体感上就是,网站先变卡,因为 nginx 在等 upstream 释放出新的连接。等 nginx 撑不住了才会 502 错误。而从客户端来说,没有任何手段能保证你优先获得到 upstream 的连接。

所以说 tcp 手动发包这个事情……别挣扎了。
192 天前
回复了 tracymcladdy 创建的主题 程序员 用脚本帮朋友抢个专家号居然没抢到
手动 tcp 发包的方法也太扯淡了……没实践过就别瞎说。

这种行为本质上和 Slowloris 攻击没区别,当服务器都是傻子呢。
192 天前
回复了 shermie 创建的主题 程序员 寻求一个 Lua 脚本加密工具
既然是 Lua 可以嵌入到 C 里面?
193 天前
回复了 tracymcladdy 创建的主题 程序员 用脚本帮朋友抢个专家号居然没抢到
对于你的这个目标,没有什么风控措施的,最理想的方案就是用上你最大的资源,短间隔高并发请求。

对于有风控和频率控制的目标,那就是个技术活了。
python 版本管理:asdf
https://asdf-vm.com/

虚拟环境:python venv

包管理:pdm
https://pdm-project.org/
196 天前
回复了 wjx0912 创建的主题 程序员 这个是什么编码方式有人能看出来吗
纯靠猜的话,我感觉外层是去掉 +/ 的 base62 ,内层是 aes cbc 128bit 块。

userid 和 did 应该是两个等长字符串的拼接。即 userid/did 都是两段明文,分别 aes 加密然后 base62 编码,然后连接了两段编码后的字符串。

你要是能逆向的话可以试试看我猜得对不对。
197 天前
回复了 kuanat 创建的主题 Go 编程语言 分享一些 Go 在全栈开发中的经验
@yang2yang #33

我不是专门做外包的,但是在疫情封城的时候高强度做过一段时间。那段时间是单人在做,后来回头客多了就根据兴趣和精力选择性做一些,其他的介绍给朋友做。

我在正文暗示过了,这个事情不能卷……外包需求最多的都是做网站做小程序这种,耗时耗力均价也不高,最大的问题是要花大量时间去争抢订单。但是你看我列举的例子,都是比较偏门或者专业的需求,这些订单多数都是各种原因转到我手上的。

另一方面就是这个帖子的由来,交付快自然就能做得多了。
197 天前
回复了 kuanat 创建的主题 Go 编程语言 分享一些 Go 在全栈开发中的经验
@m1dsummer #30

我只了解非常有限的 .NET 的技术,跨平台 Mono 就在项目里尝试过一次,印象还不错。

以我个人的感受来说,微软家的技术路线切换太过于商业驱动了,虽然跨平台方面这两年进步很大,但市场和开发者的信心依旧不够。所以学习相关的技术属于投资有风险那一类。
198 天前
回复了 kuanat 创建的主题 Go 编程语言 分享一些 Go 在全栈开发中的经验
@seers #28

在 Go 的生态里 Cgo 的意义非常重要。做个不那么恰当的类比,游戏行业 Unity/C#/Lua 生态很大程度上就是因为 Lua 和 C 互通能力。

还是回到我正文里提到的 OpenCV 的例子,在 CV 领域几乎没有完全的替代。不得不用的情况下,除非用 C++ 开发,其他语言想要使用 OpenCV 都要有相应的 bindings 。

开发这种 bindings 本身就是很复杂的事情,这里 Cgo 的优势就显现出来了。简单的项目不用写 bindings 就可以直接用,复杂的项目通过 Cgo 可以高度自动化 bindings 的生成过程。

某种程度上可以认为,C 生态完全可以为 Go 所用。正文里提到过,Go 欠缺的恰恰就是这一类的轮子。
198 天前
回复了 kuanat 创建的主题 Go 编程语言 分享一些 Go 在全栈开发中的经验
@jackmod #25

实时性要求高的场景,无 GC 永远都比有 GC 优秀。对于 GC 来说,最难处理的情形要么是碎片化分配,要么是海量引用。我的贴子里提到的技巧主要是解决前者,后者是很难在代码层面上有所改善的,也是 Go 原理上做不到的。

还是那句话,技术栈该换就换。

~~虽然 discord 技术博客写得很好,但是软件体验仍旧是一坨……~~
198 天前
回复了 kuanat 创建的主题 Go 编程语言 分享一些 Go 在全栈开发中的经验
@GeekGao #19

确实是这个意思,Go 的性价比很高。另外 cNk 这个说法我感觉好多年没见过了,N=1 的时候算是个硬件问题,N=10 的时候算是技术问题,N=100 大概更接近于工程问题了。

我在正文没有提到 Go 带来的另一个好处,主要原因是这个体验比较主观。我确实认为 Go 的项目很有利于长期维护。

最明显的一点是,即使我现在回看两年前的项目,不管文档、注释写得如何,几乎很快都能回顾起来。看 GitHub 的开源项目也有同感,几乎都能非常快地定位到关键代码。

核心原因应该是得益于 Go 的两个优秀设计,一个是 `share by communicating` 的 concurrency 模型,另一个是 `accepting interfaces, return structs` 的解耦机制。

在 Go 之前,想要轻松写出正确的多线程代码是非常困难的事情,想轻松写出扩展性强的代码也需要有深厚的功底。
198 天前
回复了 kuanat 创建的主题 Go 编程语言 分享一些 Go 在全栈开发中的经验
@mainjzb #16

Windows API 难用某种程度上说是微软故意的。我没做过特别专业的 Windows 开发,这是我个人的推测。
198 天前
回复了 kuanat 创建的主题 Go 编程语言 分享一些 Go 在全栈开发中的经验
@root71370 #12

对于去掉符号表、字符串混淆之后的 go 二进制,基本只能硬怼汇编,参考对应版本 runtime 的实现来逆向。不是专门做这个的,可能连 main 入口都找不到。基本上黑产都喜欢拿 go 过免杀。

常规没有防逆向措施的 go 二进制就和 c 逆向差不多。
198 天前
回复了 kuanat 创建的主题 Go 编程语言 分享一些 Go 在全栈开发中的经验
@sadfQED2 #11

文章里逆向那段主要是想表达一个意思:防破解本质上是个投入产出比的事情。Go 加上十分钟就能学会的小技巧,只需要几行代码和一点点自动化工具,足以为价值几万元的业务提供防御。

我给这个十分钟小技巧,其实包含了动态、静态、控制流和混淆多个层面的防护手段。强度差不多等同于各类动态语言的 vmp 水平。

对于绝大多数开发者来说,如果不是有丰富的对抗经验,是很难写出来真正有效的防破解逻辑的。从专业逆向者的角度上说,过高的门槛本就拦住了绝大部分人,甚至轮不到考虑投入产出比的事情。
如果这个请求不是特定的,指定 upstream 代理。如果是特定的,再写 python 脚本过滤一下。
1  2  3  4  5  6  7  8  9  10  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1510 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 28ms · UTC 17:15 · PVG 01:15 · LAX 10:15 · JFK 13:15
Developed with CodeLauncher
♥ Do have faith in what you're doing.