Hedis是Haskelk目前最完善的redis绑定,而且有个独特的特性:自动流水线,号称是最优的。
conn <- connect defaultConnectInfo
runRedis conn $ do
foo <- get "foo"
bar <- get "bar"
liftIO $ print (foo,bar)
在这个简单的例子里,据作者说这两个get
命令被pipeline,完全不需用户干预。我用Wireshark查看发现两个get
请求会在一个连接上分两次发送,没有达到号称的最优。更严重的问题是自动流水线是通过Lazy IO实现的,而且跨过多个runRedis
块,然后问题就来了:
conn <- connect defaultConnectInfo
let fast = runRedis conn (get "nextid")
blocked = runRedis conn (blpop ["queue"] 10)
forkIO $ blocked >>= print
threadDelay (10^6)
forkIO $ fast >>= print
我们分别在两个线程里执行runRedis
,理想情况下blpop
会阻塞一下,而get
会马上返回。但是Hedis会把这两个请求放在同一个连接上分两次发送,结果get
必须等blpop
返回之后才能返回。
如果你没有在runRedis
里面对结果求值,请求就可能和其他runRedis
里的请求共用一个连接。
我的临时应对方案就是在runRedis
的最后对结果进行求值,强制流水线结束,防止对后续请求产生影响(实际上会让后续请求使用连接池里其他空闲连接):
runRedis' conn cmd = runRedis conn $ do
result <- cmd
result `seq` return result
也就是说,有大量blpop
这些可能阻塞的请求时,就要用很大的连接池来防止非阻塞请求被阻塞。
Lazy IO真是坑,Hedis的自动流水线反而让这库变得难用了。