B站用户视频观看记录的存储方案

中午上B站看视频的时候,突然好奇B站的视频观看记录是怎么存的。
首先,因为多端观看的视频记录都是同步的,即不管是在手机观看,还是网页观看,还是新设备观看,视频都是可以直接跳转到上次的观看位置的,所以这个记录应该不是存储在本地的。
其次,B站的用户截至2020年底,MAU(月活)达2.02亿,视频总量2020年看有人统计约为7千万条,假设按1亿条计,即使按人均每日观看100个视频,那10年到用户的话每个用户也有36.5万条记录。这样一个超大的稀疏矩阵该怎么存?传统数据库显然不合适。
我自己首先想到的应该是HBase,单实例支持十亿行百万列。因为视频内容是无限增长的,而用户是有限的,所以可以以用户id作为rowkey,视频内容id作为column来记录该用户每个视频的观看位置。
但是视频的数量现在就已经达到亿级别,而且还会继续增长,百万列明显不够。这可能就需要考虑多实例,即把视频内容id按一定的hash规则(或者直接按照顺序,方便扩展)分别存储在不同的实例里,比如1亿条条视频就每个实例存100万条,分100个实例,查询用户视频观看位置时,就根据视频id先到指定的实例去,然后再通过用户id主键+视频内容id列查询位置记录。
不过,想了想又让我感觉这是个比较笨拙的方案。
以上纯属脑子里突然冒出的一个问题,暂且记录一下,具体B站或者其他视频网站(比如腾优爱)是采用的什么技术方案,我会再继续查找和思考。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
融通基金 存储升级及双活数据中心项目 测试方案V1.0 市桑威科技 2016年5月 文档信息 "项目名称: "融通基金VPLEX "文档版本号:"1.0 " " "Metro项目测试报告 " " " "文档作者: "世华 "生成日期: "2016年5月 " "文档审核者: " "审核日期: " " 文档维护记录 "版本号 "维护日期 "作者/维护人 "描述 " "1.0 "2016年5月12日 "世华 "创建初稿 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " 目 录 1. 环境配置 5 2. 测试目的 5 3. VPLEX测试环境的建立 5 3.1. SAN Switch物理连线 5 3.2. SAN Switch配置 5 3.3. 存储系统配置 5 4. 测试场景 6 5. 测试结论 7 6. 测试步骤及结果 8 6.1. 测试1 : VPLEX控制器高可用测试 8 6.2. 测试2: VPLEX后端链路高可用测试 9 6.3. 测试3: 双活镜像测试 9 6.4. 测试4: VPLEX Metro级联链路高可用测试_1 10 6.5. 测试5:双活仲裁测试_1 10 6.6. 测试6:双活仲裁测试_2 11 6.7. 测试7:双活仲裁测试_3 11 6.8. 测试8:双活仲裁测试_4 12 6.9. 测试9:双活仲裁测试_5 12 6.10. 测试10:双活仲裁测试_6 13 6.11. 测试11:双活仲裁测试_7 13 7. 场景测试及结果 14 7.1. 测试场景1:超算VNX5800故障 14 7.2. 测试场景2:汉唐VNX5500故障 14 7.3. 测试场景3:所有存储同时故障 15 7.4. 测试场景4:Witness发生故障 15 7.5. 测试场景5:vplex_1、vplex_2同时故障 15 7.6. 测试场景6:vplex_1、vplex_2同时故障,后端存储也同时故障 16 7.7. 测试场景7:Witness与vplex_1 IP通信中断,其他正常 16 7.8. 测试场景8:Witness与vplex_2 IP通信中断,其他正常 17 7.9. 测试场景9:Witness与vplex_1、vplex2 IP通信中断,其他正常 17 7.10. 测试场景10:vplex_1、vplex2 IP通信中断,但是vplex_1、vplex2各自与witness IP通信正常 18 7.11. 测试场景11:4台FC交换机之间的裸光纤全部故障,其他正常 18 7.12. 测试场景12:超算FC交换机故障 19 7.13. 测试场景13:汉唐FC交换机故障 19 7.14. 测试场景14:所有交换机同时故障 20 7.15. 测试场景15:vplex_1与vnx5800光纤链路故障,其它正常 20 7.16. 测试场景16:vplex_2与vnx5500光纤链路故障,其它正常 21 7.17. 测试场景17:裸光纤全故障,外网LUN在超算起来,网LUN从汉唐起来 21 8. 审计意见 22 9. 讨论反馈后备注 22 环境配置 硬件: "设备 "数量 "微码/版本 "备注 " "VPLEX VS2 "2 "5.5 SP2 "每个机房配置双引擎 " " " " "建议安装版本5.5 SP2 " "SAN Switchs "4 "7.2.1c1 "用于连接EMC VPLEX和后端存储 " "EMC " " "现有5100微码需要升级 " "DS6510B\5100B" " " " "VNX5800\5400 "2 "5.33 "我们使用全新的VNX5800和5400测试" " "2 " "测试主机(待定) " 软件: "项目 "版本 "备注 " "虚化化 "Esxi 6.0 " " "物理机 "OL 6.5 " " "多路径软件 "Esxi 6.0自带、Powerpath " " " "6.0 " " 测试目的 测试不同场景下,测试主机对EMC虚拟化存储的访问状况,从而测试EMC VPLEX metro的高可用性。 VPLEX测试环境的建立 4 SAN Switch物理连线 完成测试主机到SAN Switch,SAN Switch到测试存储系统之间的光纤连接。 5 SAN Switch配置 完成测试主机与测试存储系统之间的ZONE配置。 6 存储系统配置 VPLEX将已封装的LUN分配给ESX主机及物理机 测试场景 vplex_1、Witness、vnx5800 在超算机房,vplex_2、vnx5500 在汉唐机房。 " "测试容 "说明 "预期 " "测试1 "VPLEX单个direct"将一台VPLEX的一
目 录 1.工艺概述 7 2.物料性质 7 3.干燥目的 8 4.编制依据 9 5.干燥原则及要求 9 6.干燥合格标准 9 7.干燥范围 9 8.干燥应具备的条件 9 9.安全注意事项 9 10.干燥步骤: 10 11.所需工具 11 12.组织机构 12 13.记录表 13 14.附表 14 1.工艺概述 将外购液态N2O4卸入N2O4储罐V-3701存储,用于原始开车或出现紧急情况向系统补充氮元素,在酯化塔内与甲醇反应,合成一定量的亚硝酸甲酯用于羰化反应生成草酸二甲酯。当储罐压力高时,储罐压力调节阀PV-37001B打开,排至吸收液槽保持压力稳定。压力低时用氮气调节阀PV-37001A打开保持储罐正压。为保证V-3701四氧化二氮储罐的温度,夏季时,选用冷冻水回水调节阀TV-37001B控制;冬季时,选用循环水回水调节阀TV-37001A控制。 2.物料性质 2.1 理化性质 化学品中文名称:四氧化二氮分子式:N2O4。分子量:92。外观与性状:纯品为无色透明液体。熔点(℃):-11.2。相对密度(水=1): 1.466。沸点(℃):21.1。相对蒸气密度(空气=1):无资料。主要成分:N2O4。饱和蒸气压(kPa): 102kPa(21.1℃)。燃烧热(kJ/mol):无意义。闪点(℃):无意义。引燃温度(℃):无意义。溶解性:溶于水。主要用途:用于制硝酸、硝化剂、氧化剂、催化剂、丙烯酸酯聚合抑制剂等。禁配物:易燃或可燃物、强还原剂、硫、磷。 2.2 健康危害 2.2.1侵入途径:吸入。急性中毒氮氧化物主要损害呼吸道。吸入气体初期仅有轻微的眼及上呼吸道刺激症状,如咽部不适、干咳等。常经数小时至十几小时或更长时间潜伏期后发生迟发性肺水肿、成人呼吸窘迫综合征,出现胸闷、呼吸窘迫、咳嗽、咳泡沫痰、紫绀等。可并发气胸及纵隔气肿。肺水肿消退后两周左右可出现迟发性阻塞性细支气管炎。慢性影响主要表现为神经衰弱综合症及慢性呼吸道炎症。个别病例出现肺纤维化。可引起牙齿酸蚀症。 2.2.2环境危害:对环境有危害。 2.2.3燃爆危险:助燃。与可燃物接触易着火燃烧。
好的,下面是完整的 C 语言代码,包括加、减、乘三个操作以及菜单程序: ```c #include <stdio.h> #include <string.h> #define MAX_LEN 41 // 声明函数 void add(char a[], char b[], char result[]); void subtract(char a[], char b[], char result[]); void multiply(char a[], char b[], char result[]); void print_menu(); void clear_input_buffer(); int main() { char a[MAX_LEN], b[MAX_LEN], result[MAX_LEN]; int choice; while (1) { print_menu(); scanf("%d", &choice); clear_input_buffer(); // 清空输入缓冲区,防止输入错误 switch (choice) { case 1: printf("请输入两个整数 A 和 B:\n"); scanf("%s %s", a, b); clear_input_buffer(); add(a, b, result); printf("A + B = %s\n", result); break; case 2: printf("请输入两个整数 A 和 B:\n"); scanf("%s %s", a, b); clear_input_buffer(); subtract(a, b, result); printf("A - B = %s\n", result); break; case 3: printf("请输入两个整数 A 和 B:\n"); scanf("%s %s", a, b); clear_input_buffer(); multiply(a, b, result); printf("A * B = %s\n", result); break; case 4: printf("程序已结束。\n"); return 0; default: printf("输入错误,请重新输入。\n"); break; } } return 0; } // 加法函数 void add(char a[], char b[], char result[]) { int carry = 0; int len_a = strlen(a); int len_b = strlen(b); // 对齐两个数的位数 for (int i = 0; i < MAX_LEN - 1; i++) { if (i < len_a) { result[i] = a[i] - '0'; } else { result[i] = 0; } if (i < len_b) { result[i] += b[i] - '0'; } result[i] += carry; if (result[i] >= 10) { carry = 1; result[i] -= 10; } else { carry = 0; } } // 去除前导零 int i; for (i = MAX_LEN - 2; i >= 0; i--) { if (result[i] != 0) { break; } } for (int j = 0; j <= i; j++) { result[j] += '0'; } result[i+1] = '\0'; strrev(result); // 反转字符串 } // 减法函数 void subtract(char a[], char b[], char result[]) { int borrow = 0; int len_a = strlen(a); int len_b = strlen(b); // 对齐两个数的位数 for (int i = 0; i < MAX_LEN - 1; i++) { if (i < len_a) { result[i] = a[i] - '0'; } else { result[i] = 0; } if (i < len_b) { result[i] -= b[i] - '0'; } result[i] -= borrow; if (result[i] < 0) { borrow = 1; result[i] += 10; } else { borrow = 0; } } // 去除前导零 int i; for (i = MAX_LEN - 2; i >= 0; i--) { if (result[i] != 0) { break; } } for (int j = 0; j <= i; j++) { result[j] += '0'; } result[i+1] = '\0'; strrev(result); // 反转字符串 } // 乘法函数 void multiply(char a[], char b[], char result[]) { int len_a = strlen(a); int len_b = strlen(b); // 结果最多有 len_a + len_b 位 for (int i = 0; i < MAX_LEN - 1; i++) { result[i] = 0; } for (int i = 0; i < len_a; i++) { for (int j = 0; j < len_b; j++) { result[i+j] += (a[i] - '0') * (b[j] - '0'); } } // 对结果进行进位处理 for (int i = 0; i < MAX_LEN - 2; i++) { result[i+1] += result[i] / 10; result[i] %= 10; } // 去除前导零 int i; for (i = MAX_LEN - 2; i >= 0; i--) { if (result[i] != 0) { break; } } for (int j = 0; j <= i; j++) { result[j] += '0'; } result[i+1] = '\0'; strrev(result); // 反转字符串 } // 输出菜单 void print_menu() { printf("请选择功能:\n"); printf("1. A + B\n"); printf("2. A - B\n"); printf("3. A * B\n"); printf("4. 结束程序\n"); } // 清空输入缓冲区 void clear_input_buffer() { while (getchar() != '\n') {} } ``` 在编写代码的过程中,主要遇到了以下两个问题: 1. 数组越界问题 当输入的数字长度超过 40 位时,数组会发生越界,导致程序崩溃。为了解决这个问题,我们需要在定义数组时将长度设为 41,其中最后一位用来存储结束符 '\0'。 2. 输入缓冲区问题 在输入数字时,如果用户输入了非法字符(例如字母或符号),会导致程序出错。为了解决这个问题,我们可以在读取用户输入后,清空输入缓冲区,防止输入错误。 最终,我们得到了一个高精度整数运算程序,可以进行加、减、乘三个操作,满足题目要求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值