赞
踩
之前的一个自动化操作项目,因为涉及到资金的操作,操作又需要特别的频繁,所以对响应速度有很高的要求,使用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)
--[[ 受限的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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。