Redis 大数据量(百亿级)Key存储需求及解决方案

C#开发的Windows服务获取计算机登陆用户名 C#开发的Windows服务获取计算机登陆用户名 最近再开发一个Windows服务,需要获取到计算机等信息,但是服务是以系统管理员身份执行的,然后获取到的登陆用户名一直都是System’,用环境函数等都不行,最后研究了半天才弄好,话不多说,直接上代码, using ComsxLog; using Oracle.ManagedDataAccess.Client; using System; using System.Data; using System.Linq; using System.Configur 阅读详情

一 需求背景

该应用场景为DMP缓存存储需求,DMP需要管理非常多的第三方id数据,其中包括各媒体cookie与自身cookie(以下统称supperid)的mapping关系,还包括了supperid的人口标签、移动端id(主要是idfa和imei)的人口标签,以及一些黑名单id、ip等数据。

在hdfs的帮助下离线存储千亿记录并不困难,然而DMP还需要提供毫秒级的实时查询。由于cookie这种id本身具有不稳定性,所以很多的真实用户的浏览行为会导致大量的新cookie生成,只有及时同步mapping的数据才能命中DMP的人口标签,无法通过预热来获取较高的命中,这就跟缓存存储带来了极大的挑战。

经过实际测试,对于上述数据,常规存储超过五十亿的kv记录就需要1T多的内存,如果需要做高可用多副本那带来的消耗是巨大的,另外kv的长短不齐也会带来很多内存碎片,这就需要超大规模的存储方案来解决上述问题。

二 存储何种数据

人⼝标签主要是cookie、imei、idfa以及其对应的gender(性别)、age(年龄段)、geo(地域)等;mapping关系主要是媒体cookie对supperid的映射。以下是数据存储⽰示例:

  1. PC端的ID:

媒体编号-媒体cookie=>supperid

supperid => { age=>年龄段编码,gender=>性别编码,geo=>地理位置编码 }

  1. Device端的ID:

imei or idfa => { age=>年龄段编码,gender=>性别编码,geo=>地理位置编码 }

显然PC数据需要存储两种key=>value还有key=>hashmap,⽽而Device数据需要存储⼀一种

key=>hashmap即可。

三 数据特点

  • 短key短value:

其中superid为21位数字:比如1605242015141689522;
imei为小写md5:比如2d131005dc0f37d362a5d97094103633;
idfa为大写带”-”md5:比如:51DFFC83-9541-4411-FA4F-356927E39D04;

  • 媒体自身的cookie长短不一;

  • 需要为全量数据提供服务,supperid是百亿级、媒体映射是千亿级、移动id是几十亿级;

  • 每天有十亿级别的mapping关系产生;

  • 对于较大时间窗口内可以预判热数据(有一些存留的稳定cookie);

  • 对于当前mapping数据无法预判热数据,有很多是新生成的cookie;


四 存在的技术挑战

1)长短不一容易造成内存碎片;

2)由于指针大量存在,内存膨胀率比较高,一般在7倍,纯内存存储通病;

3)虽然可以通过cookie的行为预判其热度,但每天新生成的id依然很多(百分比比较敏感,暂不透露);

4)由于服务要求在公网环境(国内公网延迟60ms以下)下100ms以内,所以原则上当天新更新的mapping和人口标签需要全部in memory,而不会让请求落到后端的冷数据;

5)业务方面,所有数据原则上至少保留35天甚至更久;

6)内存至今也比较昂贵,百亿级Key乃至千亿级存储方案势在必行!

五 解决方案

5.1 淘汰策略

存储吃紧的一个重要原因在于每天会有很多新数据入库,所以及时清理数据尤为重要。主要方法就是发现和保留热数据淘汰冷数据。

网民的量级远远达不到几十亿的规模,id有一定的生命周期,会不断的变化。所以很大程度上我们存储的id实际上是无效的。而查询其实前端的逻辑就是广告曝光,跟人的行为有关,所以一个id在某个时间窗口的(可能是一个campaign,半个月、几个月)访问行为上会有一定的重复性。

数据初始化之前,我们先利用hbase将日志的id聚合去重,划定TTL的范围,一般是35天,这样可以砍掉近35天未出现的id。另外在Redis中设置过期时间是35天,当有访问并命中时,对key进行续命,延长过期时间,未在35天出现的自然淘汰。这样可以针对稳定cookie或id有效,实际证明,续命的方法对idfa和imei比较实用,长期积累可达到非常理想的命中。

5.2 减少膨胀

Hash表空间大小和Key的个数决定了冲突率(或者用负载因子衡量),再合理的范围内,key越多自然hash表空间越大,消耗的内存自然也会很大。再加上大量指针本身是长整型,所以内存存储的膨胀十分可观。先来谈谈如何把key的个数减少。

大家先来了解一种存储结构。我们期望将key1=>value1存储在redis中,那么可以按照如下过程去存储。先用固定长度的随机散列md5(key)值作为redis的key,我们称之为BucketId,而将key1=>value1存储在hashmap结构中,这样在查询的时候就可以让client按照上面的过程计算出散列,从而查询到value1。

过程变化简单描述为:get(key1) -> hget(md5(key1), key1) 从而得到value1。

如果我们通过预先计算,让很多key可以在BucketId空间里碰撞,那么可以认为一个BucketId下面挂了多个key。比如平均每个BucketId下面挂10个key,那么理论上我们将会减少超过90%的redis key的个数。

具体实现起来有一些麻烦,而且用这个方法之前你要想好容量规模。我们通常使用的md5是32位的hexString(16进制字符),它的空间是128bit,这个量级太大了,我们需要存储的是百亿级,大约是33bit(2的33次方),所以我们需要有一种机制计算出合适位数的散列,而且为了节约内存,我们需要利用全部字符类型(ASCII码在0~127之间)来填充,而不用HexString,这样Key的长度可以缩短到一半。

下面是具体的实现方式


public static byte [] getBucketId(byte [] key, Integer bit) {

    MessageDigest mdInst = MessageDigest.getInstance("MD5");

    mdInst.update(key);

    byte [] md = mdInst.digest();

    byte [] r = new byte[(bit-1)/7 + 1];// 因为一个字节中只有7位能够表示成单字符,ascii码是7位

    int a = (int) Math.pow(2, bit%7)-2;

    md[r.length-1] = (byte) (md[r.length-1] & a);

    System.arraycopy(md, 0, r, 0, r.length);

    for(int i=0;i<r.length;i++) {

    if(r[i]<0) r[i] &= 127;

    }

    return r;

}

参数bit决定了最终BucketId空间的大小,空间大小集合是2的整数幂次的离散值。这里解释一下为何一个字节中只有7位可用,是因为redis存储key时需要是ASCII(0~127),而不是byte array。如果规划百亿级存储,计划每个桶分担10个kv,那么我们只需2^30=1073741824的桶个数即可,也就是最终key的个数。

5.3 减少碎片

碎片主要原因在于内存无法对齐、过期删除后,内存无法重新分配。通过上文描述的方式,我们可以将人口标签和mapping数据按照上面的方式去存储,这样的好处就是redis key是等长的。另外对于hashmap中的key我们也做了相关优化,截取cookie或者deviceid的后六位作为key,这样也可以保证内存对齐,理论上会有冲突的可能性,但在同一个桶内后缀相同的概率极低(试想id几乎是随机的字符串,随意10个由较长字符组成的id后缀相同的概率*桶样本数=发生冲突的期望值<<0.05,也就是说出现一个冲突样本则是极小概率事件,而且这个概率可以通过调整后缀保留长度控制期望值)。而value只存储age、gender、geo的编码,用三个字节去存储。

另外提一下,减少碎片还有个很low但是有效的方法,将slave重启,然后强制的failover切换主从,这样相当于给master整理的内存的碎片。

推荐Google-tcmalloc, facebook-jemalloc内存分配,可以在value不大时减少内存碎片和内存消耗。有人测过大value情况下反而libc更节约。

  作者 |  小热爱

来源 |  juejin.cn/post/6956147115286822948

C#开发Windows窗体应用程序的步骤 使用C#开发应用程序时,一般包括创建项目、界面设计、设置属性、编写程序代码、保存项目、程序运行等6个步骤。 1.创建项目 在Visual Studio2017开发环境中选择“文件”→“新建”→“项目”菜单,弹出“新建项目”对话框,如图8.1所示。 图8.1  “新建项目”对话框 选择“Windows窗体应用(.NET Framework)”、输入项目的名称、选择保存路径,然后单击“ 阅读详情

相关推荐

c# 如何解决git克隆需要用户名和密码

网上很多的关于使用git需要用户名时是修改控制面板的用户账户的Windows凭据,本人亲试,反正是不适合自己的项目,故把自己的解决方案写下,仅供大家参考 问题:Incorrect username or password (access token) 首先,git clone的地址我使用的是码云的HTTPS地址: 开发者向代码托管平台写入代码时,最常使用的协议是SSH协议,因为SSH协议使用公钥认证,可以实现无用户名和密码访问,但如果使用HTTPS协议,每次git clone时都需要提供正确的

weixin_42077130的博客 1674

我用Java几分钟处理完30亿个数据...

往期热门文章:1、一款自动生成单元测试的 IDEA 插件2、微软 10 大最受欢迎 GitHub 项目,最高 Star 数量 13 万3、Spring Boot 中实现跨域的 5 种方式,你一定要知道!4、Java反射到底慢在哪?5、多线程批量拆分 List 导入数据库!来源:https://c1n.cn/GM8hb目录场景说明模拟数据场景分析读取数据处理数据遇到的问题...

Java之间 973

C#上传文件到Windows远程服务器

using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.UI;using System.Web.UI.WebControls;using System.Text;using System.Threading.Tasks;using System.Diagnos...

LoveMyJx的博客 3479

java上亿数据如何秒查,怎么更快速的查询1000条数据

如何更快速的查询1000条数据?现在的需求是这样的,我有一个模板,里面是JsonArray数据,里面有1000条JSONObject,每个object都有一个客户和产品属性,形如这样{"data":[{"account":"01","product":"p1"},...]}我现在要解析这个JSON,根据每一个object里的客户和产品,到另一个表里来查询它的平均销量和平均单价,然后返回一个加了平均...

weixin_34666364的博客 1885

C#使用windows服务定时调用api接口

C#使用windows服务定时调用api接口 使用VS创建windows服务项目: 创建好项目  会出现一个设计界面 右键弹出对话框 选择添加安装程序 名字什么的自己可以改: 项目目录:   打开项目中的ProjectInstaller.Designer.cs 修改windows服务名称描述以及启动方式等: part...

这里将承载一段最初的记忆 6292

C#访问网络共享文件夹,带用户名密码域,解决电脑重启后访问不到网络文件夹

问题:winform访问网络共享文件夹,电脑重启后访问不到指定目录 原因:访问网络共享文件夹目录需要相关的用户凭据,文件资源管理器可以记住凭据,但是电脑重启后直接用软件访问网络文件夹路径是没有凭据的,就会出现访问不到的问题。 解决:访问时携带用户名,密码,域,等信息 使用: using (SharedTool tool = new SharedTool("用户名", "密码", "域")...

马儿不吃草 3499

这么设计,Redis 10亿数据量只需要100MB内存

点击上方关注 “终端研发部”设为“星标”,和你一起掌握更多数据库知识 ​因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享本文主要和大家分享一下redis的高级特性:bit位操作。本文redis试验代码基于如下环境:操作系统:Mac OS 64位 版本:Redis 5.0.7 64 bit 运行模式:standalone moderedis位操作reids位操作也...

这个时代,作为程序员可能要学习小程序 619

C#系列---⑧ 用C#做windows的任务栏(StatusStrip控件的使用)

一、什么是StatusStrip控件 状态栏控件,该控件中包含4个重要的伴随类 ToolStripStatusLabel 表示StatusStrip控件中的一个面板。 ToolStripDropDownButton 显示一个相关联的ToolStripDropDown,用户可以从中选择单个项目。 ToolStripSplitButton ...

Elsa~的博客 2352

Redis--各个数据类型最大存储量

转载自:https://blog.youkuaiyun.com/sinat_27143551/article/details/80701089 类型 最大存储数据量 string 512M list 2^32 - 1 set 2^32 - 1 sorted set 2^32 -1 hash 2^32 - 1

Liubo的博客 5767

c#中获取cpu序列号、网卡mac地址、ip地址、硬盘序列号、登录用户名、pc类型、计算机名称、物理内存

using System;using System.Management;namespace SHJ{    ///     /// Computer Information    ///     public class Computer    {        public string CpuID;        public string MacAd

dz45693的专栏 2980

C#,WMI获取系统信息

加入命名空间: using System.Management;     ///         /// //获取CPU序列号代码         ///         ///         string GetCpuID()         {             try             {                 string cpuInf

haylhf的专栏 3914

.net4.0 c# 启动远程服务器windows服务

private string strPath; private ManagementClass managementClass; public void startService() { if (remoteC

liuyanhui414的专栏 3566

C#窗体应用

一、窗体及控件的使用 1.编写一个Windows应用程序包含两个窗体,其中一个为登录界面窗体如图2-1所示;单击第登录按钮时,判断用户名和密码是否为空,如果为空则弹出提示信息,否则判断用户名和密码是否匹配,如果不匹配弹出提示信息,如果匹配隐藏登录窗体,同时显示如图2-2所示第二窗体。 2.当单击“黑板效果”命令按钮时,窗体的背景设置为黑色,前景设置为白色。 3.当单击“设置窗体的宽和高”命令按钮时,窗体的宽设置为8000,高设置为6000。 4.当单击“显示文字”命令按钮时,在窗体上输出“黑板效果的测

zzzzzwwwwjjjj的博客 1883

C# WinForm免注册调用大漠插件3.1233:Windows 10自动化开发实战

本文详细介绍了如何在C# WinForm项目中免注册调用大漠插件3.1233进行Windows 10自动化开发。通过动态加载DLL技术,开发者无需管理员权限即可实现鼠标控制、键盘模拟等基础功能,适用于电商抢单、数据采集等场景。文章包含开发环境配置、核心代码实现及实战案例,帮助开发者快速掌握自动化开发技巧。

860

【Windows应用程序开发】窗体应用

👨‍💻

2302_80645965的博客 3386

C# 实现RDP远程桌面连接

C# 实现RDP远程桌面连接 因为频繁的访问远程windows server服务器,每次调出cmd运行mstsc,然后输入一大串信息去链接就显得有些繁琐了。为方便计,于是使用C#编写一个小工具。 本文采用用 .NET 平台中 Microsoft Terminal Services Client ActiveX control 来实现远程桌面的访问。因为这个控件不在默认的工具箱中,所以需要引用出来,...

般若浮尘的博客 1万+

FTP服务器搭建及C#实现FTP文件的上传

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Net; namespace PrintDBSQL.Framework.Utility { public class FTPHelper { /// <summary> .

liulv_yan的博客 1万+

SAP HR模块配置假期日历和缺勤类型

SAP的HR模块。业务顾问在实施时一般会配置未来10年的假期日历,到期后再进行配置。同时,增加请假类型(缺勤信息类型)。记录这一过程。

福来阁86号技师的妖孽日常 5526
上一篇: 程序员缺乏经验的 7 种表现!
下一篇: SpringBoot+webSocket实现扫码登录功能
Java思维导图
博客等级 码龄10年 306粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值