V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  wxf666  ›  全部回复第 25 页 / 共 26 页
回复总数  516
1 ... 17  18  19  20  21  22  23  24  25  26  
2022-07-21 19:07:13 +08:00
回复了 cmxzj 创建的主题 问与答 大表更新
@cmxzj 我感觉 #3 的语句应该是很快的?分批操作,实际没有减少数据量,反而增加了数据传输的损耗
2022-07-21 19:03:39 +08:00
回复了 cmxzj 创建的主题 问与答 大表更新
@gy123 咋会不走主键和索引呢

#3 的语句,我觉得流程应该是:

1. 扫描表 B ,过滤掉 B.colb 为 null 的行
2. 表 B 剩余的行,每行查索引 A.colc ,看是否存在值 B.cold
3. 若存在,从 A.colc 覆盖索引获取 表 A 的主键 ID ,再定位到表 A 的行记录,更新数据

如果说 update set limit 存在不足,就是表 B 已更新的行,每次都还要再检查一遍吧

加个 update set where B.id > ? limit 就好,但 mysql 好像没有 update returning ,无法确定上一次更新了哪些行
2022-07-21 17:47:00 +08:00
回复了 cannotfindobject 创建的主题 Java 咨询一下大家 activiti 几百万数据查询慢怎么办
为何不先加 ASSIGNEE_索引试试呢?
2022-07-21 17:43:17 +08:00
回复了 cmxzj 创建的主题 问与答 大表更新
@L0L 数据库新手求问,能不能不查出来(有传输数据的损耗),直接 update set limit ?

分批的作用,就是为了不一直堵塞,是嘛?
2022-07-21 17:08:53 +08:00
回复了 cmxzj 创建的主题 问与答 大表更新
这样?语句等价不?

update A join B on A.colc = B.cold and B.colb is not null
set A.cola = B.colb
2022-07-21 16:24:20 +08:00
回复了 PEax 创建的主题 Python 新手请教,关于操作列表的问题
@PEax 这样?

def sum_of_squares(items):

   def square(n: int):
     time.sleep(1)
     return n ** 2

   with ThreadPoolExecutor(max_workers=4) as executor:
     return sum(executor.map(square, items))

if __name__ == '__main__':
   print(sum_of_squares(range(1, 10 + 1))) # 1^2 + 2^2 + ... + 10^2
2022-07-20 19:27:51 +08:00
回复了 tool2d 创建的主题 问与答 有谁考虑过用移动硬盘当主力盘使用?
@512357301 叠瓦盘写放大有多严重?写 1MB 等于写 100MB ?
数据库新手求问,多大规模的表,就考虑冗余呢?

总不至于整个用户表就十几 MB ,也搞个冗余吧?估计数据库都缓存它们到内存了
原因 @zhlxsh #1 和 @foam #2 说了,解决方法 @foam #2 说了,我来扩展下思路


在通过『|』『()』『$()或``』启动的 subshell 中修改变量,只会在 subshell 中生效,不会影响 parent shell:
```bash
declare -i total=0

sum() {
   for i in {1..3}; do
     total+=i
   done
   printf '%4s: %d\n' "$1" "$total"
}

sum '|' | cat
(sum '()')
echo "$(sum '$()')"
echo "外部: $total"
```


结果,三种方式启动的 subshell ,都计算得 total=1+2+3=6 ,但实际都未修改外部的 total:
```
  |: 6
 (): 6
$(): 6
外部: 0
```


若要修改,就要在同一个 shell 环境中。对于『|』,可以尽量用『<<<』『< <()』等代替:
```bash
# seq 3 |
while read -r i; do
   total+=i
done < <(seq 3)
```


如果要捕捉输出,就想办法赋值到某个变量中(如 @foam #2 利用的 printf -v )。但归根结底,还是利用了 bash 的『动态作用域』。

bash 手册说,变量对自身及调用的子函数可见
> variables are visible only to the function and the commands it invokes

函数中使用了某个变量,先在自身找,找不到则在上一层调用者中找,一直到全局作用域
> visible variables and their values are a result of the sequence of function calls that caused execution to reach the current function. The value of a variable that a function sees depends on its value within its caller, if any, whether that caller is the "global" scope or another shell function


1. 所以,简单地,可以直接约定,子函数输出到 out 变量中,调用者直接用 out 变量
```bash
count=1

counter() {
  (( count++ ))
   out='test' # 自身找不到 out ,就在调用者 main 中找到 out 再赋值
}

main() {
   local out
   counter
   echo "count: $count, out: $out"
}

main  # 输出:count: 2, out: test
```


2. 将『要赋值到哪个变量』作为参数 /环境变量,传递给子函数,子函数自己想办法赋值

2.1 使用 @foam #2 说的 printf -v

2.2 使用『引用』
```bash
count=1
global_out=

counter1() {
  # 本函数内,out 是『名为「$1 的值」的变量』的引用(可同名,外部作用域的同名变量暂时被隐藏)
  # 如,out 是 main_out 的引用。对 out 的操作,实际是对 main_out 操作(自身找不到 main_out ,就在 main 找)
   declare -n out=$1; shift
  (( count++ ))
   out='test1'
}

counter2() {
  # 本函数内,out 是『名为「$out 的值」的变量』的引用
  # 右边的 out 是调用者临时扩充的环境变量,如 global_out (自身、main 找不到 global_out ,就在全局作用域找)
   declare -n out=$out
  (( count++ ))
   out='test2'
}

main() {
   local main_out
   counter1 main_out  # 作为参数传递
   out=global_out counter2  # 作为临时环境变量传递(综合觉得这种调用好看)
   echo "count: $count, main_out: $main_out, global_out: $global_out"
}

main  # 输出:count: 3, main_out: test1, global_out: test2
```
2022-07-19 20:19:20 +08:00
回复了 huntley 创建的主题 问与答 有没有自动生成 pdf 书签(目录)的软件?
@Juszoe 非扫描件,一般不都自带书签吗。。
2022-07-19 20:16:42 +08:00
回复了 huntley 创建的主题 问与答 有没有自动生成 pdf 书签(目录)的软件?
上面改成:

手动整理出下表,……
2022-07-19 20:15:40 +08:00
回复了 huntley 创建的主题 问与答 有没有自动生成 pdf 书签(目录)的软件?
自动搜集并生成书签的不知道

手动把诸如下表,转换成目录,并嵌入 pdf ,还是可以写个小脚本解决的

页码 层级 标题
  1    0  封面
  2    0  第一章
  3    1  第一章·第一节
……

关键命令:pdftk update_info_utf8
2022-07-16 22:41:44 +08:00
回复了 blankmiss 创建的主题 Python 刚撸 Python ,问个问题
你放点需求出来呗,说不定 Python 自身的语言特性都能满足你了
@brader 我试了下,大致有以下结论:

1. 即使我用 text 类型存 "1,2,3,…"( latin1 编码),无论是 700 / 7000 字节,find_in_set 都是 index

看来 MySQL 还不足够聪明优化 find_in_set

我还以为会生成个临时表,然后 FROM b JOIN a ON a.id IN tmp_table_of_a_ids 呢。。


2. JSON_TABLE 根据 "1,2,3,…" 生成表,再 join 表 a 是 eq_ref ,效率看来不错


3. 表 b 即使插入两行有 7000 字节的 ids 的行记录,也都在同一页( 16KB ),不用担心查找溢出页导致的效率问题


4. 你说的方法二(新增 C 表),我试了下,插入一千万行(a_id, b_id),磁盘占用 272.8 MB ,平均每行占用约 29 字节

看了下书,主要是每行数据额外记录了(记录头信息 5 字节 + 事务 ID 列 6 字节+ 回滚指针列 7 字节)= 20 字节,然后才是 (int, int) 的 8 字节,所以 C 表其实空间利用率很低

若是用 "1,2,3,…" 存储,即使每个 a_id 是 8 个数字+1 个逗号,一千万个记录也才 9 * (10 ^ 7) / (1 << 20) ≈ 85.8 MB

可即使是 85.8 MB ,楼主也说“计算量小,但是耗地方”

所以 C 表更不符合楼主要求


SET SESSION group_concat_max_len = 8192;
SET SESSION cte_max_recursion_depth = 2048;

CREATE TABLE `a` (
 `id` int NOT NULL AUTO_INCREMENT,
 `time` date NOT NULL,
 `data` int NOT NULL,
 `user` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

CREATE TABLE `b` (
 `id` int NOT NULL AUTO_INCREMENT,
 `user` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
 `sum_data` int NOT NULL,
 `a_ids` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

INSERT INTO a (time, data, user)
WITH RECURSIVE
  generate_series(i) AS (
   SELECT 0
   UNION ALL
   SELECT i+1 FROM generate_series WHERE i < 1600
 )
SELECT DATE_ADD('2021-01-01', INTERVAL i DAY), i + 1, 'a'
  FROM generate_series
WHERE i < 199  -- ids 长度为 2*9(1~9,) + 3*90(10~99,) + 4*100(100~199,) -1(末尾逗号) = 687 B
UNION ALL
SELECT DATE_ADD('2021-01-01', INTERVAL i DAY), i + 1, 'b'
  FROM generate_series
WHERE i <= 1760 - 200  -- ids 长度为 4*800(200~999,) + 5*761(1000~1760,) -1 = 7004 B
UNION ALL
SELECT DATE_ADD('2021-01-01', INTERVAL i DAY), i + 1, 'c'
  FROM generate_series
WHERE i <= 3240 - 1761; -- ids 长度为 5*1480(1761~3240,) -1 = 7399 B

INSERT INTO b (user, sum_data, a_ids)
SELECT user, sum(data), GROUP_CONCAT(id)
  FROM a
GROUP BY user;

EXPLAIN
SELECT a.id
  FROM b
  JOIN a ON FIND_IN_SET(a.id, b.a_ids)
WHERE b.user = 'c';

EXPLAIN
SELECT a.*
  FROM b,
    JSON_TABLE(
      CONCAT('[', b.a_ids, ']'),
      '$[*]' COLUMNS (id INT PATH '$')
    ) AS ids
  JOIN a USING(id)
WHERE b.user = 'c';


C 表测试:

SET SESSION cte_max_recursion_depth = 1 << 31;

CREATE TABLE `c` (
 `a_id` int NOT NULL,
 `b_id` int NOT NULL,
  PRIMARY KEY (`a_id`, `b_id`)
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

INSERT INTO c
WITH RECURSIVE
  generate_series(i) AS (
   SELECT 1
   UNION ALL
   SELECT i+1 FROM generate_series WHERE i <= 10000000
 )
SELECT i, i
  FROM generate_series;
@brader

1. 我查了下,你是说方法一会扫描表 a 整个聚集索引才得出结果?(这么傻的么。。)

有没有将 a_indexes 变成一张表再去 join 的方法( split 后成为一张表)?我试着用了一下 json_table ,应该可行?

SELECT a.*
FROM b, JSON_TABLE(
CONCAT('[', b.a_ids, ']'),
'$[*]' COLUMNS (id INT PATH '$')
) AS ids JOIN a USING (id)
WHERE b.id = 1;


2.1 不知道😳


2.2 确实要扩充 a_ids 的长度,但不知你说的“索引失效”何意?超过 767 字节时,只是行溢出到其他页上?

而且,我读了下《 MySQL 技术内幕:InnoDB 存储引擎(第二版)》 111-116 页,大意是:

Compact 格式下,只要保证一页至少有两行数据,VARCHAR 、TEXT 、BLOB 可以不溢出到其他页(一页 16KB 情况下,大概是一行不超过 8098 字节?)。若溢出了,前 768 字节存在数据页上,其余在溢出页
@brader 数据库新人求问,为何方法二高效?

方法一查表 B 得到 a_indexes (如"0,1,2"),然后去 join 表 A 得到各自数据,好像这个思路看起来也不慢?

SELECT a.*
FROM b JOIN a ON FIND_IN_SET(a.index, b.a_indexes)
WHERE b.index = 0

是这样实现吗?应该足够聪明,不是扫 a 全表吧?
@mylovesaber 不是大佬,只是被 bash 折磨过一阵

原文有些错误,后面也有人纠正了,注意甄别

1. 对于 7s 部分的代码,每测试一个含 : 的参数,你就要开启 2 + 614 * 6 = 3686 次子进程。。。

2. awk 的初始化部分,应该在外部传递参数。如 -v keywords="$(IFS=:; echo "${MySQLKeywords[*]}")"

不能 xxx.sh 是因为没有执行权限?好奇你 bash xxx.sh 会发生啥
@aloxaf 嗯,$() 会启动 sub shell ,但 | 只是保证各个命令会在独立进程中执行吧

> Each command in a pipeline is executed as a separate process (i.e., in a subshell)

1. 如 sleep 2 | sleep 3:

/init
└─ -bash
  ├─ sleep 2
  └─ sleep 3

2. 而 f() { sleep 2; }; f | sleep 3:

/init
└─ -bash
  ├─ -bash
  │ └─ sleep 2
  └─ sleep 3

至于你说的“<<< 只是单纯的重定向”,是其 stdin 重定向至(临时)文件?还是管道(由某个 bash 喂进去)?
@haoliang 试了一下,<<< 和 <<EOF 都实现为 pipe ,< file 则不是

bash -c '[[ -p /dev/stdin ]] && echo piped' <a.txt # 没回应

echo | bash -c '[[ -p /dev/stdin ]] && echo piped' # piped
bash -c '[[ -p /dev/stdin ]] && echo piped' < <(echo 'abc') # piped
bash -c '[[ -p /dev/stdin ]] && echo piped' <<<'abc' # piped
bash -c '[[ -p /dev/stdin ]] && echo piped' <<EOF # piped
abc
EOF

所以应该不是管道导致的慢(大家都用的管道)

也试了下,在 $() 中确实没见到 echo 等 builtin 命令的进程,如:$(read -t 3 | sleep 3)

估计是由 $() 实现了 read ?没启动外部进程,但内部还是执行了相关功能?(如 echo -e 会转义参数里的 \ 内容)

所以 echo | xxx 比 <<< 慢?
@haoliang 请教一下,`echo xx | cmd` 的主要开销如果 pipeline 上的话,<<< 的实现是啥?不是管道传输作为标准输入吗?

为嘛 v 站的回复不能用 markdown 。。
1 ... 17  18  19  20  21  22  23  24  25  26  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2973 人在线   最高记录 6543   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 26ms · UTC 14:11 · PVG 22:11 · LAX 07:11 · JFK 10:11
Developed with CodeLauncher
♥ Do have faith in what you're doing.