-
-
Save Petelin/6efe0f27500e5f80977518c49ddcb77b to your computer and use it in GitHub Desktop.
| // Copyright 2013 Gary Burd | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"): you may | |
| // not use this file except in compliance with the License. You may obtain | |
| // a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| // License for the specific language governing permissions and limitations | |
| // under the License. | |
| package redis_test | |
| import ( | |
| "fmt" | |
| "github.com/gomodule/redigo/redis" | |
| ) | |
| // zpop pops a value from the ZSET key using WATCH/MULTI/EXEC commands. | |
| func zpop(c redis.Conn, key string) (result string, err error) { | |
| defer func() { | |
| // Return connection to normal state on error. | |
| if err != nil { | |
| c.Do("DISCARD") | |
| } | |
| }() | |
| // Loop until transaction is successful. | |
| for { | |
| if _, err := c.Do("WATCH", key); err != nil { | |
| return "", err | |
| } | |
| members, err := redis.Strings(c.Do("ZRANGE", key, 0, 0)) | |
| if err != nil { | |
| return "", err | |
| } | |
| if len(members) != 1 { | |
| return "", redis.ErrNil | |
| } | |
| c.Send("MULTI") | |
| c.Send("ZREM", key, members[0]) | |
| queued, err := c.Do("EXEC") | |
| if err != nil { | |
| return "", err | |
| } | |
| if queued != nil { | |
| result = members[0] | |
| break | |
| } | |
| } | |
| return result, nil | |
| } | |
| // zpopScript pops a value from a ZSET. | |
| var zpopScript = redis.NewScript(1, ` | |
| local r = redis.call('ZRANGE', KEYS[1], 0, 0) | |
| if r ~= nil then | |
| r = r[1] | |
| redis.call('ZREM', KEYS[1], r) | |
| end | |
| return r | |
| `) | |
| // This example implements ZPOP as described at | |
| // http://redis.io/topics/transactions using WATCH/MULTI/EXEC and scripting. | |
| func Example_zpop() { | |
| c, err := dial() | |
| if err != nil { | |
| fmt.Println(err) | |
| return | |
| } | |
| defer c.Close() | |
| // Add test data using a pipeline. | |
| for i, member := range []string{"red", "blue", "green"} { | |
| c.Send("ZADD", "zset", i, member) | |
| } | |
| if _, err := c.Do(""); err != nil { | |
| fmt.Println(err) | |
| return | |
| } | |
| // Pop using WATCH/MULTI/EXEC | |
| v, err := zpop(c, "zset") | |
| if err != nil { | |
| fmt.Println(err) | |
| return | |
| } | |
| fmt.Println(v) | |
| // Pop using a script. | |
| v, err = redis.String(zpopScript.Do(c, "zset")) | |
| if err != nil { | |
| fmt.Println(err) | |
| return | |
| } | |
| fmt.Println(v) | |
| // Output: | |
| // red | |
| // blue | |
| } |
local message = redis.call('ZRANGEBYSCORE', KEYS[1], '-inf', ARGV[1], 'LIMIT', 0, ARGV[2]); if #message > 0 then redis.call('ZREM', KEYS[1], unpack(message)); return message; else return {}; end
➜ gitlib redis-benchmark -n 100000 -t -q evalsha 36990d55283ad0265fbb0f84fac937ff9f7d7e7d test 1559042241 1
====== evalsha 36990d55283ad0265fbb0f84fac937ff9f7d7e7d test 1559042241 1 ======
100000 requests completed in 1.30 seconds
50 parallel clients
3 bytes payload
keep alive: 1
99.86% <= 1 milliseconds
100.00% <= 1 milliseconds
77041.60 requests per second
➜ gitlib redis-benchmark -n 100000 -t -q ZRANGEBYSCORE test -inf 1559042241 limit 0 1
====== ZRANGEBYSCORE test -inf 1559042241 limit 0 1 ======
100000 requests completed in 1.25 seconds
50 parallel clients
3 bytes payload
keep alive: 1
99.83% <= 1 milliseconds
100.00% <= 1 milliseconds
80256.82 requests per second
这个东西track的地方是, 不直接用原子操作去get, 而是先watch一下, 直接get,这样是没有锁的. 在没有数据的情况下是并发的.
当然如果是繁忙的总是有资源返回, 这种方式反而拖慢了