V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  h0099  ›  全部回复第 8 页 / 共 8 页
回复总数  159
1  2  3  4  5  6  7  8  
2023-01-11 20:42:09 +08:00
回复了 GGGG430 创建的主题 MySQL sql 中包含特殊字符问题
难道您还在手动拼接 raw sql ?要是这几百 m 的字符串里正好有个`; DROP DATABASE 您的库名;`呢?
2023-01-11 20:11:50 +08:00
回复了 yezheyu 创建的主题 程序员 关于异步任务的一点疑问,有没有老哥帮忙解答下
那就直接把 event loop 视作 FIFO stack
https://i.imgur.com/6v8EAF1.png
您每次 setTimeout/Interval 或创建 Promise (不论 new 还是 Promise.reslove/reject())都是 push 了一个回调进 stack
等您导致 push tsack 的这些同步 js 代码都执行完了之后 js 主线程空闲时就会去 pop stack 取出回调来逐个执行
这也不需要去阅读理解有关 spec html.spec.whatwg.org/multipage/webappapis.html#task-queue 中的 micro/marcotask 概念 stackoverflow.com/questions/25915634/difference-between-microtask-and-macrotask-within-an-event-loop-context
请不要在每一个回复中都包括外链,这看起来像是在 spamming
2023-01-11 17:42:46 +08:00
回复了 jaredyam 创建的主题 Java 这段随机数生成代码为什么这么写?
2023-01-11 17:34:04 +08:00
回复了 h0099 创建的主题 程序员 如何从理论上避免这类并行任务交错执行时的冲突问题
> 但不论 UPSERT 还是 IGNORE 都是从数据库层面缓解问题,他不是保证永不发生 DUPLICATE 错误

如果要从数据库层面解决可以尝试
7.`SELECT ... FOR UPDATE`语法所产生的`插入意向排他锁`
---
https://i.imgur.com/sH4wlWZ.png
线程 1 和 2 每次 SELECT 时都通过 WHERE 子句(只取 field=a 的行)缩小了`FOR UPDATE`所造成的[IX 锁]( https://dev.mysql.com/doc/refman/8.0/en/glossary.html#glos_intention_exclusive_lock)的范围(不然没有 WHERE 我怀疑会锁全表,也就是类似于[`LOCK TABLES ... WRITE`]( https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html))
线程 2 在执行后必须无限期(直到达到[innodb_lock_wait_timeout]( https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_lock_wait_timeout))等待线程 1`COMMIT`,因为线程 2 想要 SELECT 的行`field=a`已于此前被线程 1 设置 IX 锁
不难看出此时进程范围的全局锁(进程锁)已经没有存在的必要了,因为他的目的跟 IX 锁是类似的(但他无法目前像 IX 锁那样无限期阻塞任何试图读取进程锁的线程)
实际上 IX 锁同样降低了并行度,因为这跟原文中提出的`1.延后线程 2 查询数据库的时机`是类似的目的:
都是将任何试图读取某些行(`IX 锁`是 field=a 的行,`1.延后线程 2`是所有行,也就是回到了`LOCK TABLES`)的线程挂起直至正在写入某些行的其他线程`COMMIT`
也就是说两者都是试图通过[serialize]( https://en.wikipedia.org/wiki/Serializability)最开始读行的过程从而避免后续`INSERT`时冲突

https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-intention-locks
https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-insert-intention-locks
https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html
https://stackoverflow.com/questions/21261213/select-for-update-with-insert-into
https://stackoverflow.com/questions/10935850/when-to-use-select-for-update

但人生自古谁无死,不幸地,截止 2022 年 11 月发布的最新版本`EFCore 7`,我所使用的.NET 企业级 ORM `Entity Framework Core`中仍未引入任何原生的 IQueryable<T> linq2sql operator 使得其可以被翻译为追加在`SELECT`末尾的`FOR UPDATE`: https://github.com/dotnet/efcore/issues/26042#issuecomment-993534289
目前能用的变通只有额外查询 rawsql 来表示 IX 锁(而不能直接嵌入已有的 linq2sql 中): https://stackoverflow.com/questions/37984312/how-to-implement-select-for-update-in-ef-core
2023-01-11 16:51:08 +08:00
回复了 yezheyu 创建的主题 程序员 关于异步任务的一点疑问,有没有老哥帮忙解答下
> 要把 js 引擎和浏览器分开来,js 引擎负责解析 js 代码,浏览器才是最终真正干活的东西,这两者不是一体的。

分开理解 v8 和 chromium 最简单的方式就是去用 njs ,njs 环境下有浏览器 api 吗?您能在 njs 里去操作根本不存在的 dom 吗?

> 当 js 引擎执行完所有的同步代码之后,你可以理解为 js 引擎唯一的工作就是,等浏览器踢一脚去执行队列里对应的回调函数。

setTimeout 是在往 loop 追加了一个应该在未来执行的 task ,而`delay: 0`参数只是意味着 loop 应该挂起这个 task 等到 0ms 后再执行( 0ms 也就是不等待)
但不论是传入 setTimeout 还是 Promise.reslove 的回调都是在未来才会发生的,所以才必须等到 console.log(1 和 3)两个同步调用都执行完(也就是整个`console.log(1) setTimeout console.log(3)`)后才会再去执行 loop 中已有的应该在未来执行的 task (在这里就是 setTimeout 的回调 console.log(2))

> js 引擎是单线程的,本身也没有计时、网络请求之类的功能,他的任务也只是把活交给浏览器去干。相对于的 nodejs 也一样。

准确地说是 v8 解释器解释执行 js 时是跑在一个 os thread 内部的
2023-01-11 16:05:55 +08:00
回复了 hemingyang 创建的主题 Oracle 请问 SQL 跟新大量数据怎么处理快呢?
看看 EXPLAIN SELECT tcz.KCXZ FROM T_J20230106 tcz WHERE T.ID=tcz.ID AND T.KCXZ!=tcz.KCXZ 是不是 full table scan
2023-01-11 15:59:04 +08:00
回复了 PrilgrimLi 创建的主题 Google google maps 定位不准
2023-01-11 15:55:40 +08:00
回复了 yezheyu 创建的主题 程序员 关于异步任务的一点疑问,有没有老哥帮忙解答下
#19 @biguokang
> 是 js 代码通过调用浏览器 web api 从而调用 c++的过程,表面上你写的是 js 代码,但最后实际上干活的是 c++,这个道理放在 nodejs 也同理。

实际上`干活的`涉及许多层级,chromium 的 cpp 代码实际上也是调包特定 os 提供的各种异步 io api 和 syscall ,渲染同理(如果涉及硬件加速还需要通过 ogl/dx 间接的让显卡驱动去跟更多的硬件通信)

> 踢了一下 js 引擎,说我活干完了,你可以执行任务等待队列里的对应回调函数了。

这就是 event loop/message queue

#20 @autoxbc > OP 混淆了 JS 线程和 JS 引擎线程,JS 是单线程,引擎执行完下载,后续代码自然需要回到 JS 线程继续。JS 编码应该保持 JS 视角,只观察引擎提供的接口,不关心引擎实现的细节

js 目前的主流解释器 v8 引擎只是一个 js 解释器,他同样不负责浏览器层面的那些网络请求 ui 渲染等任务
2023-01-11 15:50:07 +08:00
回复了 yezheyu 创建的主题 程序员 关于异步任务的一点疑问,有没有老哥帮忙解答下
#18 @yezheyu
> 所以 xhr 只是负责和浏览器的网络线程通信,下发任务

xhr/fetch 是浏览器暴露的一套 api ,以允许您去指挥网络线程发起 http request
然而很明显在一个单纯的`<img src="url">`中不存在任何`<script>`中的 js 代码去通过 xhr/fetch api 请求这个 url
因此在这里实际上只有 html parser 在请求图片 url ,而后 network thread 再把 url 的 response body 传输给渲染图片的 thread ,还有可能导致 css reflow
建议复习
https://www.chromium.org/developers/design-documents/displaying-a-web-page-in-chrome/
https://www.chromium.org/developers/design-documents/multi-process-architecture/
https://www.chromium.org/developers/design-documents/multi-process-resource-loading/
https://chromium.googlesource.com/chromium/src/+/master/docs/how_cc_works.md

> 对于下载好的资源,你怎么处理,就算只是打印一下,也必须让主线程处理。

如果阁下所说的`打印一下`是指`console.log(await(await fetch('https://www.v2ex.com')).text())`这样的 js 代码,那么 network thread 当然会把 response body 传回给 js thread 从而作为您所调用的的`Response.text()`返回的 promise 的 reslove 值

> 对于结果不管是更新 UI ,还是打印结果,都只能是主线程处理

但如果您没有在 js 中消费这个 Response 对象那不会有任何 byte[]被从 network thread 传输到您的 js thread 上,例如`console.log(await fetch('https://www.v2ex.com'))`
而对于最开始的例子 一个单纯的`<img src="url">` 这里同样没有任何 js 的存在,即上文所说的:
> 要么是其他浏览器 thread 消费,比如有一个`<img src="url">`,那么 network thread 下载完了`url`后就会把 byte[]传给负责图片渲染和 css layout 的 thread ,让他们绘制这个<img>,这里同样没有 js 的事(即便您禁用了浏览器 js 也不会妨碍<img src>正常工作)
owncloud
回顾经典之
https://officechai.com/news/developer-asks-javascript-question-twitter-javascript-founder-steps-help/
https://twitter.com/BrendanEich/status/818570098998800388

另外 php 创始人 Rasmus Lerdorf 并不懂现代 php 生态圈,如 https://news.ycombinator.com/item?id=1044280 https://news.ycombinator.com/item?id=6919248
您不如去致电在 jetbrains 任职开发 phpstorm 时( php7~php8 时代)为 php-src 做出大量贡献的 Nikita Popov
2023-01-11 14:13:30 +08:00
回复了 h0099 创建的主题 程序员 如何从理论上避免这类并行任务交错执行时的冲突问题
原地 UPDATE 为原值跟 INSERT IGNORE 是类似的
只不过完全静默了 WARN (而 INSERT IGNORE 是将 DUPLICATE ERR 降至 WARN )
2023-01-11 14:09:34 +08:00
回复了 yezheyu 创建的主题 程序员 关于异步任务的一点疑问,有没有老哥帮忙解答下
> 那你在代码中的耗时任务该怎么办呢? js 又没有多线程让你开辟一个线程去处理,而浏览器也没有一个特定的 WEB API 帮你执行。

现在您可以使用 web worker 标准的 api https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers 来创建新的 js/wasm thread 来跑您的 cpu 密集耗时任务

> 所以为了不阻塞后续代码只能延期执行,让其它代码先执行,执行完后再执行耗时任务和对其结果的依赖代码,即微任务。

这就是异步

> 每个宏任务都有一个微任务队列,里面放的就是这些延期代码。微任务的执行时机就是当前宏任务执行完,下一个宏任务执行前。这样就可以完美解决。

mirco/macrotask 是 html spec 中标准化了的对 js thread 上的 event loop 和异步调度的优先级实现: https://stackoverflow.com/questions/25915634/difference-between-microtask-and-macrotask-within-an-event-loop-context

> 而 Promise 就是这样的实现

promise 是无栈协程,也就是说他本质上跟回调地狱异步没有区别(这也是为什么 es6 spec 标准化 promise 之前有 jq promise https://api.jquery.com/category/deferred-object/ 、promises/a+ https://promisesaplus.com/ 等各种民间标准,但他们的本质都是对回调地狱的封装使其更好读)
2023-01-11 14:03:41 +08:00
回复了 yezheyu 创建的主题 程序员 关于异步任务的一点疑问,有没有老哥帮忙解答下
> js 之所以是单线程,主要为了避免资源竞争问题。

js 是单线程+无栈异步( promise )+回调异步(传统的 callback hell 就像 IndexedDB )

> 但实际上 js 不可能是单线程,就比如 xhr 图片加载,xhr 在下载时没有阻塞主线程,那图片的下载必定不是在主线程执行,那必定有一个线程帮你执行下载工作,就是网络线程。

负责下载图片等网络请求的是 chrome 的 network thread ,其跟 js 无关

> 所以我认为 js 单线程是指你在 js 代码中无法像其它语言那样使用多线程模块创建一个线程执行一个代码块。

您可以用 worker 创建新的 js thread

> 只能使用预留的 API 调用现有的线程,如浏览器的 xhr Web Api ,node 的 fs 模块

因此 xhr/fetch 本质上是浏览器暴露的一套 api ,其标准化于 https://xhr.spec.whatwg.org https://fetch.spec.whatwg.org 他们存在的目的是允许您在 js thread 中与 network thread 通信,以派发您的下载图片的任务

> 那像 xhr 中在网络线程中要渲染图片,涉及到操作 dom ,就会出现资源竞争问题,所以必须放到任务队列中让主线程来进行

xhr/fetch 没有渲染图片,他们只负责网络请求相关的 payload (实际上跟 os socket 打交道处理网络栈已经够复杂了)
xhr/fetch 最终会给您一坨 byte[]也就是您的 http request 的 http response body ,以及相关的 response 元数据如 http header ( fetch 标准是都放在 https://developer.mozilla.org/en-US/docs/Web/API/Response 里)
要么您的 js thread 去取回这些 byte[](例如通过 https://developer.mozilla.org/en-US/docs/Web/API/Response/arrayBuffer
要么是其他浏览器 thread 消费,比如有一个`<img src="url">`,那么 network thread 下载完了`url`后就会把 byte[]传给负责图片渲染和 css layout 的 thread ,让他们绘制这个<img>,这里同样没有 js 的事(即便您禁用了浏览器 js 也不会妨碍<img src>正常工作)
要么是没有任何人消费这些 byte[],那就直接丢弃
#3 @sinnosong1 猜猜 Select-Object 是什么语义
2023-01-11 13:50:32 +08:00
回复了 edis0n0 创建的主题 Linux 修改 md1 容量后怎么修改包含这个 md1 raid 组的 part 的容量?
> 1GB 大小文件

这是 pgsql 的一个文件分片机制: https://www.postgresql.org/docs/current/storage-file-layout.html
> When a table or index exceeds 1 GB, it is divided into gigabyte-sized segments. The first segment's file name is the same as the filenode; subsequent segments are named filenode.1, filenode.2, etc. This arrangement avoids problems on platforms that have file size limitations. (Actually, 1 GB is just the default segment size. The segment size can be adjusted using the configuration option --with-segsize when building PostgreSQL.) In principle, free space map and visibility map forks could require multiple segments as well, though this is unlikely to happen in practice.

根据
https://dba.stackexchange.com/questions/48273/whats-stored-in-data-dir-base-of-postgresql
https://dba.stackexchange.com/questions/228114/why-is-my-postgres-base-directory-filling-up-rapidly
您有执行过[VACUUM]( https://www.postgresql.org/docs/current/routine-vacuuming.html#VACUUM-FOR-SPACE-RECOVERY)吗
2023-01-11 13:31:43 +08:00
回复了 h0099 创建的主题 程序员 如何从理论上避免这类并行任务交错执行时的冲突问题
#2 @codehz 并不需要`INSERT INTO ... ON DUPLICATE KEY UPDATE`( PGSQL 又称`UPSERT`)因为这是仅插入而没有更新或删除(即 CRUD 只有 C )
也可以直接`INSERT IGNORE`: https://dev.mysql.com/doc/refman/8.0/en/insert.html
> If you use the IGNORE modifier, ignorable errors that occur while executing the INSERT statement are ignored. For example, without IGNORE, a row that duplicates an existing UNIQUE index or PRIMARY KEY value in the table causes a duplicate-key error and the statement is aborted. With IGNORE, the row is discarded and no error occurs. Ignored errors generate warnings instead.

然后在每次`INSERT`后[`SELECT ROW_COUNT`]( https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_row-count)就可以知道少了多少行没有被插入(由于 DUPLICATE 或其他错误)(但只有行的数量,而非精确的对应关系,如果需要知道具体少插入了哪些行仍然需要`SELECT`之前插入的行范围)

但不论 UPSERT 还是 IGNORE 都是从数据库层面缓解问题,他不是保证永不发生 DUPLICATE 错误,而是保证发生 DUPLICATE 错误后您的程序也能跑(因为一个改成了 UPDATE ,一个将 ERR 降级到 WARN )

而我更需要避免的是这种类似 DUPLICATE 造成了数据冗余,但又完全符合数据库层的 UNIQUE 约束的问题:
https://i.imgur.com/l32gdQA.png
可以看到两个线程都插入了“完全一致”的行,除了 time 字段值分别是 1674453494 和 1674453492 (因此两者 INSERT 时都不会触发 DUPLICATE 错误)
而这是因为右侧线程在左侧线程于`12:39:54.874436`时间`COMMIT`之前就已经`SELECT`了,所以右侧不知道左侧即将`INSERT` time 为 1674453492 的“重复”行

对此问题我当然可以选择写一个基于[window function]( https://learnsql.com/blog/sql-window-functions-cheat-sheet/)的`DELECT`的后台 crontab (或是线程每次`INSERT`后都尝试`DELETE`一次)来定期执行删除这类冗余的“重复”行
但这跟`UPSERT/INSERT IGNORE`类似仍然是缓解问题而不是解决问题
而且`DELETE`作为事(`INSERT`)后补救也不可能解决更罕见(线程在同一秒内完成所有任务)的两个线程插入的所有字段都相同(也就是触发 DUPLICATE 错误)的场景
2023-01-11 03:07:21 +08:00
回复了 h0099 创建的主题 程序员 如何从理论上避免这类并行任务交错执行时的冲突问题
1  2  3  4  5  6  7  8  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2377 人在线   最高记录 6543   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 29ms · UTC 13:07 · PVG 21:07 · LAX 06:07 · JFK 09:07
Developed with CodeLauncher
♥ Do have faith in what you're doing.