我已经有一段时间没有接受软件工程面试了。 但我仍然记得我最喜欢的面试问题。 那是在 2013 年左右的 MemSQL 会议上。(他们 甚至没有保留他们的名字,所以我认为他们不再依赖这个特定的面试问题。 我并不因为揭露它而感到难过。 这是一个很棒的故事,我给人们讲过很多次; 我以前从未写过博客。)
好的,所以这本身不是一个“问题”;而是一个“问题”。 这是一个编程挑战。 我忘记了他们为此付出了多少时间。 假设三个小时,算上解释问题的时间。
由于 MemSQL 是一家数据库公司,因此这是一个数据库挑战。
介绍内存缓存
你知道 memcached
? 不? 嗯,它是内存中的键值存储。 (请在此处阅读其“关于”页面。)让我们下载它的代码并构建它,以便我可以向您展示它的作用。
您可能需要
brew install libevent
也许还需要一些其他东西才能成功构建 memcached。 弄清楚这一点并不会太困难; 但无论如何,与环境的争论并不是测试的一部分。 您可以假设受访者已经获得了访问 Linux 机器的权限,并且所有正确的依赖项都已就位。
为了获得真实的 2013 年体验,让我们绕过 GitHub 仓库
并解压当代源代码发行版:
curl -O https://memcached.org/files/memcached-1.4.15.tar.gz
tar zxvf memcached-1.4.15.tar.gz
cd memcached-1.4.15
./configure
make
现在我们已经构建了 memcached
可执行的。 让我们开始运行:
我们可以通过默认的memcached端口11211与服务器通信。它的协议基本上是纯文本,所以我们可以使用普通的旧协议 telnet
与它交谈。 (如果你没有 telnet
不再,替换单词 nc -c
为了 telnet
.)
$ telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
玩转 memcached
memcached 是一个键值存储。 这意味着我们可以告诉它记住一些东西(键和值之间的关联),然后再次请求该键,它会告诉我们它记住的值。 在 memcached 中,键始终是 ASCII 字符串,值始终是任意字节流(这意味着您必须手动指定它们的长度)。 例如,在您的 telnet 会话中输入以下内容:
set fullname 0 3600 10
John Smith
这告诉 memcached 记住字符串键之间的关联 fullname
和 10 字节值 John Smith
。 该行上的其他数字是“标志”值(0
)与字节流值一起记住; 和过期超时(3600
)以秒为单位,之后 memcached 将忘记此关联。 不管怎样,当你输入这两行之后,memcached 将会响应:
现在您可以检索值 fullname
通过在同一个 telnet 会话中输入:
memcached 将响应:
VALUE fullname 0 10
John Smith
END
您可以覆盖与关联的值 fullname
通过发行另一个
set fullname
命令。 您还可以要求memcached以某些方式修改该值; 例如,有专门的命令 append
和 prepend
。
append fullname 0 3600 6
-Jones
STORED
get fullname
VALUE fullname 0 16
John Smith-Jones
END
当然如果你想追加 -Jones
到 fullname
从客户端程序中,您 可以 做这样的事情:
# pip install python-memcached
import memcache
mc = memcache.Client(['127.0.0.1:11211'])
v = mc.get('fullname') # get the old value from memcached
v += '-Jones' # append -Jones
mc.set('fullname', v) # set the new value into memcached
但是memcached的专用优势 append
命令是保证执行的 原子地。 如果您有多个客户端连接到同一个 memcached 服务器,并且它们都尝试同时附加到同一个密钥,则
get/set
版本可能会导致其中一些更新丢失,而 append
你放心,他们都会被查到的。
另一个以原子方式执行的专用命令是 incr
:
set age 0 3600 2
37
STORED
incr age 1
memcached 以后增量值响应:
由于存在多个客户端,此响应很有用。 如果您单独发出
get age
命令,您可能只有在其他几个客户端完成自己的更新后才能看到新值。 如果您打算将该值用作序列号、SQL 主键或类似的东西,那么有一种方法可以查看递增的值,这是非常好的 原子地。
当然,memcached 也会记住增加的值:
get age
VALUE age 0 2
38
END
请注意 37
和 38
仍然以字节串的形式存储和返回; 它们作为原子操作的一部分从 ASCII 解码为整数,然后再解码回来。 如果你尝试 incr
一个非整数值,你会得到一个错误:
incr fullname 1
CLIENT_ERROR cannot increment or decrement non-numeric value
最后,请注意 incr
是一个成熟的加法运算:您可以递增(或 decr
)通过任何正值,而不仅仅是通过 1
。
incr age 10
48
decr age 10
38
incr age -1
CLIENT_ERROR invalid numeric delta argument
顺便说一句,当您与 memcached 通信完毕并想要终止连接时,您可以键入 memcached 命令 quit
。 (如果您正在使用 nc -c
, Ctrl+D 也有效。 或者,只需转到终端窗口 memcached
服务器正在运行,请按 Ctrl+C 将其关闭。)
挑战:修改 memcached
通过其 incr
和 decr
命令,memcached 提供了一种内置方法来原子地将 (k) 添加到数字上。 但它不提供其他算术运算; 特别是,不存在“原子乘以(k)”运算。
您的编程挑战: 添加一个 mult
命令到 memcached。
完成后,我应该能够远程登录到您的 memcached 客户端并运行如下命令
你有三个小时的时间。 去!
剧透及分析请参见
“我遇到过的最好的工程面试问题,第 2 部分。”
1711375950
#我遇到过的最好的工程面试问题第 #部分 #Arthur #ODwyer #主要是关于 #的内容
2024-03-25 05:28:28