Gsoc08-tss
项目概述
实现从 tsvector 列收集统计信息,以及一个用于 @@ 算子的选择性函数,该函数将利用这些统计信息。执行下列操作即可获得最新版本:
git clone git://git.postgresql.org/git/~wulczer/gsoc08-tss.git
或浏览 http://git.postgresql.org/?p=~wulczer/gsoc08-tss.git;a=summary
详细信息
基本目标是编写一个用于 tsvector 类型的自定义 typanalyze 函数和一个新的选择性函数,该函数将取代 contsel 进行 @@ 选择性估计。出现的频率最高的词素将存储在 pg_statistic 中,而选择性函数将根据这些数据做出决策。两个函数都将包含在 contrib 模块中。
估计的时间表
- 深入研究源代码,编写用于 tsvector 类型的 typanalyze 函数以计算出现频率最高的词素,然后将它们存储在 pg_statistic 中。(4 周)
- 编写用于 @@ 算子的选择性函数,该函数会查看收集的统计信息并返回选择性估计值(2 周)
- 修补算法,探索使用先前计算的统计信息来获得更准确估计值的可能性(1-2 周)
- 编写测试来估算规划器中引入的开销(尤其是在复杂的 tsqueries 中),必要时重构代码(1-2 周)
- 作为真正的 contrib 模块对成果进行封装,为最终用户添加文档 + 在出现意外延迟的情况下提供安全缓冲(2 周)
可能的改进
如果我的工作进展超出计划,则可以留出空间来运用一些更智能的算法来估计查询的选择性。这些任务似乎与一些自然语言处理问题相关,可能有一些巧妙的解决方案可以考虑。
附加资源
-hackers 上提供了较为详细的设计建议 (http://archives.postgresql.org/pgsql-hackers/2008-03/msg00702.php)
您还可以查看提交给 Google 的原始建议 (http://code.google.com/soc/2008/postgres/appinfo.html?csaid=6EACD033B4F97D56)
工作成果
第一步是要粗略地了解统计代码的作用。我大概了解如何存储统计信息,并且知道你可以轻松插入自己的统计信息收集函数,所以我直接开始尝试存储,嗯,假设一个包含单词“foo”且频率为 1.0 的一元数组。
经过数百个分段错误,我开始阅读 gdb-mode 的文档。
经过一些苦苦地单步调试(我要告诉你,Emacs *是* gdb 的最佳前端),我发现创建 SQL 数组的 Postgres 表示形式的函数需要传递其元素的数据类型。好吧,说得通。但是!它的默认值是列的数据类型,而统计信息就是针对该列收集的!所以我一直在向它提供 C 字符串,而它一直将它们用作 tsvector!嗯,等等,关于数据类型的假设是硬编码在代码中的。如果你分析的是 tsvector,那么你将存储的就是 tsvector。也许这是一个缺陷……所以,向 -hackers 发送邮件。检查两次,按“发送”。刷新,刷新,刷新。啊哈!不管这听起来多么不可思议,但我还是对了。那么,我们对此编写一个补丁。
等等,他们使用 CVS。我需要 *在线* 才能获得 *diff*?那个,rsync 存储库?好的,有一个叫 cvsup 的东西,没有它,CSV 只是一个不可用的垃圾,应该被放置(以所有荣誉)在一个橱柜里,每年擦拭两次灰尘,这样旁观者就不会咳死。
用 Google 搜索 cvsup。嗯,“如果你幸运的话,你的操作系统供应商会为你打包 cvsup”。是啊,没错,Slackware 11.1 打包了 cvsup。或者 Modula-3 编译器。这绝对不会容易。不过,我似乎记得有一个 Postgres CVS 的 git 镜像。而且 Heikki(我的导师)提到他想尝试使用 git 进行开发。Slackware 打包了 git,这多么方便。
浏览至 http://git.postgresql.org/。给管理员发邮件,git-pull 源。万岁,它奏效了,现在我可以在公共场所输入 git-foo 来搭讪了。我甚至得到了一个帐户,这样我就可以 git-pull 我所做的。性感。
现在,补丁。等等,他们使用制表符?哦,太可恨了!嗯,他们还有一个示例 .emacs 代码段,它应该可以很好地设置一些东西。我们来试试看。好吧,它确实看起来很漂亮,变量排列整齐。也许制表符并不是那么邪恶。我的意思是,它们*确实*邪恶,我们都知道,但是你可以忍受它们,它们不会掏空你的大脑。无论如何,如果你在 c-mode-hook 中添加一个巧妙的钩子。
所以,最后,一些编码。设计、设计、设计——Postgres 的法门(至少给我的印象是这样)。好极了,你用电子邮件发送自己的想法,人们告诉你你是错的(他们也确实错了)。更棒的是,他们告诉你你应该怎样做。为所有人节省时间。所以,在短时间后它就在那里了。看!几天之后——它已提交
我的天,这是什么?一封声称你可以去参加加拿大 PGCon 会议,并且他们会付费的信!稍加思索——按下“回复”。“嗨,我想去”。“嗯,那是一个很远的地方,而且你需要避开美国,因为他们有针对你们这些二等公民的签证”。“是的,但是航班很便宜,而且我将避开美国”。“好的,你在名单上了”。
于是,我就在加拿大,倾听所有这些黑客的意见,迄今为止,这些黑客更像是来自邮件列表存档的鬼魂声音。日程很紧,而且我有自己的笔记本电脑,所以白天听讲座,晚上黑客。听听、听听、听听、吃饭、听听、听听、去酒吧、黑客、黑客、黑客、怎么回事?、黑客、黑客、编写 elisp 代码、黑客、黑客、!> 连接已终止、黑客、诅咒、黑客、睡觉。
第二天,我将笔记本电脑带到了辅导教程现场(最后一天我是三个没有带上电脑的人之一。我觉得很奇怪)。突然,收到罗伯特·特里特的一封电子邮件。“嗨,你向我发送了你的闪电演讲幻灯片吗?”。天哪,幻灯片?我认为只有五分钟?“嗯,还没有,我将在明天之前准备完毕”。看起来又是一个漫长的夜晚...
然后是闪电演讲本身。我实际上认为站在演讲者面前举着的大计时器显示的是*剩余*时间,而不是*经过*时间。我快速瞥了一眼,顿时惊慌失措,于是我提前一分钟结束。哦,好吧,总比超时好。
回到华沙之后(经由阿姆斯特丹,所以有很多观光要做),是继续进行这个项目的时候了。所以,既然我现在可以存储我想要的的数据类型,就让我们看看是否可以从 tsvector 样本中提取最常见的词素。嗯,它看起来像是进行一个简单的循环的问题。将补丁编码,然后将其发送给-hackers。嗯,首先编译 diffutils,然后想一想人们为什么会偏好上下文差异而不是统一差异。哦,好吧,评判这件事不是我的职责。
等等,在几百个 tsvector 上循环将很慢。更糟的是,通过蛮力确定最常见的词素将为 O(N^2),那*非常*慢。哈希表可以解决这个问题。现在,我始终通过这种方式使用它们:foo['sth'] = 5。在*C*中执行它完全不同。但它没有那么难,对吧?
这次通读了 gdb 文档后,加上漫长的小时花费来随机探测不同的内存地址,我终于弄懂了。哦哦哦,所以哈希结构 *包含* 了散列键?我称之为违反直觉(我知道有记录。但总感觉怪怪的)。
现在,一切都井然有序,经过了哈希处理。但是...*哈希处理* 所有这些元素也没有好处。你不希望把所有内容都储存在内存中,你需要想出一个聪明的办法。在谷歌上搜索“从流中获取最常见元素”。那是什么?某数据库会议上的论文。嗯,有损计数。看起来是一种精巧的算法,易于理解,易于执行,易于证明其正确。或许可以试一试。
嗯,你懂得。在论文中读到的算法确实有效。并且它可以帮助你快速获得正确的成果。在进行了一些其他编码之后,利用新掌握的 git 技能召唤出另一个补丁。了解到 Emacs 也具有 diff-context->unified。
一件奇妙的事情发生了,它在下一轮提交时 已提交!tsvector 的一个全新 typanalyze 函数。现在可以利用它来获取信息,造福规划人员。
实际上它很容易。从下至上浏览 tsquery,使用常识方法对乘法和否定概率求值。只要我不必在最常用的词素数组中查看每个词素...二分查找来补救这种情况。啊,糟了,词素是非空终止 C 字符串,而统计数据存储文本数据(因为存储 C 字符串似乎不合规范)。因此,我需要以一种方式使用 qsort(),并以另一种方式使用 bsearch()。丑陋。然后,每次计划查询时,我都需要对最常见的词素进行排序,以便在遍历到 tsquery 时可以使用二分查找。它是缓慢的,并且设计不当。
最终顿悟。让 ANALYZE 对词素进行排序,让规划人员看到预排序的数据。与此同时,先按词素长度排序,再按字节数排序,因此如果长度不匹配,你无需解冻数据即可对它进行比较。仍不确定这是否是一个胜利,但我们拭目以待。在进行了一些小改动后,我得到了一个准备提交的 代码。
因此,这三个月一直很忙碌,并且获得了充足的黑客乐趣。9 月份的提交已安排,并且文本搜索选择性得到改进,这很可能纳入 8.4 版。激动、乐趣、耳目一新、教育。这就是 GSoC 带给你的!
学到的东西清单(按随机顺序排列)
- Emacs pwnz(早已知道这一点,但总是欢迎确认)
- 文档比代码更重要
- hsearch(3) 以一种我倾向于称之为“变态”的方式工作
- git 不错
- 从论文中获取的算法 *确实* 有效
- 制表符很邪恶,但可以驯服
- Postgres V1 在 Lisp 中(它!)
- 您永远不知道您下周会在哪里睡觉
这便概括了 - 总之,大获成功!