> 昨天我在本站使用指南节点看到站长说骂人也属于言论自由
您是说
https://www.v2ex.com/t/36991 吗,这都 12 年的主题帖了
> 怎么记住那么多页面的?感觉某些好像不是搜索就行的,有什么方法吗
建议立即安装浏览器扩展之
https://chrome.google.com/webstore/detail/better-history/egehpkpgpgooebopjihjmnpejnjafefi https://chrome.google.com/webstore/detail/session-buddy/edacconmaakjimmfgnblocblbcdcpbko> 你说的那个 bit array 的位数是 tag 的数量吧
是,比如您现在总共有 10 个 tags 那么 bitmask 的长度就是 10 位(但注意大多数环境下处理 bitmask 时都会被视作 unsigned int 处理并对齐到最近的 int32/64 上因此即便您只需要 10 位他内部也还是 padding 了一堆 0 的 32/64 位)
如果使用 mysql 对 bitmask 的封装类型之
https://dev.mysql.com/doc/refman/8.0/en/set.html 那就是`SET(a,b,c,d,e,f,g,h,i,j)`
请注意由于 mysql 的 set 类型只不过是对不同长度的 int 类型的封装所以他也受到最长的 bigint 也只有 64 位的限制,所以当您的 tags 超过 64 个时您应该增加第二个 tags 列来存储,这样也会稍微增加查询时的复杂度(多复制粘贴约束一个字段),如果以后 mysql 自带了 bignumber 那样的可以进行数学运算的长度“无限”(实际上一般是 2^32-1 )的 int 类型那您就不需要按 64 个来拆分
不要使用字符串类型(定长 varchar/变长 text )来存储二进制位的字符串表达(比如 0101010 ),因为他们实际上是 ASCII 的 0x30 和 0x31 字节,也就是说阁下花了 8 倍的空间( 1byte=8bits )来存储他们,而且还成功实现了无法直接进行位运算(除非先把二进制字符串转二进制 int )以实现 CRUD tags 集合或 xor 计算 hamming 距离
> 为了防止你有什么更高明的方法问一下
v2 人们会直接推荐您上各种现成的开源或商业的推荐算法封装实现产品,这样您就只需要做调包侠把 mysql 已有的数据灌进去就能获得“实时”计算出的相关推荐流,但这同样也带来了您去学习如何调包的成本(当然总比折腾 mysql 简单)
> 存储方面我只能想到数据库压缩存储
https://dev.mysql.com/doc/refman/8.0/en/storage-requirements.html 进一步指出
> SET('value1','value2',...) 1 、2 、3 、4 或 8 个字节,具体取决于集合成员的数量(最多 64 个成员)
实际上阁下并不需要担心存储这一堆纯 int 有多大,您之前也看到了阁下生成的 10m 行的 content_tag_rel 表才几百 m 大
您可以先尝试 innodb 的两种压缩方式:tablespace 层面(`ALTER TABLE ... ROW_FORMAT=COMPRESSED`)和 page 层面的压缩(`ALTER TABLE ... COMPRESSION="zlib"`):
https://blog.koehntopp.info/2021/09/09/mysql-two-kinds-of-compression.html https://dev.mysql.com/doc/refman/8.0/en/innodb-compression.html如果阁下真要十亿级别的行并空间尽可能小建议直接换列存储(如 clickhouse tidb )或时序(如 influxdb )数据库,但这也同样陷入了上述`v2 人们会直接推荐您`
> 计算方面怎么处理我就想不出来了
建议复习
https://en.wikipedia.org/wiki/Mask_(computing)假设您现在每个 content 行中的 tags 字段类型是`SET(a,b,c,d)`,因此您总共有着 4 个可能的 tags ,按经典的 CRUD 来看:
C:给某个 content 增加`a`tag:`tag = tag | 0b0001`(注意是小端序,下同)
R:tags 中是否有`a`:`tag & 0b0001 = 0b0001`
U:反转 tag (如果已经有`a`就删除,反之亦然):`tag ^ 0b0001`,其中^是 XOR 异或运算符
https://en.wikipedia.org/wiki/Exclusive_or https://en.wikipedia.org/wiki/XOR_gateD:删除`a`:`tag & 0b1110`,注意这的操作数跟 R 是完全相反的,但运算符都是 AND ,也就是说~0b0001=0b1110 ,其中~是 NOT 运算符
https://en.wikipedia.org/wiki/Negation以上所有运算都可以同时对多个 tag 执行并在单个位运算之内完成
也就是说如果您想查询同时有着 tag`a`和`c`,那您只需要写`WHERE tags & 0b0101 = 0b0101`,而不需要分开写两个 AND 约束
这被称作
https://en.wikipedia.org/wiki/Bit-level_parallelism 或者说
https://en.wikipedia.org/wiki/Single_instruction,_multiple_data最后回到阁下的计算 tags 集合相似度上:
如果使用对所有 content 两两配对计算之间的 hamming 编辑距离法找出距离<=5 的 tags 集合( 5 是指两个 tags bitmask 之间最多相差 5 个位 /tags )
最朴素的方式是`SELECT * FROM table WHERE BIT_COUNT(tags ^ 当前 content 的 tags) <= 5`,然后对所有 content 的 tags 循环执行这个 sql ,也就是`O(2n)`的时间复杂度
但由于 hamming 距离运算是符合交换律的也就是说 a 与 b 之间(`BIT_COUNT(a 的 tags ^ b 的 tags)`)跟 b 与 a 之间(`BIT_COUNT(b 的 tags ^ a 的 tags)`)是相同的距离,因此您就可以像乘法表裁剪掉了斜向的一半重复值那样只计算一半配对的结果,也就是`O((n*n+1)/2)`的时间复杂度
进一步的阁下还可以利用
https://github.com/Starry-OvO/aiotieba/pull/63#issuecomment-1364693204 中的思路将 32/64 位长的 bitmask 给分组拆成更小的多个 int8/16 字段,然后通过多个 WHERE 约束来 pre-filter 以进一步减少最终需要计算 hamming 距离的行数