当前位置:   article > 正文

Python Django Redis+Lua条件筛选达到极致响应速度

Python Django Redis+Lua条件筛选达到极致响应速度

之前的一个自动化操作项目,因为涉及到资金的操作,操作又需要特别的频繁,所以对响应速度有很高的要求,使用MYSQL最快也需要一百多毫秒,所以感觉还是太慢了,最后使用Redis做数据库,因为没有筛选功能,只能自己写个程序来实现这个功能,LUA脚本为Redis所支持的语言,最终使HTTP响应速度在30-50毫秒之间,比较满意了,代码仅供参考交流
按时间查询指定数据

params = {
            'page': page, "limit": limit, "is_on": is_on, "day": day, "end_bid_day": end_bid_day, "user": user
        }
query_data = function(now_day, end_bid_day, key)
  • 1
  • 2
  • 3
  • 4
--[[
    受限的Lua库:Redis Lua环境仅包含下列Lua库:table、string、math、debug、cjson和cmsgpack
    布尔值返回:true:1,false:None
]]--

local v_include = function(list, value)
    --判断table中是否包含某个值
    if not list then
        return false
    end
    for k, v in pairs(list) do
        if v == value then
            return true
        end
    end
    return false
end

local function merge_data(data, is_on_list)
    -- 使用合并数组的方式,使开启的数据排前面
    for k, v in pairs(is_on_list) do
        table.insert(data, 1, v);
    end
    return data
end

local function isEmpty(data)
    -- 判断字符,以及表是否为空
    local t = type(data);
    if t == "number" or t == "string" then
        return data == nil or data == ''
    elseif t == "table" then
        if next(data) == nil then
            return true
        else
            return false
        end
    end
    return data
end

local table_diff = function(tbl_a, tbl_b)
    -- 数组差集计算
    if type(tbl_a) ~= 'table' or next(tbl_a) == nil then
        return nil
    end
    if type(tbl_b) ~= 'table' or next(tbl_b) == nil then
        return tbl_a
    end
    local tbl_c = {}
    for _, v in pairs(tbl_a) do
        for _, vv in pairs(tbl_b) do
            if v == vv then
                --存在于tbl_b, 赋值成nil,不写入新tbl_c
                v = nil
                break
            end
        end
        if v ~= nil then
            table.insert(tbl_c, v)
        end
    end
    table.sort(tbl_c)
    return tbl_c
end

local table_len = function(tab)
    -- 计算键值对的长度  计算数组的长度 可以直接使用:table.getn(tab) #tab
    local l = 0
    for _, _ in pairs(tab)
    do
        l = l + 1
    end
    return l
end

local split = function(str, reps)
    local resultStrList = {}
    string.gsub(str, '[^' .. reps .. ']+', function(w)
        table.insert(resultStrList, w)
    end)
    return resultStrList
end

local query_data = function(now_day, end_bid_day, key)
    --[[
        参数前增加 ( 符号来使用可选的开区间 (小于或大于)
        end_bid_day:有参,仅显示当天的数据
        now_day:显包含当天及之后天数的数据
    ]]--
    local data = {}
    if end_bid_day ~= nil then
        data = redis.call('ZRANGEBYSCORE', key, end_bid_day, end_bid_day)
    else
        data = redis.call('ZRANGEBYSCORE', key, now_day, '+inf')
    end
    return data
end

if KEYS[1] == "set_bid_over" then
    -- 数据写入,需要先判断键名是否存,然后再进行写入,返回布尔值
    local i = 0
    for k, v in pairs(cjson.decode(ARGV[2])) do
        local uid = ARGV[1]
        local end_time = string.gsub(split(v["jssj"], " ")[1], "-", "")
        local number_id = v['id']
        local key = table.concat({ "data", uid, number_id }, ":")
        local is_exist = redis.call('EXISTS', key)
        if is_exist == 1 then
            -- 将数据处理一下,得标的状态:1001,失标:-1001
            if v['zt'] == "失标" then
                redis.call('HSET', key, 'status_code', "-1000")
            end
            if v['zt'] == "得标" then
                redis.call('HSET', key, 'status_code', "1000")
            end
            -- 得标或是失标,需要将剩余时间写0,方便后面正常排序
            redis.call('HSET', key, 'surplus_time', 0)
            redis.call('HSET', key, 'bid_over', cjson.encode(v))
            i = i + 1
        end
    end
    return i
end

if KEYS[1] == "query_domain" then
    -- 域名数据查询,需要支持多域名,支持分页功能,因为返回不了完整理的结果,可以只返回一个结果的键,然后脚本,再按键获取结果

    -- 传入参数部分处理
    local params = cjson.decode(KEYS[2])
    local user = params['user']

    local page = tonumber(params['page'])
    local limit = tonumber(params['limit'])
    local start = page * limit - limit + 1
    local is_on = tonumber(params['is_on'])
    local now_day = tonumber(params['day'])
    -- 先日期排序,方便超过当天日期的就直接过滤,对值的格式,内容进行一个规范
    local end_bid_day = params['end_bid_day']
    local second
    if end_bid_day ~= nil and string.len(end_bid_day) >= 8 then
        -- 判断是否带了秒的信息,防止 end_bid_day 空白字符导致的非 nil 状态
        local arr_time = split(end_bid_day, '|')
        if table.getn(arr_time) == 2 then
            second = tonumber(arr_time[2])
        end
        end_bid_day = string.gsub(arr_time[1], "-", "")
    end
    end_bid_day = tonumber(end_bid_day)

    local domain_list = cjson.decode(ARGV[1])

    local offList = {}
    local onList = {}
    local i = 0
    local total

    -- -----------------------------------------------  内部函数功能  Start ---------------------------------------
    local get_hash_data = function(v)
        -- 将转入的hash键名数据,数组格式转成字典格式数据
        local record = redis.call('HGETALL', v)
        --将记录处理成字典格式
        local res_record = {}
        for rk, rv in pairs(record) do
            if rk % 2 == 1 then
                res_record[rv] = record[rk + 1]
            end
        end
        return res_record
    end

    local page_limit = function(start, page, limit, data)
        -- 数组分页设计 带搜索规则的可能需要遍历到最后
        local page_record = {}
        for i = start, page * limit do
            -- 无记录就不用遍历了
            if data[i] == nil then
                break
            end
            table.insert(page_record, data[i])
        end
        return page_record
    end

    local table_sort = function(tab, order)
        -- 排序,按日期排序,日期包含的信息在'end_time'中,键名已经是按日期排序的,只是没有精确到时,分
        table.sort(tab, function(a, b)
            if order == "-" then
                return tonumber(a.surplus_time) > tonumber(b.surplus_time)
            else
                return tonumber(a.surplus_time) < tonumber(b.surplus_time)
            end
        end)
        return tab
    end

    local data_split = function(data_list, reps)
        -- 在数组中返回过滤符合条件的数据
        local data = {}
        for k, v in pairs(data_list) do
            table.insert(data, split(v, reps)[1]);
        end
        return data
    end

    local str_to_json = function(data)
        -- JSON 格式转换 page_record 数组内容直接被改写了
        for rk, rv in pairs(data) do
            if rv["detail"] ~= nil then
                rv["detail"] = cjson.decode(rv["detail"])
            end
            if rv["offer_record"] ~= nil then
                rv["offer_record"] = cjson.decode(rv["offer_record"])
            end
            if rv["bid_over"] ~= nil then
                rv["bid_over"] = cjson.decode(rv["bid_over"])
            end
        end
        return data
    end

    local wr_data = function(res_record, tab)
        -- 统一写入数据的函数,is_on:true:使用数组,是为了与服务程序取值方式保持一致性,未使用:res_record['is_on'] == '1'
        if v_include(tab, res_record['id']) then
            table.insert(onList, res_record);
        else
            table.insert(offList, res_record);
        end
    end

    local is_time_area = function(second, data)
        -- 针对秒范围内的数据处理,还需要考虑剩余秒数在负数的,允许在结标之前的5分钟数据显示
        if second == nil then
            return true
        end
        local surplus_time = tonumber(data["surplus_time"])

        local now_second = tonumber(redis.call('TIME')[1])
        local get_time = tonumber(data["get_time"])
        local calculate_sc = (get_time + surplus_time) - now_second
        if (second >= surplus_time or second >= calculate_sc) and calculate_sc >= -300 then
            return true
        end
        return false
    end

    local filter_user = function(value, data_list)
        -- 在数组中返回过滤符合条件的数据
        local user_list = {}
        for k, v in pairs(data_list) do
            local key = split(v, ':')
            if string.find(key[2], value) ~= nil then
                table.insert(user_list, v);
            end
        end
        return user_list
    end
    -- -----------------------------------------------  内部函数功能  End ---------------------------------------

    -- 代码区,起始 普通数据 默认数据或是未开启的
    local all_data = query_data(now_day, end_bid_day, 'data:total')
    if next(all_data) == nil then
        return cjson.encode({ data = {} })
    end
    all_data = data_split(all_data, "#")

    -- 代码区 开始了的数据 未开启的也需要使用开启的做排除,无数据:data_is_on = []
    local data_is_on = query_data(now_day, end_bid_day, 'is_on:true')
    data_is_on = data_split(data_is_on, "#")


    -- 对数据求差集,得到不包含开启竞价的数据 适合于is_on == 2
    local data_is_off = (data_is_on ~= nil and { table_diff(all_data, data_is_on) } or { all_data })[1]
    local data
    if is_on == 0 then
        data = merge_data(data_is_off, data_is_on)
    end
    if is_on == 1 then
        data = data_is_on
    end
    if is_on == 2 then
        data = data_is_off
    end

    -- 对帐号ID进行筛选
    if isEmpty(user) == false then
        data = filter_user(user, data)
    end
    --if user == '' then
    --    return data
    --end

    if next(domain_list) == nil then
        -- 无域名查询条件 避免不需要的遍历,只取需要的部分,总数量后期处理
        for k, v in pairs(data) do
            i = i + 1
            local res_record = get_hash_data(v)
            local is_time_area = is_time_area(second, res_record)
            if is_time_area then
                wr_data(res_record, data_is_on)
            end
        end
    end

    if next(domain_list) ~= nil then
        -- 有域名查询条件
        for k, v in pairs(data) do
            local res_record = get_hash_data(v)
            for k2, v2 in pairs(domain_list) do
                i = i + 1
                --进行判断域名值比对 find 第二个参数是匹配模式,并非简单的字符串
                local find_str = string.gsub(v2, "-", "%%-");
                if string.find(res_record['domain'], find_str) ~= nil then
                    local is_time_area = is_time_area(second, res_record)
                    if is_time_area then
                        wr_data(res_record, data_is_on)
                        break
                    end
                end
            end
        end
    end



    local page_record = {}
    total = table_len(offList) + table_len(onList)

    --按结束日期排序
    local sort_onList = table_sort(onList, "-")
    local sort_offList = table_sort(offList, "+")



    -- 合并开启,与未开启数据
    page_record = merge_data(sort_offList, sort_onList)

    -- 分页处理
    page_record = page_limit(start, page, limit, page_record)

    -- 将字符转JSON格式
    page_record = str_to_json(page_record)
    return cjson.encode({ page = page, total = total, limit = limit, start = start, data = page_record, i = i });
end

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/355536
推荐阅读
相关标签
  

闽ICP备14008679号