首页 文章 精选 留言 我的

精选列表

搜索[响应],共10006篇文章
优秀的个人博客,低调大师

10 亿条数据秒级响应

坦白讲,每次看性能测试排行榜,我都会下意识地先找找 Apache Doris 在哪个位置。 这次打开 JSONBench 的榜单,心情一如既往的期待加紧张。 好在结果让我松了一口气:默认配置下就能排到第三,仅次于维护方 ClickHouse 的两个版本。 不过,Doris 只能止步于此了吗?经过一系列优化后,查询时长能不能再缩短点?和 ClickHouse 的差距在哪里? 调优前后对比图镇楼,至于调优的具体思路,请一起往下看吧。 Apache Doris 排名 (Default) Apache Doris 排名 (Unofficial Tuned) JSONBench 简介 JSONBench 是一个为 JSON 数据而生的数据分析 Benchmark,简单来说,它由 10 亿条来自真实生产环境的 JSON 数据、5 个针对 JSON 构造的特定 SQL 查询组成,旨在对比各个数据库系统对半结构化数据的处理能力。目前榜单包括 ClickHouse、MongoDB、Elasticsearch、DuckDB、PostgreSQL 等知名数据库系统,截至目前,Doris 的性能表现是 Elasticsearch 的 2 倍,是 PostgreSQL 的 80 倍。 JSONBench 官网地址:jsonbench.com 不仅在性能上 Apache Doris 领先其他同类产品,在数据集相同的情况下,Apache Doris 的存储占用是 Elasticsearch 的 1/2、PostgreSQL 的 1/3。 JSONBench 测试具体流程:首先在数据库中创建一张名为 Bluesky 的表,并导入十亿条真实的用户行为日志数据。测试过程中,每个查询重复执行三次,并且在每次查询前清空操作系统的 Page Cache,以模拟冷热查询的不同场景。最终,通过综合计算各查询的执行耗时得出数据库的性能排名。 在这个测试中,Apache Doris 使用了 Variant 数据类型来存储 JSON 数据,默认的建表 Schema 如下: CREATE TABLE bluesky ( `id` BIGINT NOT NULL AUTO_INCREMENT, `data` variant NOT NULL ) DISTRIBUTED BY HASH(id) BUCKETS 32 PROPERTIES ("replication_num"="1"); Variant 是 Apache Doris 2.1 中引入一种新的数据类型 ,它可以存储半结构化 JSON 数据,并且允许存储包含不同数据类型(如整数、字符串、布尔值等)的复杂数据结构,而无需在表结构中提前定义具体的列。Variant 类型特别适用于处理复杂的嵌套结构,而这些结构可能随时会发生变化。在写入过程中,该类型可以自动根据列的结构、类型推断列信息,动态合并写入的 schema,并通过将 JSON 键及其对应的值存储为列和动态子列。 Apache Doris Variant 类型详情 调优思路与原理 JSONBench 榜单排名依据各个数据库系统在默认配置下的性能数据,那么能否通过调优,让 Apache Doris 进一步释放性能潜力,实现更好的性能效果呢? 01 环境说明 测试机器:AWS M6i.8xlarge(32C128G); 操作系统:Ubuntu24.04; Apache Doris: 3.0.5; 02 Schema 结构化处理 由于 JSONBench 特定查询中涉及到的 JSON 数据都是固定的提取路径,换言之,半结构化数据的 Schema 是固定的,因此,我们可以借助生成列,将常用的字段提取出来,实现半结构化数据和结构化数据结合的效果。类似的高频访问的 JSON 路径或者需要计算的表达式,都可以使用该优化思路,添加对应的生成列来实现查询加速。 查看 JSONBench 查询 Apache Doris 生成列详情 CREATE TABLE bluesky ( kind VARCHAR(100) GENERATED ALWAYS AS (get_json_string(data, '$.kind')) NOT NULL, operation VARCHAR(100) GENERATED ALWAYS AS (get_json_string(data, '$.commit.operation')) NULL, collection VARCHAR(100) GENERATED ALWAYS AS (get_json_string(data, '$.commit.collection')) NULL, did VARCHAR(100) GENERATED ALWAYS AS (get_json_string(data,'$.did')) NOT NULL, time DATETIME GENERATED ALWAYS AS (from_microsecond(get_json_bigint(data, '$.time_us'))) NOT NULL, `data` variant NOT NULL ) DUPLICATE KEY (kind, operation, collection) DISTRIBUTED BY HASH(collection, did) BUCKETS 32 PROPERTIES ("replication_num"="1"); 除了可以减少查询时提取数据的开销,还可以用展平出来的列作为分区列,使得数据分布更均衡。 需要注意的是,查询的 SQL 语句也要改为使用展平列的版本: // JSONBench 原始查询: SELECT cast(data['commit']['collection'] AS TEXT ) AS event, COUNT(*) AS count FROM bluesky GROUP BY event ORDER BY count DESC; SELECT cast(data['commit']['collection'] AS TEXT ) AS event, COUNT(*) AS count, COUNT(DISTINCT cast(data['did'] AS TEXT )) AS users FROM bluesky WHERE cast(data['kind'] AS TEXT ) = 'commit' AND cast(data['commit']['operation'] AS TEXT ) = 'create' GROUP BY event ORDER BY count DESC; SELECT cast(data['commit']['collection'] AS TEXT ) AS event, HOUR(from_microsecond(CAST(data['time_us'] AS BIGINT))) AS hour_of_day, COUNT(*) AS count FROM bluesky WHERE cast(data['kind'] AS TEXT ) = 'commit' AND cast(data['commit']['operation'] AS TEXT ) = 'create' AND cast(data['commit']['collection'] AS TEXT ) IN ('app.bsky.feed.post', 'app.bsky.feed.repost', 'app.bsky.feed.like') GROUP BY event, hour_of_day ORDER BY hour_of_day, event; SELECT cast(data['did'] AS TEXT ) AS user_id, MIN(from_microsecond(CAST(data['time_us'] AS BIGINT))) AS first_post_ts FROM bluesky WHERE cast(data['kind'] AS TEXT ) = 'commit' AND cast(data['commit']['operation'] AS TEXT ) = 'create' AND cast(data['commit']['collection'] AS TEXT ) = 'app.bsky.feed.post' GROUP BY user_id ORDER BY first_post_ts ASC LIMIT 3; SELECT cast(data['did'] AS TEXT ) AS user_id, MILLISECONDS_DIFF(MAX(from_microsecond(CAST(data['time_us'] AS BIGINT))),MIN(from_microsecond(CAST(data['time_us'] AS BIGINT)))) AS activity_span FROM bluesky WHERE cast(data['kind'] AS TEXT ) = 'commit' AND cast(data['commit']['operation'] AS TEXT ) = 'create' AND cast(data['commit']['collection'] AS TEXT ) = 'app.bsky.feed.post' GROUP BY user_id ORDER BY activity_span DESC LIMIT 3; // 使用展平列改写的查询: SELECT collection AS event, COUNT(*) AS count FROM bluesky GROUP BY event ORDER BY count DESC; SELECT collection AS event, COUNT(*) AS count, COUNT(DISTINCT did) AS users FROM bluesky WHERE kind = 'commit' AND operation = 'create' GROUP BY event ORDER BY count DESC; SELECT collection AS event, HOUR(time) AS hour_of_day, COUNT(*) AS count FROM bluesky WHERE kind = 'commit' AND operation = 'create' AND collection IN ('app.bsky.feed.post', 'app.bsky.feed.repost', 'app.bsky.feed.like') GROUP BY event, hour_of_day ORDER BY hour_of_day, event; SELECT did AS user_id, MIN(time) AS first_post_ts FROM bluesky WHERE kind = 'commit' AND operation = 'create' AND collection = 'app.bsky.feed.post' GROUP BY user_id ORDER BY first_post_ts ASC LIMIT 3; SELECT did AS user_id, MILLISECONDS_DIFF(MAX(time),MIN(time)) AS activity_span FROM bluesky WHERE kind = 'commit' AND operation = 'create' AND collection = 'app.bsky.feed.post' GROUP BY user_id ORDER BY activity_span DESC LIMIT 3; 03 Page Cache 调整 调整查询语句后,开启 profile,执行完整的查询测试: set enable_profile=true; 进入 FE 8030 端口的 Web 页面,找到相关 profile 进行分析,此时发现 SCAN Operator 中的 Page Cache 命中率较低,导致热读测试过程中存在一部分冷读操作。 - CachedPagesNum: 1.258K (1258) - TotalPagesNum: 7.422K (7422) 这种情况通常是由于 Page Cache 容量不足,无法完整缓存 Bluesky 表中的数据。建议在 be.conf 中添加配置项 storage_page_cache_limit=60%,将 Page Cache 的大小从默认的内存总量的 20% 提升至 60%。重新运行测试后,可以观察到冷读问题已得到解决。 - CachedPagesNum: 7.316K (7316) - TotalPagesNum: 7.316K (7316) 04 最大化并行度 为了进一步挖掘 Doris 的性能潜力,可以将 Session 变量中的parallel_pipeline_task_num设为 32,因为本次 Benchmark 测试机器m6i.8xlarge为 32 核,所以我们将并行度设置为 32 以最大程度发挥 CPU 的计算能力。 // 单个 Fragment 的并行度 set global parallel_pipeline_task_num=32; 调优结果 经过上述对 Schema、Query、内存限制、CPU 等参数的调整,我们对比了调优前后 Doris 的性能表现以及一些其他数据库系统的成绩,有如下结果: 可以看到,对比调优前的 Doris,调优后 Doris 查询整体耗时降低了 74%,对比原榜单第一的 ClickHouse 产品实现了 39% 的领先优势。 总结与展望 通过对 Schema 的结构化处理、查询语句的优化、缓存配置的调整以及并行参数的设置,Apache Doris 整体查询耗时显著下降,并超越 ClickHouse。 在默认设置下,Doris 在 10 亿条 JSON 的查询耗时与 ClickHouse 仍有数秒的差异。然而,依托于 Doris 在 JSON 处理、Variant 类型支持及生成列等能力的加持,经调优后,其半结构化数据处理性能获得了进一步显著提升,并在同类数据库中表现出明显的领先优势。 未来,Apache Doris 将继续打磨在半结构化领域的数据处理能力,为用户带来更加优质、高效的分析体验,包括: 优化 Variant 类型稀疏列的存储空间,支持万列以上的子列; 优化万列大宽表的内存占用; 支持 Variant 子列根据列名的 Pattern 自定义类型、索引等。 推荐阅读 Apache Doris 针对半结构化数据分析的解决方案及典型场景 揭秘 Variant 数据类型:灵活应对半结构化数据,JSON 查询提速超 8 倍,存储空间节省 65% 欢迎扫码并备注 “半结构化” 加入 Apache Doris 社区专项交流群,免费领取 100+ 企业实践案例集和 Doris x AI 更多资料、获取技术帮助、了解最新动态,并与更多开发者和用户互动。

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

Redis淘汰+过期双向保证高可用,单线程如何做到快速响应?

2 --> 前言 redis和数据相比除了他们的结构性颠覆以外!还有他们存储位置也是不相同。传统数据库将数据存储在硬盘上每次数据操作都需要IO而Redis是将数据存储在内存上的。这里稍微解释下IO是啥意思。IO就是输入流输出流方式将数据在硬盘和内存之间进行交互!而redis直接在内存上就剩下了IO操作。这也是redis快的原因之一吧 内存相对于硬盘来说很宝贵。我们平时的电脑也是硬盘是内存的几百倍。既然内存很宝贵而redis又将数据存储在内存上那么redis肯定不能肆无忌惮的进行存储 。这就需要redis和开发者们作出相应的优化 首先redis在配置文件(redis.conf)中通过maxmemory参数指定redis 设置整个对内存的支配大小! 其次就是要求我们开发者在想redis中填值的时候根据自己的需求设置相应的key过期时间。这样不必要的数据就会被redis过期驱逐策略清除。从而节省内存供别人使用 内存分配 maxmemory 指定大小。在redis.conf配置文件中可以直接指定。他的单位时byte。 上图中注释部分是给大家的解释,实际中#配置需要换行哈!!!友情提示 另外我们可以连接上redis通过config命令来设置 两种方式都可以设置,前者是全局设置重启之后仍然有效!后者是临时设置重启之后就会重新加载redis.conf中的配置。 键过期 上面我们从redis本身角度出发设置了内存限制,这样不用担心他们吞噬系统内存!下面就需要我们开发者设计角度约束自己了。 设置过期时间 expire key time 设置过期时间默认单位时S 。 然后通过ttl 命令可以查看剩余过期时间 经过多次执行ttl能够观察到剩余时间在不断的减少!当减少到0的时候就被给驱逐策略驱逐!注意这里说的是驱逐策略驱逐并不是意味着立马被删除 更新过期时间 del key 直接将key删除了那么该key对应的过期自然也就不存在了!这种情况笔者也算作是更新过期时间 set getset等命令重新设置key、value方式会覆盖过期时间 , 直接被覆盖成-1 set 、 getset包括del严格意义是覆盖过期时间。真正做到更新过期时间的还是expire .在expire是以最新为准的! 上面其实都修改了key才会引发原本的过期时间失效的!因为此key非彼key 。 但是append、incr 等命令是改变值这种命令是不会影响到原来的过期设置的 淘汰策略 根据上面配置我们可以将我们的redis最大内存设置为1MB , 设置大小随便最好能小点。然后我们通过Java小程序不断向redis中填充。最终当内存不够使用时就会报错 报错就是因为内存满了,新增的key被redis拒绝了!不仅仅是新增的被拒绝,就算此时我们想改变已经在redis中的key的值也是不可用的 public static void main(String[] args) { Jedis jedis = new Jedis("39.102.60.114", 6379); jedis.auth("Qq025025"); Integer index = 1; while (true) { String uuid = UUID.randomUUID().toString(); jedis.set(index.toString(), uuid); System.out.println(index++); } } 不管是append 还是set 全都是报OOM command not allowed when used memory > maxmemory 。代码中打印和redis键个数一致;说明我们默认的淘汰策略是直接拒绝 总结下来就是:当redis内存被使用满了后,任何的写操作都会被拒绝! 当没有足够内存时难道就这么直接拒绝吗?上面也提到了需要我们程序员自己根据需求设置键过期已释放内存供其他有需要的key使用!那么设置了过期key之后这些key又是怎么被清除的呢? 这就牵扯出我们的淘汰策略 volatile-lru【最近很少使用】 当内存告警时redis会将近期很少使用且设置了过期时间的key剔除出去,即使该key还没有到过期时间。如果没有符合的key也就是执行之后内存仍然不足时将会和默认淘汰策略noeviction抛出一样的错误OOM command not allowed when used memory > maxmemory 首先我们在redis.conf中配置我们最近很少使用策略. maxmemory-policy volatile-lru 。 然后重启我们的redis服务 。重启之后flushall清空所有数据,我们在通过上面的Java程序重新生成下数据! Java程序中我们设置前100个key添加过期时间 public static void main(String[] args) { Jedis jedis = new Jedis("39.102.60.114", 6379); jedis.auth("Qq025025"); Integer index = 1; while (true) { String uuid = UUID.randomUUID().toString(); if (index < 100) { jedis.setex(index.toString(),360, uuid); } else { jedis.set(index.toString(), uuid); } System.out.println(index++); } } 简单分析下为什么程序计数器大于redis库中的key数量!就是因为我们为前100设置了过期时间。当内存不足时redis就会将当前设置了过期时间的key中最近最少使用的key进行剔除!所以我们计数器会大于键数量。因为有部分键被清除了!我们获取前100的key都是null , 说明被删除了! 那么为什么本次计数器不是比上次多100 。 那是因为我们每次存储进来的是uuid, 所占长度都不是固定的。还有本身淘汰策略也是占用内存的 策略总结 上面演示了最近最少使用的淘汰策略!除此之外还有其他的策略 noeviction:拒绝写请求,正常提供读请求,这样可以保证已有数据不会丢失(默认策略); 2. volatile-lru:尝试淘汰设置了过期时间的key,虽少使用的key被淘汰,没有设置过期时间的key不会淘汰; 3. volatile-ttl:跟volatile-lru几乎一样,但是他是使用的key的ttl值进行比较,最先淘汰ttl最小的key; 4. volatile-random:其他同上,唯一就是他通过很随意的方式随机选择淘汰key集合中的key; 5. allkeys-lru:区别于volatile-lru的地方就是淘汰目标是全部key,没设置过期时间的key也不能幸免; 6. allkeys-random:这种方式同上,随机的淘汰所有的key。 使用哪种淘汰策略需要我们结合自己的项目场景来配合使用!!! 过期删除 上面我们从【键过期】、【淘汰策略】两个角度分析了redis 。 仅仅这两方面还没有完全高效使用内存!淘汰策略是濒临内存不足时触发。那么当设置了过期时间的键真正到了过期时间而此时内存尚够使用?这种场景是不是需要将过期键删除呢? 因为redis是单线程,那么在键过期的时候如何不影响对外服务的同时清除过期键呢?答案是【不行】。严格意义是无法解决的因为单线程同时只能做一件事!既然无法解决那么我们可以达到一种协调状态!如果同一时刻出现一个过期键那么清除键很快这时候阻塞外部服务的时间很短可能毫秒级设置纳秒级! 但是如何同一时间发生上万键过期,如果想要删除上万键那肯定需要花费一定时间这时候就会阻塞对外服务!这肯定是不能接受的,阻塞时间过长会导致客户端连接超时报错的。这在并发场景下更是无法接受的!所以redis如何应对同一时间过多数据过期的场景,他的删除过期键的方法略有不同! 定时清除 针对每个过期键设置一个定时器,在过期时就会进行清理该键! 该做法能够做到数据实时被清理从而保证内存不会被长期占用!提高了内存的使用率! 但是问题也随之而来,每一个key需要设置一个定时器进行跟踪。redis这里笔者猜测应该是启用另外线程来进行定时跟踪!这里有清除的还请帮忙解答下? 当同一时间过期key很多的时候!我们的CPU就需要不断的执行这些定时器从而导致CPU资源紧张。最终会影响到redis服务的性能 定期清除 定期删除就是上面我们图示效果,redis会每隔100ms执行一次定时器,定时器的任务就是随机抽取20个设置过期的key 。 判断是否进行清除。上面图示中说明中写错了不是10S , 而是每隔100ms 。请原谅我的粗心!!! 定期删除和定时删除作用是相反的!定期删除是将key集中进行处理同时为了保证服务的高可用在处理时加入的时间限制。每次执行总时长不能超过25ms 。 也就是说对于客户端来说服务端的延迟不会超过25ms 。 他的优点就是不需要CPU频繁的进行操作key清除!因为他是定期进行清除所以就会导致一部分数据没有来得及清除从而导致内存使用上会被一直占用! 惰性清除 关于惰性删除我们在平时开发中也经常使用这种方式!当数据过期时redis并不急着去清除这些数据,而是等到该key被再次请求时进行删除!这样在最终效果上是没有问题的。 优点和定期清除一样他保证了CPU不必频繁的进行切换!但是缺点也很明显会导致很多已经过期的key仍然在redis中。 惰性清除+定期清除 我们开头说过了既要高可用又要实时清理过期key 这是无法做到的!既然无法做到我们就需要在CPU和内存中间做一个权衡!redis内部是使用惰性清除和定期清除两种方式结合使用,最终保证CPU和内存之间的一种平衡! 总结 相信大家对上面三个概念有点模糊了。【键过期】、【淘汰策略】、【过期清除】 首先【键过期】是redis给我们开发者提供的功能。我们可以根据自己的业务需求合理的设置键的过期时间,从而保证内存的高可用 其次【过期清除】在我们之前设置的过期的key如何进行合理的清除,并不能一股脑一下子进行清除因为数据过大会导致服务的卡顿。这个时候我们需要通过定期清除减缓清除key代码的卡顿。在redis.conf中我们可以设置 hz 10 代表1S中平均执行几次这也是我们上面所说的100MS的由来。但是仅仅定期删除会产生遗漏数据所以我们还需要加上惰性清除,最终保证对客户端来说数据是准确实时清除的。 那么关于【淘汰策略】又是啥呢?在上面过期清除是如果用户一直不请求过期的key ,并且随着业务产生越来越多的过期key . 这时候redis服务中还会堆积很多过期的无效key 。这个时候如果内存不够用了的话那又该怎么办呢?这时候我们需要设置淘汰策略比如果volatile-lru . 就会将最近最少使用的设置过期key进行清除从而保证尽可能地接收更多的有效数据! 这就是为什么会设计三者的原因!好好理解上面三个主题我们再去想想为什么会发生【缓存雪崩】、【缓存崩溃】、【缓存击穿】就好理解一点了呢?

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

Vue 3 中 reactive 对象解构丢失响应性的真实原因与替代写法

在 Vue 3 项目里,不少人在组合式 API 中写 const { count } = reactive({ count: 0 }) 之后,发现模板里 count 永远不会更新。这个问题通常不是 Vue 的 bug,而是 reactive 返回的是 Proxy 对象,解构本质上就是读取属性值再赋给新变量;如果属性是原始类型,新变量拿到的是一个普通值,和原来的 Proxy 已经断开联系。模板或 computed 再访问这个普通变量时,Vue 的依赖收集不会建立到原对象上,因此更新源对象也不会触发视图。常见场景包括把 reactive 表单字段解构给子组件、从 store 中解构状态、在组合式函数返回对象后直接解构使用。这篇文章会从 Proxy 的读取机制讲起,结合真实代码说明为什么解构后失效,并给出 toRefs、toRef、computed 以及直接保留对象引用的写法,同时对比 ref 和 reactive 在解构行为上的差异。

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

互联网架构:从请求到响应的万里长征

当你在浏览器地址栏敲下 www.example.com 并按下回车,到页面完整呈现在屏幕上,这短短几百毫秒里,无数硬件、协议、算法和系统在默契协作。互联网架构并非单一技术,而是一套分层的、去中心化的、高度容错的工程体系。本文不堆砌庞杂术语,而是跟随一个请求的足迹,勾勒出互联网骨架的核心设计思想,并辅以极简代码片段帮助理解。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

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

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册