美文网首页
Redis最佳实践 - 秒杀

Redis最佳实践 - 秒杀

作者: 诺之林 | 来源:发表于2018-09-19 18:02 被阅读298次

    目录

    入门

    启动Redis服务: docker run --name redis-in-action -p 6379:6379 -d redis

    redis-cli
    
    EVAL "return {KEYS[1], KEYS[2], ARGV[1], ARGV[2]}" 2 1 2 3 4
    
    1) "1"
    2) "2"
    3) "3"
    4) "4"
    
    echo "return { KEYS[1], KEYS[2], ARGV[1], ARGV[2] }" >> hello.lua
    
    redis-cli --eval hello.lua 1 2 , 3 4
    
    1) "1"
    2) "2"
    3) "3"
    4) "4"
    

    关于Lua更多介绍 参考Lua简明教程

    秒杀

    • 高并发 - Redis内存

    • 原子性 - Redis单线程

    vim seckilling.lua
    
    local n = tonumber(ARGV[1])
    if not n or n == 0 then
        return 0
    end
    local vals = redis.call("HMGET", KEYS[1], "Total", "Booked")
    local total = tonumber(vals[1])
    local blocked = tonumber(vals[2])
    if not total or not blocked then
        return 0
    end
    if blocked + n <= total then
        redis.call("HINCRBY", KEYS[1], "Booked", n)
        return n;
    end
    return 0
    
    redis-cli script load "$(cat seckilling.lua)" # 58652c43df5e7752b7bb1f8e65e382299b5bd8b6
    
    redis-cli
    
    HMSET goodsId Total 100 Booked 0
    
    HMGET goodsId Total Booked
    
    1) "100"
    2) "1"
    
    SCRIPT EXISTS 58652c43df5e7752b7bb1f8e65e382299b5bd8b6
    
    EVALSHA 58652c43df5e7752b7bb1f8e65e382299b5bd8b6 1 goodsId 1 
    
    HMGET goodsId Total Booked
    
    1) "100"
    2) "1"
    

    关于Redis单线程模型的更多讨论 可以参考为什么redis 是单线程的? & Redis单线程优势 & Redis is single threaded. How can I exploit multiple CPU / cores?

    队列

    • 发布-订阅 - Redis队列

    • 缓存 - 高速Redis + MySQL

    vim seckilling.lua
    
    local n = tonumber(ARGV[1])
    if not n or n == 0 then
        return 0
    end
    local vals = redis.call("HMGET", KEYS[1], "Total", "Booked")
    local total = tonumber(vals[1])
    local blocked = tonumber(vals[2])
    if not total or not blocked then
        return 0
    end
    if blocked + n <= total then
        redis.call("HINCRBY", KEYS[1], "Booked", n)
        redis.call("LPUSH", KEYS[2], "Booked "..n)
        return n;
    end
    return 0
    
    redis-cli script load "$(cat seckilling.lua)" # b191ffa49361dac8f9142f0eac552cecd0df2489
    
    redis-cli
    
    SCRIPT EXISTS b191ffa49361dac8f9142f0eac552cecd0df2489
    
    EVALSHA b191ffa49361dac8f9142f0eac552cecd0df2489 2 goodsId orderList 1 
    
    HMGET goodsId Total Booked
    
    1) "100"
    2) "2"
    
    BRPOP orderList 60
    
    1) "orderList"
    2) "Booked 1"
    

    这里只是将orderList的数据取出 实际项目中可以结合关系数据库处理该订单

    服务

    egg-init --type=simple server
    
    cd server && cnpm i
    
    cnpm i --save egg-redis
    
    vim config/plugin.js
    
    'use strict';
    
    exports.redis = {
        enable: true,
        package: 'egg-redis',
    };
    
    vim config/config.default.js
    
    'use strict';
    
    module.exports = appInfo => {
        const config = exports = {};
    
        // use for cookie sign key, should change to your own and keep security
        config.keys = appInfo.name + '_1537341461009_7706';
    
        // add your config here
        config.middleware = [];
    
        config.redis = {
            client: {
                host: '127.0.0.1',
                port: 6379,
                password: '',
                db: '0',
            }
        };
    
        return config;
    };
    
    vim app/controller/home.js
    
    'use strict';
    
    const Controller = require('egg').Controller;
    
    class HomeController extends Controller {
        async index() {
            const { ctx, app } = this;
            const code =
                `
    local n = tonumber(ARGV[1])
    if not n or n == 0 then
        return 0
    end
    local vals = redis.call("HMGET", KEYS[1], "Total", "Booked")
    local total = tonumber(vals[1])
    local blocked = tonumber(vals[2])
    if not total or not blocked then
        return 0
    end
    if blocked + n <= total then
        redis.call("HINCRBY", KEYS[1], "Booked", n)
        redis.call("LPUSH", KEYS[2], "Booked "..n)
        return n;
    end
    return 0
            `;
            app.redis.defineCommand('seckilling', {
                lua: code
            });
            ctx.body = await app.redis.seckilling(2, 'goodsId', 'orderList', '1');
        }
    }
    
    module.exports = HomeController;
    

    关于egg-redis的更多介绍 可以参考egg-redis & ioredis

    cnpm run dev # localhost:7001
    

    测试

    redis-cli
    
    HMSET goodsId Total 100 Booked 0
    
    DEL orderList
    
    sudo apt update
    
    sudo apt install -y apache2-utils
    
    ulimit -n 10000
    
    ab -n 50000 -c 5000 http://192.168.56.1:7001/
    

    关于ab的更多介绍 可以参考超实用压力测试工具-ab工具

    redis-cli
    
    HMGET goodsId Total Booked
    
    1) "100"
    2) "100"
    

    参考

    相关文章

      网友评论

          本文标题:Redis最佳实践 - 秒杀

          本文链接:https://www.haomeiwen.com/subject/jmpdnftx.html