首页 文章 精选 留言 我的

精选列表

搜索[实践],共10001篇文章
优秀的个人博客,低调大师

缓存空间优化实践

作者:京东科技 董健 导读 缓存Redis,是我们最常用的服务,其适用场景广泛,被大量应用到各业务场景中。也正因如此,缓存成为了重要的硬件成本来源,我们有必要从空间上做一些优化,降低成本的同时也会提高性能。 下面以我们的案例说明,将缓存空间减少70%的做法。 场景设定 1、我们需要将POJO存储到缓存中,该类定义如下 public class TestPOJO implements Serializable { private String testStatus; private String userPin; private String investor; private Date testQueryTime; private Date createTime; private String bizInfo; private Date otherTime; private BigDecimal userAmount; private BigDecimal userRate; private BigDecimal applyAmount; private String type; private String checkTime; private String preTestStatus; public Object[] toValueArray(){ Object[] array = {testStatus, userPin, investor, testQueryTime, createTime, bizInfo, otherTime, userAmount, userRate, applyAmount, type, checkTime, preTestStatus}; return array; } public CreditRecord fromValueArray(Object[] valueArray){ //具体的数据类型会丢失,需要做处理 } } 2、用下面的实例作为测试数据 TestPOJO pojo = new TestPOJO(); pojo.setApplyAmount(new BigDecimal("200.11")); pojo.setBizInfo("XX"); pojo.setUserAmount(new BigDecimal("1000.00")); pojo.setTestStatus("SUCCESS"); pojo.setCheckTime("2023-02-02"); pojo.setInvestor("ABCD"); pojo.setUserRate(new BigDecimal("0.002")); pojo.setTestQueryTime(new Date()); pojo.setOtherTime(new Date()); pojo.setPreTestStatus("PROCESSING"); pojo.setUserPin("ABCDEFGHIJ"); pojo.setType("Y");  常规做法 System.out.println(JSON.toJSONString(pojo).length()); 使用JSON直接序列化、打印 length=284,这种方式是最简单的方式,也是最常用的方式,具体数据如下: {"applyAmount":200.11,"bizInfo":"XX","checkTime":"2023-02-02","investor":"ABCD","otherTime":"2023-04-10 17:45:17.717","preCheckStatus":"PROCESSING","testQueryTime":"2023-04-10 17:45:17.717","testStatus":"SUCCESS","type":"Y","userAmount":1000.00,"userPin":"ABCDEFGHIJ","userRate":0.002} 我们发现,以上包含了大量无用的数据,其中属性名是没有必要存储的。  改进1-去掉属性名 System.out.println(JSON.toJSONString(pojo.toValueArray()).length()); 通过选择数组结构代替对象结构,去掉了属性名,打印 length=144,将数据大小降低了50%,具体数据如下: ["SUCCESS","ABCDEFGHIJ","ABCD","2023-04-10 17:45:17.717",null,"XX","2023-04-10 17:45:17.717",1000.00,0.002,200.11,"Y","2023-02-02","PROCESSING"] 我们发现,null是没有必要存储的,时间的格式被序列化为字符串,不合理的序列化结果,导致了数据的膨胀,所以我们应该选用更好的序列化工具。  改进2-使用更好的序列化工具 //我们仍然选取JSON格式,但使用了第三方序列化工具 System.out.println(new ObjectMapper(new MessagePackFactory()).writeValueAsBytes(pojo.toValueArray()).length); 选取更好的序列化工具,实现字段的压缩和合理的数据格式,打印 length=92,空间比上一步又降低了40%。 这是一份二进制数据,需要以二进制操作Redis,将二进制转为字符串后,打印如下: ��SUCCESS�ABCDEFGHIJ�ABCD��j�6���XX��j�6����?`bM����@i��Q�Y�2023-02-02�PROCESSING 顺着这个思路再深挖,我们发现,可以通过手动选择数据类型,实现更极致的优化效果,选择使用更小的数据类型,会获得进一步的提升。  改进3-优化数据类型 在以上用例中,testStatus、preCheckStatus、investor这3个字段,实际上是枚举字符串类型,如果能够使用更简单数据类型(比如byte或者int等)替代string,还可以进一步节省空间。其中checkTime可以用Long类型替代字符串,会被序列化工具输出更少的字节。 public Object[] toValueArray(){ Object[] array = {toInt(testStatus), userPin, toInt(investor), testQueryTime, createTime, bizInfo, otherTime, userAmount, userRate, applyAmount, type, toLong(checkTime), toInt(preTestStatus)}; return array; } 在手动调整后,使用了更小的数据类型替代了String类型,打印 length=69  改进4-考虑ZIP压缩 除了以上的几点之外,还可以考虑使用ZIP压缩方式获取更小的体积,在内容较大或重复性较多的情况下,ZIP压缩的效果明显,如果存储的内容是TestPOJO的数组,可能适合使用ZIP压缩。 但ZIP压缩并不一定会减少体积,在小于30个字节的情况下,也许还会增加体积。在重复性内容较少的情况下,无法获得明显提升。并且存在CPU开销。 在经过以上优化之后,ZIP压缩不再是必选项,需要根据实际数据做测试才能分辨到ZIP的压缩效果。  最终落地 上面的几个改进步骤体现了优化的思路,但是反序列化的过程会导致类型的丢失,处理起来比较繁琐,所以我们还需要考虑反序列化的问题。 在缓存对象被预定义的情况下,我们完全可以手动处理每个字段,所以在实战中,推荐使用手动序列化达到上述目的,实现精细化的控制,达到最好的压缩效果和最小的性能开销。 可以参考以下msgpack的实现代码,以下为测试代码,请自行封装更好的Packer和UnPacker等工具: <dependency> <groupId>org.msgpack</groupId> <artifactId>msgpack-core</artifactId> <version>0.9.3</version> </dependency> public byte[] toByteArray() throws Exception { MessageBufferPacker packer = MessagePack.newDefaultBufferPacker(); toByteArray(packer); packer.close(); return packer.toByteArray(); } public void toByteArray(MessageBufferPacker packer) throws Exception { if (testStatus == null) { packer.packNil(); }else{ packer.packString(testStatus); } if (userPin == null) { packer.packNil(); }else{ packer.packString(userPin); } if (investor == null) { packer.packNil(); }else{ packer.packString(investor); } if (testQueryTime == null) { packer.packNil(); }else{ packer.packLong(testQueryTime.getTime()); } if (createTime == null) { packer.packNil(); }else{ packer.packLong(createTime.getTime()); } if (bizInfo == null) { packer.packNil(); }else{ packer.packString(bizInfo); } if (otherTime == null) { packer.packNil(); }else{ packer.packLong(otherTime.getTime()); } if (userAmount == null) { packer.packNil(); }else{ packer.packString(userAmount.toString()); } if (userRate == null) { packer.packNil(); }else{ packer.packString(userRate.toString()); } if (applyAmount == null) { packer.packNil(); }else{ packer.packString(applyAmount.toString()); } if (type == null) { packer.packNil(); }else{ packer.packString(type); } if (checkTime == null) { packer.packNil(); }else{ packer.packString(checkTime); } if (preTestStatus == null) { packer.packNil(); }else{ packer.packString(preTestStatus); } } public void fromByteArray(byte[] byteArray) throws Exception { MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(byteArray); fromByteArray(unpacker); unpacker.close(); } public void fromByteArray(MessageUnpacker unpacker) throws Exception { if (!unpacker.tryUnpackNil()){ this.setTestStatus(unpacker.unpackString()); } if (!unpacker.tryUnpackNil()){ this.setUserPin(unpacker.unpackString()); } if (!unpacker.tryUnpackNil()){ this.setInvestor(unpacker.unpackString()); } if (!unpacker.tryUnpackNil()){ this.setTestQueryTime(new Date(unpacker.unpackLong())); } if (!unpacker.tryUnpackNil()){ this.setCreateTime(new Date(unpacker.unpackLong())); } if (!unpacker.tryUnpackNil()){ this.setBizInfo(unpacker.unpackString()); } if (!unpacker.tryUnpackNil()){ this.setOtherTime(new Date(unpacker.unpackLong())); } if (!unpacker.tryUnpackNil()){ this.setUserAmount(new BigDecimal(unpacker.unpackString())); } if (!unpacker.tryUnpackNil()){ this.setUserRate(new BigDecimal(unpacker.unpackString())); } if (!unpacker.tryUnpackNil()){ this.setApplyAmount(new BigDecimal(unpacker.unpackString())); } if (!unpacker.tryUnpackNil()){ this.setType(unpacker.unpackString()); } if (!unpacker.tryUnpackNil()){ this.setCheckTime(unpacker.unpackString()); } if (!unpacker.tryUnpackNil()){ this.setPreTestStatus(unpacker.unpackString()); } } 场景延伸 假设,我们为2亿用户存储数据,每个用户包含40个字段,字段key的长度是6个字节,字段是分别管理的。 正常情况下,我们会想到hash结构,而hash结构存储了key的信息,会占用额外资源,字段key属于不必要数据,按照上述思路,可以使用list替代hash结构。 通过Redis官方工具测试,使用list结构需要144G的空间,而使用hash结构需要245G的空间(当50%以上的属性为空时,需要进行测试,是否仍然适用)     在以上案例中,我们采取了几个非常简单的措施,仅仅有几行简单的代码,可降低空间70%以上,在数据量较大以及性能要求较高的场景中,是非常值得推荐的。: • 使用数组替代对象(如果大量字段为空,需配合序列化工具对null进行压缩) • 使用更好的序列化工具 • 使用更小的数据类型 • 考虑使用ZIP压缩 • 使用list替代hash结构(如果大量字段为空,需要进行测试对比)

优秀的个人博客,低调大师

Tableau连接openGauss实践

目录 一、摘要 二、什么是Tableau? 三、安装Tableau 四、安装ODBC驱动 1、openGauss数据库 2、连接前置条件 3、Tableau连接openGauss方式一 4、Tableau连接openGauss方式二 一、摘要 Tableau可以连接到多种数据库,包括关系型数据库,如MySQL,Oracle,Microsoft SQL Server,PostgreSQL等,以及非关系型数据库,如Hadoop,Amazon Redshift,Google BigQuery等。Tableau可以从数据库中提取数据,并将其可视化,以便更好地理解和分析数据。本文将简单介绍一下Tableau如何连接openGauss数据库。 二、什么是Tableau? 1、首先我们先认识一下Tableau,Tableau 是一个可视化分析平台,它改变了我们使用数据解决问题的方式,使个人和组织能够充分利用自己的数据。Tableau 是一款能够帮助大家查看并理解数据的商业化智能软件。 2、Tableau的产品特点: 快速分析:在数分钟内完成数据连接和可视化。Tableau 比现有的其他解决方案快 10 到 100 倍。 大数据,任何数据:无论是电子表格、数据库还是 Hadoop 和云服务,任何数据都可以轻松探索。 自动更新:通过实时连接获取最新数据,或者根据制定的日程表获取自动更新。 简单易用:任何人都可以使用直观明了的拖放产品分析数据。无需编程即可深入分析。 智能仪表板:集合多个数据视图,进行更丰富的深入分析。数据可视化最佳做法等待您去体验。 瞬时共享:只需数次点击,即可发布仪表板,在网络和移动设备上实现实时共享。 适用对象:分析师、企业高管、IT相关人员等。 三、安装Tableau 在官网或者第三方平台下载(购买/试用版)并安装Tableau 软件(自定义安装——安装——完成——注册试用版),本文以“TableauDesktop”版本为例。 四、安装ODBC驱动 用户可以下载使用开源的ODBC驱动程序(windows版本)。将下载的ODBC驱动解压,选择64位的进行安装。 安装过程:以管理员身份运行,然后一路点“Next”,直到 Finish 即可。 五、连接openGauss数据库 1、openGauss数据库 openGauss 是一款全面友好开放,携手伙伴共同打造的企业级开源关系型数据库。openGauss采用木兰宽松许可证v2发行,提供面向多核架构的极致性能、全链路的业务、数据安全、基于AI的调优和高效运维的能力。openGauss深度融合华为在数据库领域多年的研发经验,结合企业级场景需求,持续构建竞争力特性。同时,openGauss也是一个开源、免费的数据库平台,鼓励社区贡献、合作。 openGauss社区官网:openGauss openGauss代码托管平台:openGauss: 一款高性能、高安全、高可靠的企业级开源关系型数据库。 2、连接前置条件 连接前置条件:openGauss数据库服务正常启动 切换用omm,使用以下命令启动openGauss: gs_om -t start [-h HOSTNAME] [-D dataDir] [--time-out=SECS] [--security-mode=MODE] [--cluster-number=None] [-l LOGFILE] 使用以下命令查询openGauss状态: gs_om -t status [-h HOSTNAME] [-o OUTPUT] [--detail] [--all] [-l LOGFILE] 3、Tableau连接openGauss方式一 1)启动Tableau主程序 2)在连接页面的“到服务器”模块选择“PostgreSQL” 3)在登录页面填写数据库连接信息后单击登录 4)成功登录后,显示界面如下(可将左侧加载出来的数据表拖拽到右侧界面) 5)从上图“数据源”页切换到“工作表”页面,可以进行字段“拖拽”,形成图表等。 4、Tableau连接openGauss方式二 1)配置ODBC数据源 依次打开:控制面板-管理工具—ODBC 数据源(64 位) 添加—选择“PostgreSQL ANSI(x64)” 配置数据库信息,点击Test 测试连接是否成功。提示成功后,点击保存(Save)。 2)启动Tableau主程序,连接上文配置好的“openGauss”数据源,如下图: 3)成功登录后,显示界面如下(可将左侧加载出来的数据表拖拽到右侧界面)。 4)从上图“数据源”页切换到“工作表”页面,可以进行字段“拖拽”,形成图表等。 以上就是本期内容, 欢迎交流、学习!

优秀的个人博客,低调大师

前端精准测试实践

作者:京东云质量部 背景 随着前端技术发展,已经转变为数据绑定为主流的框架方式,与后端服务一样,前端代码实现也会涉及相互依赖,引用这些场景,那么应该如何准确的评估前端代码改动的影响范围?依赖开发评估?依靠经验评估?或者直接前端自动化全回归?手工测试全回归?显然以上的策略都不是最优策略,本文叙述了通过对前端代码进行静态分析,找到改动文件影响的功能范围,从实现了一种前端精准测试的思路。 如何进行精准分析 前端对外可直接感知的就是页面,最终目标是要确定影响哪个功能。整个前端精准测试划分为4步: 第一步,确定影响的页面。 第二步,确定影响的功能。 第三步,根据分析结果,找到对应的自动化用例集合,并触发运行 第四步,对比前端代码增量覆盖率,确认改动覆盖完成 前端页面与路由直接相关,从路由入手,建立路由与展示页面的关系,再依据入口文件的import关系,建立前端代码文件依赖树,再通过git diff对比找到改动的文件,从而反查到影响的前端页面。 精准分析实现 设计思路 解析路由文件,建立路由文件中每个菜单的依赖树,从而根据改动文件反查影响页面 实现逻辑 鉴于上述设计思路,结合目前技术支撑现状及快速实验的目标,开发了前端精准分析的第一版方案。 (路由文件分析暂时未找到规律,且实际场景中,路由文件变更不会频繁,维护成本不高,所以此步骤暂由人工维护) 关键逻辑:文件依赖树的分析及存储 应用 webhook自动触发分析 在代码平台上配置webhook触发分析接口,即可实现代码提交,自动触发分析 平台手动触发分析 规划 目前平台仅仅实现了前端精准测试4步中第一步的80%(路由与入口文件的关联还未实现自动分析),推荐仅仅到了页面级别,还未达到按钮级别,此处还需要再研究一下前端开发的相关技术,找到自动解析路由的方法。第二步计划尝试借助istanbul前端覆盖率工具,做一下增量覆盖率对比,保证手动回归可覆盖改动。

优秀的个人博客,低调大师

编写 Dockerfile 最佳实践

官方仓库虽然有数十万计的免费镜像,但大多数无法直接满足公司业务需求,这就需要我们自己去定制镜像了。 Docker通过Dockerfile自动构建镜像,Dockerfile是一个包含用于组建镜像的文本文件,由一条一条的指令组成。 这里,给你提供5点编写建议,可帮助你编写高效易用的Dockerfile。 1、减少镜像层 一次RUN指令形成新的一层,尽量Shell命令都写在一行,减少镜像层。 例如: FROM centos:7 MAINTAINER www.aliangedu.cn RUN yuminstallepel-release-y RUN yuminstall-y gcc gcc-c++ make -y RUN wgethttp://docs.php.net/distributions/php-5.6.36.tar.gz RUN tar zxf php-5.6.36.tar.gz RUN cd php-5.6.36 RUN ./configure--prefix=/usr/local/php RUN make -j4 RUN makeinstall EXPOSE9000 CMD ["php-fpm"] 应该写成: FROM centos:7 MAINTAINER www.aliangedu.cn RUN yuminstallepel-release-y && \ yuminstall-y gcc gcc-c++ make RUN wgethttp://docs.php.net/distributions/php-5.6.36.tar.gz && \ tar zxf php-5.6.36.tar.gz && \ cd php-5.6.36&& \ ./configure--prefix=/usr/local/php && \ make -j4&& makeinstall EXPOSE9000 CMD ["php-fpm"] 结果:12层 -> 6层 2、优化镜像大小:清理无用数据 一次RUN形成新的一层,如果没有在同一层删除,无论文件是否最后删除,都会带到下一层,所以要在每一层清理对应的残留数据,减小镜像大小。 FROM centos:7 MAINTAINER www.aliangedu.cn RUN yum install epel-release -y&& \ yum install -ygcc gcc-c++makegd-devel libxml2-devel \ libcurl-devel libjpeg-devel libpng-devel openssl-devel \ libmcrypt-devel libxslt-devel libtidy-devel autoconf \ iproute net-tools telnet wget curl && \ yum cleanall&& \ rm -rf /var/cache/yum/* RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz && \ tar zxf php-5.6.36.tar.gz && \ cdphp-5.6.36&& \ ./configure --prefix=/usr/local/php \ make-j4&&makeinstall && \ cd/ && rm -rf php* 至少能节省几十M,甚至几百M。 3、减少网络传输时间 最好在内部有一个存放软件包的地方,类似于上述的PHP官方下载地址:http://docs.php.net/distributions/php-5.6.36.tar.gz,如果用到maven构建这样的操作,同时也更改为私有maven仓库,减少网络传输时间,提高镜像构建速度。 4、多阶段进行镜像构建 如果运行一个项目,根据咱们上面的做法,是直接把代码拷贝到基础镜像里,如果是一个需要预先代码编译的项目呢?例如JAVA语言,如何代码编译、部署在一起完成呢! 上面做法需要事先在一个Dockerfile构建一个基础镜像,包括项目运行时环境及依赖库,再写一个Dockerfile将项目拷贝到运行环境中,有点略显复杂了。 像JAVA这类语言如果代码编译是在Dockerfile里操作,还需要把源代码构建进去,但实际运行时只需要构建出的包,这种把源代码放进去有一定安全风险,并且也增加了镜像体积。 为了解决上述问题,Docker 17.05开始支持多阶段构建(multi-stage builds),可以简化Dockerfile,减少镜像大小。 例如,构建JAVA项目镜像: #gitclonehttps://github.com/lizhenliang/tomcat-java-demo #cdtomcat-java-demo #vi Dockerfile FROM maven AS build ADD ./pom.xml pom.xml ADD ./src src/ RUN mvn clean package FROM lizhenliang/tomcat RUN rm -rf /usr/local/tomcat/webapps/ROOT COPY --from=build target/*.war /usr/local/tomcat/webapps/ROOT.war #docker build -t demo:v1 . #docker container run -d -v demo:v1 首先,第一个FROM 后边多了个 AS 关键字,可以给这个阶段起个名字。 然后,第二部分FROM用的我们上面构建的Tomcat镜像,COPY关键字增加了—from参数,用于拷贝某个阶段的文件到当前阶段。这样一个Dockerfile就都搞定了。 5、选择小的基础镜像 选择原则一:追求镜像小,可使用Alpine镜像,Alpine是一个轻量级的Linux发行版,镜像仅有5.6MB,构建出的镜像也很小,但其采用MuslLibc,相比Glibc兼容性市面主流技术较差,需进一步测试。 选择原则二:追求稳定及使用习惯,使用CentOS镜像。 选择原则三:追求稳定及软件包新版,使用Ubuntu/Debian镜像,软件包新版发布上线快。 小结:镜像小有很多好处,例如快速部署、快速回滚。减少服务中断时间,同时镜像仓库占用磁盘空间也少了。

优秀的个人博客,低调大师

PostgreSQL jsonpath使用实践

jsonpath是用来解析json数据的工具,类似于xpath,jsonpath可以解析十分复杂的json数据。 PostgreSQL json发展历史: PostgreSQL从9.2开始就支持json数据类型,但是由于解析json数据的性能很差,导致并不受大家青睐,而是选择使用nosql数据库代替。于是从pg9.4开始支持了jsonb数据类型,相较于json类型,jsonb由于并不需要每次使用时都去进行解析,因此性能提升很多,都是还支持索引查询等。 而从pg12开始对于json的支持更加强大:sql 2016的sql/json标准有15条, PG 12 实现了14条, 远远超过oracle(18c 11/15), mysql(8.0.4 5/15), sqlserver(2017 2/15)最新版本。 同时在pg12中引入了jsonpath类型,以及一系列相关的函数,使得json数据的查询性能更进一步,功能也愈发强大。 JSONPATH语法: JSONpath 函数表达式语法如下: 点号 . 表示引用 Json 数据的元素 方括号 [] 表示引用数组元素 Json 数据中的数组元素下标从0开始 JSONpath中的变量如下: $ 符号表示要查询的Json文本的变量 $varname 表示指定变量 @ 指在 filter 表达式中表示当前路径元素的变量 JSONPATH使用举例: 简单查询: bill@bill=>SELECT jsonb_path_query_array('[1,2,3,4,5]', '$[*] ? (@ > 3)'); jsonb_path_query_array ------------------------ [4, 5] (1 row) 创建测试表: CREATE TABLE house(js jsonb); INSERT INTO house VALUES ('{ 'address': { 'city':'Moscow', 'street': 'Ulyanova, 7A' }, 'lift': false, 'floor': [ { 'level': 1, 'apt': [ {'no': 1, 'area': 40, 'rooms': 1}, {'no': 2, 'area': 80, 'rooms': 3}, {'no': 3, 'area': 50, 'rooms': 2} ] }, { 'level': 2, 'apt': [ {'no': 4, 'area': 100, 'rooms': 3}, {'no': 5, 'area': 60, 'rooms': 2} ] } ] }'); 查询: bill@bill=>select jsonb_pretty(js) from house ; jsonb_pretty ---------------------------------- { + 'lift': false, + 'floor': [ + { + 'apt': [ + { + 'no': 1, + 'area': 40, + 'rooms': 1 + }, + { + 'no': 2, + 'area': 80, + 'rooms': 3 + }, + { + 'no': 3, + 'area': 50, + 'rooms': 2 + } + ], + 'level': 1 + }, + { + 'apt': [ + { + 'no': 4, + 'area': 100,+ 'rooms': 3 + }, + { + 'no': 5, + 'area': 60, + 'rooms': 2 + } + ], + 'level': 2 + } + ], + 'address': { + 'city': 'Moscow', + 'street': 'Ulyanova, 7A'+ } + } (1 row) 该数据的层次结构如下图: 看上去该数据层次挺复杂的,但是实际中可能数据层次远比这个复杂的多,那么我们看看如何使用jsonpath来进行查询的: bill@bill=>SELECT jsonb_path_query_array(js, '$.floor[0, 1].apt[1 to last]') FROM house; jsonb_path_query_array ----------------------------------------------------------------------------------------------------------- [{'no': 2, 'area': 80, 'rooms': 3}, {'no': 3, 'area': 50, 'rooms': 2}, {'no': 5, 'area': 60, 'rooms': 2}] (1 row) 而如果不用jsonpath的话,我们可能需要这么写: bill@bill=>SELECT jsonb_agg(apt) FROM ( bill(# SELECT apt->generate_series(1, jsonb_array_length(apt) - 1) FROM ( bill(# SELECT js->'floor'->unnest(array[0, 1])->'apt' FROM house bill(# ) apts(apt) bill(# ) apts(apt); jsonb_agg ----------------------------------------------------------------------------------------------------------- [{'no': 2, 'area': 80, 'rooms': 3}, {'no': 3, 'area': 50, 'rooms': 2}, {'no': 5, 'area': 60, 'rooms': 2}] (1 row) 相比之下,使用jsonpath相关的函数查询简便太多了。 又比如,我们需要判断json数据中是否包含某个值,可以这样: bill@bill=>SELECT jsonb_path_exists(js, '$.** ? (@ == 'Moscow')') FROM house; jsonb_path_exists ------------------- t (1 row) 而如果不使用jsonpath呢? bill@bill=>WITH RECURSIVE t(value) AS ( bill(# SELECT * FROM house UNION ALL ( bill(# SELECT COALESCE(kv.value, e.value) AS value bill(# FROM t bill(# LEFT JOIN LATERAL jsonb_each ( bill(# CASE WHEN jsonb_typeof(t.value) = 'object' THEN t.value bill(# ELSE NULL END bill(# ) kv ON true bill(# LEFT JOIN LATERAL jsonb_array_elements ( bill(# CASE WHEN jsonb_typeof(t.value) = 'array' THEN t.value bill(# ELSE NULL END bill(# ) e ON true bill(# WHERE kv.value IS NOT NULL OR e.value IS NOT NULL bill(# ) bill(# ) SELECT EXISTS (SELECT 1 FROM t WHERE value = ''Moscow''); exists -------- t (1 row) 那么如何使用jsonpath进行数据过滤呢?已上面这张表为例,我们查询apt.no大于3的数据: bill@bill=>SELECT jsonb_path_query(js, '$.floor.apt.no ? (@>3)') FROM house; jsonb_path_query ------------------ 4 5 (2 rows) 同样,jsonpath也支持索引的使用: bill@bill=>CREATE INDEX ON house USING gin (js); CREATE INDEX bill@bill=>SET ENABLE_SEQSCAN TO OFF; SET bill@bill=>EXPLAIN (COSTS OFF) SELECT * FROM house bill-# WHERE js @? '$.floor[*].apt[*] ? (@.rooms == 3)'; QUERY PLAN -------------------------------------------------------------------------------- Bitmap Heap Scan on house Recheck Cond: (js @? '$.'floor'[*].'apt'[*]?(@.'rooms' == 3)'::jsonpath) -> Bitmap Index Scan on house_js_idx Index Cond: (js @? '$.'floor'[*].'apt'[*]?(@.'rooms' == 3)'::jsonpath) (4 rows) 除此之外,jsonpath支持了20多种相关的函数,是不是十分强大,赶快用起来吧! postgres=# \df *.*json*path* List of functions Schema | Name | Result data type | Argument data types | Type ------------+------------------------------+------------------+------------------------------------------------------------------------------------------ -+------ pg_catalog | gin_consistent_jsonb_path | boolean | internal, smallint, jsonb, integer, internal, internal, internal, internal | func pg_catalog | gin_extract_jsonb_path | internal | jsonb, internal, internal | func pg_catalog | gin_extract_jsonb_query_path | internal | jsonb, internal, smallint, internal, internal, internal, internal | func pg_catalog | gin_triconsistent_jsonb_path | 'char' | internal, smallint, jsonb, integer, internal, internal, internal | func pg_catalog | json_extract_path | json | from_json json, VARIADIC path_elems text[] | func pg_catalog | json_extract_path_text | text | from_json json, VARIADIC path_elems text[] | func pg_catalog | jsonb_delete_path | jsonb | jsonb, text[] | func pg_catalog | jsonb_extract_path | jsonb | from_json jsonb, VARIADIC path_elems text[] | func pg_catalog | jsonb_extract_path_text | text | from_json jsonb, VARIADIC path_elems text[] | func pg_catalog | jsonb_path_exists | boolean | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false | func pg_catalog | jsonb_path_exists_opr | boolean | jsonb, jsonpath | func pg_catalog | jsonb_path_exists_tz | boolean | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false | func pg_catalog | jsonb_path_match | boolean | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false | func pg_catalog | jsonb_path_match_opr | boolean | jsonb, jsonpath | func pg_catalog | jsonb_path_match_tz | boolean | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false | func pg_catalog | jsonb_path_query | SETOF jsonb | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false | func pg_catalog | jsonb_path_query_array | jsonb | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false | func pg_catalog | jsonb_path_query_array_tz | jsonb | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false | func pg_catalog | jsonb_path_query_first | jsonb | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false | func pg_catalog | jsonb_path_query_first_tz | jsonb | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false | func pg_catalog | jsonb_path_query_tz | SETOF jsonb | target jsonb, path jsonpath, vars jsonb DEFAULT '{}'::jsonb, silent boolean DEFAULT false | func pg_catalog | jsonpath_in | jsonpath | cstring | func pg_catalog | jsonpath_out | cstring | jsonpath | func pg_catalog | jsonpath_recv | jsonpath | internal | func pg_catalog | jsonpath_send | bytea | jsonpath | func (25 rows) 参考链接: https://github.com/digoal/blog/blob/master/202010/20201013_01.md http://www.postgres.cn/docs/12/functions-json.html http://www.postgres.cn/docs/12/datatype-json.html#DATATYPE-JSONPATH

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册