JSON极慢-探求更极速的替代打算以优化您的数据处置-b-b (json速度和性能)
是的,你没听错!JSON,这种在网络开发中广泛用于数据交流的格局,或许正在拖慢咱们的运行程序。在速度和照应性至关关键的环球里,审核JSON的性能影响至关关键。在这篇博客中,深化讨论JSON或许成为运行程序瓶颈的要素,并探求更快的替代方法和优化技术,使您的运行程序坚持最佳运转形态。
JSON是什么,为什么要关心?
JSON是ScriptObjectNotation的缩写,一种轻量级数据交流格局,已成为运行程序中传输和存储数据的首选。它的繁难性和可读格局使开发者和机器都能轻松经常使用。然而,为什么要在名目中关注JSON呢?
JSON是运行程序中数据的粘合剂。它是主机和客户端之间启动数据通讯的言语,也是数据库和性能文件中存储数据的格局。从实质上讲,JSON在现代网络开发中起着无足轻重的作用。
JSON的盛行以及人们经常使用它的要素...
关键有就下几点:
鉴于这些长处,难怪环球的开发人员都依赖JSON来满足他们的数据交流需求。不过,随着咱们深化讨论,会发现与JSON相关的潜在性能疑问以及如何有效处置这些应战。
对速度的需求
运行速度和照应速度的关键性
在当今快节拍的数字环境中,运行程序的速度和照应才干是不容漠视的。用户宿愿在网络和移动运行中即时失掉信息、极速交互和无缝体验。对速度的这种要求是由多种要素驱动的:
JSON会拖慢咱们的运行程序吗?
在某些状况下,JSON或许是造成运行程序运转速度减慢的罪魁祸首。解析JSON数据的环节,尤其是在处置大型或复杂结构时,或许会消耗贵重的毫秒期间。此外,低效的序列化和反序列化也会影响运行程序的全体性能。
JSON为什么会变慢
1.解析开支
JSON数据抵达运行程序后,必定经过解析环节才干转换成可用的数据结构。解析环节或许相对较慢,尤其是在处置少量或深度嵌套的JSON数据时。
2.序列化和反序列化
JSON要求在从客户端向主机发送数据时启动序列化(将对象编码为字符串),并在接纳数据时启动反序列化(将字符串转换回可用对象)。这些步骤会带来开支并影响运行程序的全体速度。
在微服务架构的环球里,JSON通罕用于在服务之间传递信息。然而,JSON信息须要序列化和反序列化,这两个环节会带来渺小的开支。
3.字符串操作
JSON以文本为基础,关键依托字符串操作来启动衔接和解析等操作。与处置二进制数据相比,字符串处置速度较慢。
4.缺乏数据类型
JSON的数据类型(如字符串、数字、布尔值)有限。复杂的数据结构或许须要效率较低的示意方法,从而造成内存经常使用量参与和处置速度减慢。
5.冗长性
JSON的人机可读设计或许造成冗长。冗余键和重复结构会参与有效载荷的大小,造成数据传输期间延伸。
6.不支持二进制
JSON缺乏对二进制数据的本地支持。在处置二进制数据时,开发人员通常须要将其编码和解码为文本,这或许会降落效率。
7.深嵌套
在某些状况下,JSON数据或许嵌套很深,须要启动递归解析和遍历。这种计算复杂性会降落运行程序的运转速度,尤其是在没有优化的状况下。
JSON的替代品
虽然JSON是一种通用的数据交流格局,但由于其在某些状况下的性能限度,开发者开局探求更快的替代格局。咱们来看呓2其中的一些替代打算。
1.协定缓冲区(protobuf)
协定缓冲区(通常称为protobuf)是谷歌开发的一种二进制序列化格局。其设计宗旨是高效、紧凑和极速。Protobuf的二进制个性使其在序列化和反序列化时比JSON快得多。
何时经常使用:当你须要高性能数据交流时,尤其是在微服务架构、物联网运行或网络带宽有限的状况下,请思考经常使用protobuf。
2.MessagePack信息包
MessagePack是另一种二进制序列化格局,以速度快、结构紧凑而著称。其设计目的是在坚持与各种编程言语兼容的同时,提高比JSON更高的效率。
何时经常使用:当你须要在速度和跨言语兼容性之间取得平衡时,MessagePack是一个不错的选用。它适用于实时运行程序和对缩小数据量有关键要求的状况。
3.BSON(二进制JSON)
BSON或二进制JSON是一种从JSON衍生进去的二进制编码格局。它保管了JSON的灵敏性,同时经过二进制编码提高了性能。BSON罕用于MongoDB等数据库。
何时经常使用:假设你正在经常使用MongoDB,或许须要一种能在JSON和二进制效率之间架起桥梁的格局,那么BSON就是一个很有价值的选用。
4.Avro(阿帕奇Avro)
ApacheAvro是一个数据序列化框架,专一于提供一种紧凑的二进制格局。它基于形式,可成功高效的数据编码和解码。
何时经常使用:Avro适用于形式演进十分关键的状况,如数据存储,以及须要在速度和数据结构灵敏性之间取得平衡的状况。
与JSON相比,这些替代打算在性能上有不同水平的优化,详细选用取决于您的详细经常使用状况。经过思考这些替代打算,您可以优化运行程序的数据交流流程,确保将速度和效率放在开发上班的首位。
每个字节的关键性:优化数据格局
JSON数据
上方是咱们的JSON数据示例片段:
{"id":1,//14bytes"name":"JohnDoe",//20bytes"eml":"johndoe@example.com",//31bytes"age":30,//9bytes"isSubscribed":true,//13bytes"orders":[//11bytes{//2bytes"orderId":"A123",//18bytes"totalAmount":100.50//20bytes},//1byte{//2bytes"orderId":"B456",//18bytes"totalAmount":75.25//19bytes}//1byte]//1byte}
JSON总大小:~139字节
JSON性能多样,易于经常使用,但也有缺陷,那就是它的文本色质。每个字符、每个空格和每个引号都很关键。在数据大小和传输速度至关关键的状况下,这些看似微无余道的字符或许会发生严重影响。
效率应战:经常使用二进制格局缩小数据大小
如今,咱们提供其余格局的数据示意并比拟它们的大小:
**协定缓冲区(protobuf)**:
syntax="proto3";messageUser{int32id=1;stringname=2;stringemail=3;int32age=4;boolis_subscribed=5;repeatedOrderorders=6;messageOrder{stringorder_id=1;floattotal_amount=2;}}
0A0E4A6F686E20446F650C4A6F686E20446F656578616D706C652E636F6D0421000000050112413132330342DCCCCC3F05303131323434353625029A99993F0D31024234353625029A99993F
协定缓冲区总大小:~38字节
MessagePack
二进制示意法(十六进制):
a36a6964000000000a4a6f686e20446f650c6a6f686e646f65406578616d706c652e636f6d042100000005011241313302bdcccc3f0530112434353625029a99993f
信息包总大小:~34字节
BinaryRepresentation(Hexadecimal):
3e0000001069640031000a4a6f686e20446f6502656d61696c006a6f686e646f65406578616d706c652e636f6d1000000022616765001f04370e4940
BSON总大小:~43字节
二进制示意法(十六进制):
0e120a4a6f686e20446f650c6a6f686e646f65406578616d706c652e636f6d049a999940040a020b4108312e3525312e323538323539
Avro总大小:~32字节
图片
如今,你或许想知道,为什么这些格局中的某些会输入二进制数据,但它们的大小却各不相反。Avro、MessagePack和BSON等二进制格局具备不同的外部结构和编码机制,这或许造成二进制示意法的差异,即使它们最终示意的是相反的数据。上方简明引见一下这些差异是如何发生的:
2.MessagePack
这些设计和编码上的差异造成了二进制示意法的不同:
总之,这些差异源于每种格局的设计指标和特点。Avro优先思考形式兼容性,MessagePack并重于紧凑性,而BSON在坚持相似JSON结构的同时参与了二进制类型。格局的选用取决于您的详细经常使用状况和要求,如形式兼容性、数据大小和易用性。
优化JSON性能
上方是一些优化JSON性能的适用技巧以及代码示例和最佳通常:
1.最小化数据大小
//Inefficient{"customer_name_with_spaces":"JohnDoe"}//Efficient{"customerName":"JohnDoe"}
//效率低{"transaction_type":"purchase"}//效率高{"txnType":"purchase"}
2.理智经常使用数组
//效率低{"order":{"items":{"item1":"ProductA","item2":"ProductB"}}}//效率高{"orderItems":["ProductA","ProductB"]}
3.优化数字示意法
尽或许经常使用整数:假设数值可以用整数示意,就用整数替代浮点数。
//效率低{"quantity":1.0}//效率高{"quantity":1}
4.删除冗余
防止反双数据:经过援用共享值来消弭冗余数据。
//效率低{"product1":{"name":"ProductA","price":10},"product2":{"name":"ProductA","price":10}}//效率高{"products":[{"name":"ProductA","price":10},{"name":"ProductB","price":15}]}
5.经常使用紧缩
运行紧缩算法:假设适用,在传输环节中经常使用GziporBrotlito等紧缩算法来减小JSON有效负载的大小。
//经常使用zlib启动Gzip紧缩的Node.js示例constzlib=require('zlib');constjsonData={//在这里填入你的JSON数据};zlib.gzip(JSON.stringify(jsonData),(err,compressedData)=>{if(!err){//经过网络发送compressedData}});
6.驳回主机端缓存:
缓存JSON照应:实施主机端缓存,高效地存储和提供JSON照应,缩小反双数据处置的须要。
7.性能文件和优化
剖析性能:经常使用剖析工具找出JSON处置代码中的瓶颈,而后优化这些局部。
实践优化:在通常中放慢JSON的处置速度
在本节中,咱们将讨论实践案例,这些案例在经常使用JSON时遇到性能瓶颈并成功克制。咱们会看到诸如LinkedIn、Auth0、Uber等出名技术公司如何处置JSON的限度并改善他们运行的性能。这些案例为如何优化运行途理速度和照应性提供了适用的战略。
1.LinkedIn的协定缓冲区集成:
2.Uber的H3天文索引:
应战:Uber经常使用JSON来示意各种天文空间数据,但解析大型数据集的JSON会降落其算法速度。
处置打算:他们引入了H3Geo-Index,这是一种用于天文空间数据的高效六边形网格系统,可缩小JSON解析开支。
影响:这一优化大大放慢了天文空间业务的开展,增强了Uber的打车和地图服务。
3.Slack的信息格局优化:
应战:Slack须要在实时聊天中传输和出现少量JSON格局的信息,这造成了性能瓶颈。
处置打算:他们优化了JSON结构,缩小了不用要的数据,只在每条信息中蕴含必要的信息。
影响:这项优化使得信息展现更快,从而提高了Slack用户的全体聊本能能。
4.Auth0的协定缓冲区成功:
应战:Auth0是一个盛行的身份和访问治理平台,在处置身份验证和授权数据时面临着JSON的性能应战。
处置打算:他们驳回协定缓冲区(ProtocolBuffers)来取代JSON,以编码和解码与身份验证相关的数据。
影响:这一优化大大提高了数据序列化和反序列化的速度,从而放慢了身份验证流程,并增强了Auth0服务的全体性能。
这些事实环球中的例子展现了经过优化战略处置JSON的性能应战如何对运行程序的速度、照应速度和用户体验发生实质性的踊跃影响。它们强调了思考替代数据格局和高效数据结构的关键性,以克制各种状况下与JSON相关的速度减慢疑问。
论断
在不时变动的网络开发环境中,优化JSON性能是一项贵重的技艺,它能让你的名目异乎寻常,并确保你的运行程序在即时数字体验时代茁壮生长。
MySQL与PostgreSQL相比哪个更好
MySQLMySQL声称自己是最流行的开源数据库。 LAMP中的M指的就是MySQL。 构建在LAMP上的应用都会使用MySQL,如WordPress、Drupal等大多数php开源程序。 MySQL最初是由MySQL AB开发的,然后在2008年以10亿美金的价格卖给了Sun公司,Sun公司又在2010年被Oracle收购。 Oracle支持MySQL的多个版本:Standard、Enterprise、Classic、Cluster、Embedded与Community。 其中有一些是免费下载的,另外一些则是收费的。 其核心代码基于GPL许可,由于MySQL被控制在Oracle,社区担心会对MySQL的开源会有影响,所以开发了一些分支,比如: MariaDB和Percona。 PostgreSQLPostgreSQL标榜自己是世界上最先进的开源数据库。 PostgreSQL的一些粉丝说它能与Oracle相媲美,而且没有那么昂贵的价格和傲慢的客服。 最初是1985年在加利福尼亚大学伯克利分校开发的,作为Ingres数据库的后继。 PostgreSQL是完全由社区驱动的开源项目。 它提供了单个完整功能的版本,而不像MySQL那样提供了多个不同的社区版、商业版与企业版。 PostgreSQL基于自由的BSD/MIT许可,组织可以使用、复制、修改和重新分发代码,只需要提供一个版权声明即可。 MySQL与PostgreSQL的对比MySQL的背后是一个成熟的商业公司,而PostgreSQL的背后是一个庞大的志愿开发组。 这使得MySQL的开发过程更为慎重,而PostgreSQL的反应更为迅速。 这样的两种背景直接导致了各自固有的优点和缺点。 PostgreSQL相对于MySQL的优势1)不仅仅是关系型数据库除了存储正常的数据类型外,还支持存储:array,不管是一位数组还是多为数组均支持json(hStore)和jsonb,相比使用text存储接送要高效很多json和jsonb之间的区别jsonb和json在更高的层面上看起来几乎是一样的,但在存储实现上是不同的。 json存储完的文本,json列会每次都解析存储的值,它不支持索引,但你可以为查询创建表达式索引。 jsonb存储的二进制格式,避免了重新解析数据结构。 它支持索引,这意味着你可以不使用指定的索引就能查询任何路径。 当我们比较写入数据速度时,由于数据存储的方式的原因,jsonb会比json稍微的慢一点。 json列会每次都解析存储的值,这意味着键的顺序要和输入的时候一样。 但jsonb不同,以二进制格式存储且不保证键的顺序。 因此,如果你有软件需要依赖键的顺序,jsonb可能不是你的应用的最佳选择。 使用jsonb的优势还在于你可以轻易的整合关系型数据和非关系型数据, PostgreSQL对于mongodb这类的基于文档的数据库是个不小的威胁,毕竟如果一个表中只有一列数据的类型是半结构化的,没有必要为了迁就它而整个表的设计采用schemaless的结构。 2)支持地理信息处理扩展PostGIS 为PostgreSQL提供了存储空间地理数据的支持,使PostgreSQL成为了一个空间数据库,能够进行空间数据管理、数量测量与几何拓扑分析。 在功能上,和MYSQL对比,PostGIS具有下列优势:O2O业务场景中的LBS业务使用PostgreSQL + PostGIS有无法比拟的优势。 3)可以快速构建REST APIPostgREST 可以方便的为任何 PostgreSQL 数据库提供完全的 RESTful API 服务。 4)支持树状结构支持R-trees这样可扩展的索引类型,可以更方便地处理一些特殊数据。 MySQL 处理树状的设计会很复杂, 而且需要写很多代码, 而 PostgreSQL 可以高效处理树结构。 5)有极其强悍的 SQL 编程能力支持递归,有非常丰富的统计函数和统计语法支持。 MySQL:支持 CREATE PROCEDURE 和 CREATE FUNCTION 语句。 存储过程可以用 SQL 和 C++ 编写。 用户定义函数可以用 SQL、C 和 C++ 编写。 PostgreSQL:没有单独的存储过程,都是通过函数实现的。 用户定义函数可以用 PL/pgSQL(专用的过程语言)、PL/Tcl、PL/Perl、PL/Python 、SQL 和 C 编写。 6)外部数据源支持可以把 70 种外部数据源 (包括 Mysql, Oracle, CSV, hadoop …) 当成自己数据库中的表来查询。 Postgres有一个针对这一难题的解决方案:一个名为“外部数据封装器(Foreign target=_blank>支持窗口函数窗口函数提供跨行相关的当前查询行集执行计算的能力。 仅当调用跟着OVER子句的聚集函数,作为窗口函数;否则它们作为常规的聚合函数。 窗口也是一种分组,但和 group by 的分组不同。 窗口,可以提供分组之外,还可以执行对每个窗口进行计算。 可以相像成是group by 后,然后对每个分组进行计算,而不像Group by ,只是单纯地分组。 MySQL 不支持 OVER 子句, 而PostgreSQL支持。 OVER 子句能简单的解决 “每组取 top 5” 的这类问题。 MySQL支持的SQL语法(ANSI SQL标准)的很小一部分。 不支持递归查询、通用表表达式(Oracle的with 语句)或者窗口函数(分析函数)。 10)对索引的支持更强PostgreSQL 的可以使用函数和条件索引,这使得PostgreSQL数据库的调优非常灵活,mysql就没有这个功能,条件索引在web应用中很重要。 对于索引类型:MySQL:取决于存储引擎。 MyISAM:BTREE,InnoDB:BTREE。 PostgreSQL:支持 B-树、哈希、R-树和 Gist 索引。 InnoDB的表和索引都是按相同的方式存储。 也就是说表都是索引组织表。 这一般要求主键不能太长而且插入时的主键最好是按顺序递增,否则对性能有很大影响。 PostgreSQL不存在这个问题。 索引类型方面,MySQL取决于存储引擎。 MyISAM:BTREE,InnoDB:BTREE。 PostgreSQL支持 B-树、哈希、R-树和 Gist 索引。 11)集群支持更好Mysql Cluster可能与你的想象有较大差异。 开源的cluster软件较少。 复制(Replication)功能是异步的并且有很大的局限性。 例如,它是单线程的(single-threaded),因此一个处理能力更强的Slave的恢复速度也很难跟上处理能力相对较慢的Master。 PostgreSQL有丰富的开源cluster软件支持。 plproxy 可以支持语句级的镜像或分片,slony 可以进行字段级的同步设置,standby 可以构建WAL文件级或流式的读写分离集群,同步频率和集群策略调整方便,操作非常简单。 另外,PostgreSQL的主备复制属于物理复制,相对于MySQL基于binlog的逻辑复制,数据的一致性更加可靠,复制性能更高,对主机性能的影响也更小。 对于WEB应用来说,复制的特性很重要,mysql到现在也是异步复制,pgsql可以做到同步,异步,半同步复制。 还有mysql的同步是基于binlog复制,类似oracle golden gate,是基于stream的复制,做到同步很困难,这种方式更加适合异地复制,pgsql的复制基于wal,可以做到同步复制。 同时,pgsql还提供stream复制。 12)事务隔离做的更好MySQL 的事务隔离级别 repeatable read 并不能阻止常见的并发更新, 得加锁才可以, 但悲观锁会影响性能, 手动实现乐观锁又复杂. 而 PostgreSQL 的列里有隐藏的乐观锁 version 字段, 默认的 repeatable read 级别就能保证并发更新的正确性, 并且又有乐观锁的性能。 13)对于字符支持更好一些MySQL 里需要 utf8mb4 才能显示 emoji 的坑, PostgreSQL 没这个坑。 14)对表连接支持较完整对表连接支持较完整,MySQL只有一种表连接类型:嵌套循环连接(nested-loop),不支持排序-合并连接(sort-merge join)与散列连接(hash join)。 PostgreSQL都支持。 15)存储方式支持更大的数据量PostgreSQL主表采用堆表存放,MySQL采用索引组织表,能够支持比MySQL更大的数据量。 16)时间精度更高MySQL对于时间、日期、间隔等时间类型没有秒以下级别的存储类型,而PostgreSQL可以精确到秒以下。 17)优化器的功能较完整MySQL对复杂查询的处理较弱,查询优化器不够成熟,explain看执行计划的结果简单。 性能优化工具与度量信息不足。 PostgreSQL很强大的查询优化器,支持很复杂的查询处理。 explain返回丰富的信息。 提供了一些性能视图,可以方便的看到发生在一个表和索引上的select、delete、update、insert统计信息,也可以看到cache命中率。 网上有一个开源的pgstatspack工具。 18)序列支持更好MySQL 不支持多个表从同一个序列中取 id, 而 PostgreSQL 可以。 19)对子查询支持更好对子查询的支持。 虽然在很多情况下在SQL语句中使用子查询效率低下,而且绝大多数情况下可以使用带条件的多表连接来替代子查询,但是子查询的存在在很多时候仍然不可避免。 而且使用子查询的SQL语句与使用带条件的多表连接相比具有更高的程序可读性。 几乎任何数据库的子查询 (subquery) 性能都比 MySQL 好。 20)增加列更加简单MySQL表增加列,基本上是重建表和索引,会花很长时间。 PostgreSQL表增加列,只是在数据字典中增加表定义,不会重建表相对于PostgreSQL的优势1)MySQL比PostgreSQL更流行流行对于一个商业软件来说,也是一个很重要的指标,流行意味着更多的用户,意味着经受了更多的考验,意味着更好的商业支持、意味着更多、更完善的文档资料。 易用,很容易安装。 第三方工具,包括可视化工具,让用户能够很容易入门。 2)回滚实现更优innodb的基于回滚段实现的MVCC机制,相对PG新老数据一起存放的基于XID的MVCC机制,是占优的。 新老数据一起存放,需要定时触发VACUUM,会带来多余的IO和数据库对象加锁开销,引起数据库整体的并发能力下降。 而且VACUUM清理不及时,还可能会引发数据膨胀。 3)在Windows上运行更可靠与PostgreSQL相比,MySQL更适宜在Windows环境下运行。 MySQL作为一个本地的Windows应用程序运行(在 NT/Win2000/WinXP下,是一个服务),而PostgreSQL是运行在Cygwin模拟环境下。 PostgreSQL在Windows下运行没有MySQL稳定,应该是可以想象的。 4)线程模式相比进程模式的优势MySQL使用了线程,而PostgreSQL使用的是进程。 在不同线程之间的环境转换和访问公用的存储区域显然要比在不同的进程之间要快得多。 进程模式对多CPU利用率比较高。 进程模式共享数据需要用到共享内存,而线程模式数据本身就是在进程空间内都是共享的,不同线程访问只需要控制好线程之间的同步。 线程模式对资源消耗比较少。 所以MySQL能支持远比PostgreSQL多的更多的连接。 但PostgreSQL中有优秀的连接池软件软件,如pgbouncer和pgpool,所以通过连接池也可以支持很多的连接。 5)权限设置上更加完善MySQL在权限系统上比PostgreSQL某些方面更为完善。 PostgreSQL只支持对于每一个用户在一个数据库上或一个数据表上的 INSERT、SELECT和UPDATE/DELETE的授权,而MySQL允许你定义一整套的不同的数据级、表级和列级的权限。 对于列级的权限, PostgreSQL可以通过建立视图,并确定视图的权限来弥补。 MySQL还允许你指定基于主机的权限,这对于目前的PostgreSQL是无法实现的,但是在很多时候,这是有用的。 6)存储引擎插件化机制MySQL的存储引擎插件化机制,使得它的应用场景更加广泛,比如除了innodb适合事务处理场景外,myisam适合静态数据的查询场景。 7)适应24/7运行MySQL可以适应24/7运行。 在绝大多数情况下,你不需要为MySQL运行任何清除程序。 PostgreSQL目前仍不完全适应24/7运行,这是因为你必须每隔一段时间运行一次VACUUM。 8)更加试用于简单的场景PostgreSQL只支持堆表,不支持索引组织表,Innodb只支持索引组织表。 索引组织表的优势:表内的数据就是按索引的方式组织,数据是有序的,如果数据都是按主键来访问,那么访问数据比较快。 而堆表,按主键访问数据时,是需要先按主键索引找到数据的物理位置。 索引组织表的劣势:索引组织表中上再加其它的索引时,其它的索引记录的数据位置不再是物理位置,而是主键值,所以对于索引组织表来说,主键的值不能太大,否则占用的空间比较大。 对于索引组织表来说,如果每次在中间插入数据,可能会导致索引分裂,索引分裂会大大降低插入的性能。 所以对于使用innodb来说,我们一般最好让主键是一个无意义的序列,这样插入每次都发生在最后,以避免这个问题。 由于索引组织表是按一个索引树,一般它访问数据块必须按数据块之间的关系进行访问,而不是按物理块的访问数据的,所以当做全表扫描时要比堆表慢很多,这可能在OLTP中不明显,但在数据仓库的应用中可能是一个问题。 总结MySQL从一开始就没有打算做所有事情,因而它在功能方面有一定的局限性,并不能满足一些先进应用程序的要求。 MySQL对某些功能(例如引用、事务、审计等)的实现方式使得它与其他的关系型数据库相比缺少了一些可靠性。 对于简单繁重的读取操作,使用PostgreSQL可能有点小题大做,同时性能也比MySQL这样的同类产品要差。 除非你需要绝对的数据完整性,ACID遵从性或者设计复杂,否则PostgreSQL对于简单的场景而言有点多余。 如何你确定只在MySQL和PostgreSQL中进行选择,以下规则总是有效的:如果你的操作系统是Windows,你应该使用MySQL。 当绝对需要可靠性和数据完整性的时候,PostgreSQL是更好的选择。 如果需要数据库执行定制程序,那么可扩展的PostgreSQL是更好的选择。 你的应用处理的是地理数据,由于R-TREES的存在,你应该使用PostgreSQL。 如果你对数据库并不了十分了解,甚至不知道事务、存储过程等究竟是什么,你应该使用MySQL。
Elasticsearch性能优化
注:文本整理自《ELKstack权威指南》
在 CRUD 章节,我们已经知道 ES 的数据写入是如何操作的了。喜欢自己动手的读者可能已经迫不及待的自己写了程序开始往 ES 里写数据做测试。这时候大家会发现:程序的运行速度非常一般,即使 ES 服务运行在本机,一秒钟大概也就能写入几百条数据。
这种速度显然不是 ES 的极限。事实上,每条数据经过一次完整的 HTTP POST 请求和 ES indexing 是一种极大的性能浪费,为此,ES 设计了批量提交方式。在数据读取方面,叫 mget 接口,在数据变更方面,叫 bulk 接口。mget 一般常用于搜索时 ES 节点之间批量获取中间结果集,对于 Elastic Stack 用户,更常见到的是 bulk 接口。
bulk 接口采用一种比较简朴的数据积累格式,示例如下:
格式是,每条 JSON 数据的上面,加一行描述性的元 JSON,指明下一行数据的操作类型,归属索引信息等。
采用这种格式,而不是一般的 JSON 数组格式,是因为接收到 bulk 请求的 ES 节点,就可以不需要做完整的 JSON 数组解析处理,直接按行处理简短的元 JSON,就可以确定下一行数据 JSON 转发给哪个数据节点了。这样,一个固定内存大小的 network buffer 空间,就可以反复使用,又节省了大量 JVM 的 GC。
事实上,产品级的 logstash、rsyslog、spark 都是默认采用 bulk 接口进行数据写入的。对于打算自己写程序的读者,建议采用 Perl 的Search::Elasticsearch::Bulk或者 Python 的.*库。
在配置 bulk 数据的时候,一般需要注意的就是请求体大小(bulk size)。
这里有一点细节上的矛盾,我们知道,HTTP 请求,是可以通过 HTTP 状态码100 Continue来持续发送数据的。但对于 ES 节点接收 HTTP 请求体的Content-Length来说,是按照整个大小来计算的。所以,首先,要确保 bulk 数据不要超过_content_length设置。
那么,是不是尽量让 bulk size 接近这个数值呢?当然不是。
依然是请求体的问题,因为请求体需要全部加载到内存,而 JVM Heap 一共就那么多(按 31GB 算),过大的请求体,会挤占其他线程池的空间,反而导致写入性能的下降。
再考虑网卡流量,磁盘转速的问题,所以一般来说,建议 bulk 请求体的大小,在 15MB 左右,通过实际测试继续向上探索最合适的设置。
注意:这里说的 15MB 是请求体的字节数,而不是程序里里设置的 bulk size。bulk size 一般指数据的条目数。不要忘了,bulk 请求体中,每条数据还会额外带上一行元 JSON。
以 logstash 默认的bulk_size => 5000为例,假设单条数据平均大小 200B ,一次 bulk 请求体的大小就是 1.5MB。那么我们可以尝试bulk_size => ;而如果单条数据平均大小是 20KB,一次 bulk 大小就是 100MB,显然超标了,需要尝试下调至bulk_size => 500 。
gateway 是 ES 设计用来长期存储索引数据的接口。一般来说,大家都是用本地磁盘来存储索引数据,即为local 。
数据恢复中,有很多策略调整我们已经在之前分片控制小节讲过。除开分片级别的控制以外,gateway 级别也还有一些可优化的地方:
注意:gateway 中说的节点,仅包括主节点和数据节点,纯粹的 client 节点是不算在内的。如果你有更明确的选择,也可以按需求写:
虽然 ES 对 gateway 使用 NFS,iscsi 等共享存储的方式极力反对,但是对于较大量级的索引的副本数据,ES 从 1.5 版本开始,还是提供了一种节约成本又不特别影响性能的方式:影子副本(shadow replica)。
首先,需要在集群各节点的中开启选项:
同时,确保各节点使用相同的路径挂载了共享存储,且目录权限为 Elasticsearch 进程用户可读可写。
然后,创建索引:
针对 shadow replicas ,ES 节点不会做实际的索引操作,而是单纯的每次 flush 时,把 segment 内容 fsync 到共享存储磁盘上。然后 refresh 让其他节点能够搜索该 segment 内容。
如果你已经决定把数据放到共享存储上了,采用 shadow replicas 还是有一些好处的:
但是请注意:主分片节点还是要承担一个副本的写入过程,并不像 Lucene 的 FileReplicator 那样通过复制文件完成,所以达不到完全节省 CPU 的效果。
shadow replicas 只是一个在某些特定环境下有用的方式。在资源允许的情况下,还是应该使用 local gateway。而另外采用 snapshot 接口来完成数据长期备份到 HDFS 或其他共享存储的需要。
我们都知道,ES 中的 master 跟一般 MySQL、Hadoop 的 master 是不一样的。它即不是写入流量的唯一入口,也不是所有数据的元信息的存放地点。所以,一般来说,ES 的 master 节点负载很轻,集群性能是可以近似认为随着 data 节点的扩展线性提升的。
但是,上面这句话并不是完全正确的。
ES 中有一件事情是只有 master 节点能管理的,这就是集群状态(cluster state)。
集群状态中包括以下信息:
这些信息在集群的任意节点上都存放着,你也可以通过/_cluster/state接口直接读取到其内容。注意这最后一项信息,之前我们已经讲过 ES 怎么通过简单地取余知道一条数据放在哪个分片里,加上现在集群状态里又记载了分片在哪个节点上,那么,整个集群里,任意节点都可以知道一条数据在哪个节点上存储了。所以,数据读写才可以发送给集群里任意节点。
至于修改,则只能由 master 节点完成!显然,集群状态里大部分内容是极少变动的,唯独有一样除外——索引的映射。因为 ES 的 schema-less 特性,我们可以任意写入 JSON 数据,所以索引中随时可能增加新的字段。这个时候,负责容纳这条数据的主分片所在的节点,会暂停写入操作,将字段的映射结果传递给 master 节点;master 节点合并这段修改到集群状态里,发送新版本的集群状态到集群的所有节点上。然后写入操作才会继续。一般来说,这个操作是在一二十毫秒内就可以完成,影响也不大。
但是也有一些情况会是例外。
在较大规模的 Elastic Stack 应用场景中,这是比较常见的一个情况。因为 Elastic Stack 建议采用日期时间作为索引的划分方式,所以定时(一般是每天),会统一产生一批新的索引。而前面已经讲过,ES 的集群状态每次更新都是阻塞式的发布到全部节点上以后,节点才能继续后续处理。
这就意味着,如果在集群负载较高的时候,批量新建新索引,可能会有一个显著的阻塞时间,无法写入任何数据。要等到全部节点同步完成集群状态以后,数据写入才能恢复。
不巧的是,中国使用的是北京时间,UTC +0800。也就是说,默认的 Elastic Stack 新建索引时间是在早上 8 点。这个时间点一般日志写入量已经上涨到一定水平了(当然,晚上 0 点的量其实也不低)。
对此,可以通过定时任务,每天在最低谷的早上三四点,提前通过 POST mapping 的方式,创建好之后几天的索引。就可以避免这个问题了。
如果你的日志是比较严重的非结构化数据,这个问题在 2.0 版本后会变得更加严重。Elasticsearch 从 2.0 版本开始,对 mapping 更新做了重构。为了防止字段类型冲突和减少 master 定期下发全量 cluster state 导致的大流量压力,新的实现和旧实现的区别在:
也就是说,一旦你日志中字段数量较多,在新创建索引的一段时间内,可能长达几十分钟一直被反复锁死!
这是另一种常见的滥用。在使用 Elastic Stack 处理访问日志时,为了查询更方便,可能会采用 logstash-filter-kv 插件,将访问日志中的每个 URL 参数,都切分成单独的字段。比如一个 /?uid=&action=payload 的 URL 会被转换成如下 JSON:
但是,因为集群状态是存在所有节点的内存里的,一旦 URL 参数过多,ES 节点的内存就被大量用于存储字段映射内容。这是一个极大的浪费。如果碰上 URL 参数的键内容本身一直在变动,直接撑爆 ES 内存都是有可能的!
以上是真实发生的事件,开发人员莫名的选择将一个 UUID 结果作为 key 放在 URL 参数里。直接导致 ES 集群 master 节点全部 OOM。
如果你在 ES 日志中一直看到有新的updating mapping [logstash-2015.06.01]字样出现的话,请郑重考虑一下自己是不是用的上如此细分的字段列表吧。
好,三秒钟过去,如果你确定一定以及肯定还要这么做,下面是一个变通的解决办法。
用 nested object 来存放 URL 参数的方法稍微复杂,但还可以接受。单从 JSON 数据层面看,新方式的数据结构如下:
没错,看起来就是一个数组。但是 JSON 数组在 ES 里是有两种处理方式的。
如果直接写入数组,ES 在实际索引过程中,会把所有内容都平铺开,变成 Arrays of Inner Objects 。整条数据实际类似这样的结构:
这种方式最大的问题是,当你采用:uid AND 语句意图搜索一个 uid= 的请求时,实际是整个 URL 参数中任意一处 value 为 的,都会命中。
要想达到正确搜索的目的,需要在写入数据之前,指定 urlargs 字段的映射类型为 nested object。命令如下:
这样,数据实际是类似这样的结构:
当然,nested object 节省字段映射的优势对应的是它在使用的复杂。Query 和 Aggs 都必须使用专门的 nested query 和 nested aggs 才能正确读取到它。
nested query 语法如下:
nested aggs 语法如下:
ES 内针对不同阶段,设计有不同的缓存。以此提升数据检索时的响应性能。主要包括节点层面的 filter cache 和分片层面的 request cache。下面分别讲述。
ES 的 query DSL 在 2.0 版本之前分为 query 和 filter 两种,很多检索语法,是同时存在 query 和 filter 里的。比如最常用的 term、prefix、range 等。怎么选择是使用 query 还是 filter 成为很多用户头疼的难题。于是从 2.0 版本开始,ES 干脆合并了 filter 统一归为 query。但是具体的检索语法本身,依然有 query 和 filter 上下文的区别。ES 依靠这个上下文判断,来自动决定是否启用 filter cache。
query 跟 filter 上下文的区别,简单来说:
所以,选择也就出来了:
不过我们要怎么写,才能让 ES 正确判断呢?看下面这个请求:
在这个请求中,
需要注意的是,filter cache 是节点层面的缓存设置,每个节点上所有数据在响应请求时,是共用一个缓存空间的。当空间用满,按照 LRU 策略淘汰掉最冷的数据。
可以用配置来设置这个缓存空间的大小,默认是 JVM 堆的 10%,也可以设置一个绝对值。注意这是一个静态值,必须在中提前配置。
ES 还有另一个分片层面的缓存,叫 shard request cache。5.0 之前的版本中,request cache 的用途并不大,因为 query cache 要起作用,还有几个先决条件:
以 Elastic Stack 场景来说,Kibana 里几乎所有的请求,都是有@timestamp作为过滤条件的,而且大多数是以 最近 N 小时/分钟 这样的选项,也就是说,页面每次刷新,发出的请求 JSON 里的时间过滤部分都是在变动的。query cache 在处理 Kibana 发出的请求时,完全无用。
而 5.0 版本的一大特性,叫 instant aggregation。解决了这个先决条件的一大阻碍。
在之前的版本,Elasticsearch 接收到请求之后,直接把请求原样转发给各分片,由各分片所在的节点自行完成请求的解析,进行实际的搜索操作。所以缓存的键是原始 JSON 串。
而 5.0 的重构后,接收到请求的节点先把请求的解析做完,发送到各节点的是统一拆分修改好的请求,这样就不再担心 JSON 串多个空格啥的了。
其次,上面说的『拆分修改』是怎么回事呢?
比如,我们在 Kibana 里搜索一个最近 7 天( @timestamp:[now-7d TO now] )的数据,ES 就可以根据按天索引的判断,知道从 6 天前到昨天这 5 个索引是肯定全覆盖的。那么这个横跨 7 天的date rangequery 就变成了 5 个match_allquery 加 2 个短时间的date_rangequery。
现在你的仪表盘过 5 分钟自动刷新一次,再提交上来一次最近 7 天的请求,中间这 5 个match_all就完全一样了,直接从 request cache 返回即可,需要重新请求的,只有两头真正在变动的date_range了。
注1: match_all不用遍历倒排索引,比直接查询@timestamp:*要快很多。 注2:判断覆盖修改为match_all并不是真的按照索引名称,而是 ES 从 2.x 开始提供的field_stats接口可以直接获取到@timestamp在本索引内的 max/min 值。当然从概念上如此理解也是可以接受的。
响应结果如下:
和 filter cache 一样,request cache 的大小也是以节点级别控制的,配置项名为 ,其默认值为1% 。
字段数据(fielddata),在 Lucene 中又叫 uninverted index。我们都知道,搜索引擎会使用倒排索引(inverted index)来映射单词到文档的 ID 号。而同时,为了提供对文档内容的聚合,Lucene 还可以在运行时将每个字段的单词以字典序排成另一个 uninverted index,可以大大加速计算性能。
作为一个加速性能的方式,fielddata 当然是被全部加载在内存的时候最为有效。这也是 ES 默认的运行设置。但是,内存是有限的,所以 ES 同时也需要提供对 fielddata 内存的限额方式:
Elasticsearch 在 total,fielddata,request 三个层面上都设计有 circuit breaker 以保护进程不至于发生 OOM 事件。在 fielddata 层面,其设置为:
但是相比较集群庞大的数据量,内存本身是远远不够的。为了解决这个问题,ES 引入了另一个特性,可以对精确索引的字段,指定 fielddata 的存储方式。这个配置项叫: doc_values 。
所谓doc_values ,其实就是在 ES 将数据写入索引的时候,提前生成好 fielddata 内容,并记录到磁盘上。因为 fielddata 数据是顺序读写的,所以即使在磁盘上,通过文件系统层的缓存,也可以获得相当不错的性能。
注意:因为doc_values是在数据写入时即生成内容,所以,它只能应用在精准索引的字段上,因为索引进程没法知道后续会有什么分词器生成的结果。
由于在 Elastic Stack 场景中, doc_values的使用极其频繁,到 Elasticsearch 5.0 以后,这两者的区别被彻底强化成两个不同字段类型: text和keyword 。
等同于过去的:
而
等同于过去的:
也就是说,以后的用户,已经不太需要在意 fielddata 的问题了。不过依然有少数情况,你会需要对分词字段做聚合统计的话,你可以在自己接受范围内,开启这个特性:
你可以看到在上面加了一段fielddata_frequency_filter配置,这个配置是 segment 级别的。上面示例的意思是:只有这个 segment 里的文档数量超过 500 个,而且含有该字段的文档数量占该 segment 里的文档数量比例超过 10% 时,才加载这个 segment 的 fielddata。
下面是一个可能有用的对分词字段做聚合的示例:
这个示例可以对经过了logstash-filter-punct插件处理的数据,获取每种 punct 类型日志的关键词和对应的代表性日志原文。其效果类似 Splunk 的事件模式功能:
[图片上传失败...(image-b0b69f-64)]
如果经过之前章节的一系列优化之后,数据确实超过了集群能承载的能力,除了拆分集群以外,最后就只剩下一个办法了:清除废旧索引。
为了更加方便的做清除数据,合并 segment,备份恢复等管理任务,Elasticsearch 在提供相关 API 的同时,另外准备了一个命令行工具,叫 curator 。curator 是 Python 程序,可以直接通过 pypi 库安装:
注意,是 elasticsearch-curator 不是 curator。PyPi 原先就有另一个项目叫这个名字
和 Elastic Stack 里其他组件一样,curator 也是被 收购的原开源社区周边。收编之后同样进行了一次重构,命令行参数从单字母风格改成了长单词风格。新版本的 curator 命令可用参数如下:
Options 包括:
--host TEXTElasticsearch host. --url_prefix TEXTElasticsearch http url prefix. --port INTEGER Elasticsearch port. --use_sslConnect to Elasticsearch through SSL. --http_auth TEXT Use Basic Authentication ex: user:pass --timeout INTEGERConnection timeout in seconds. --master-onlyOnly operate on elected master node. --dry-runDo not perform any changes. --debugDebug mode --loglevel TEXTLog level --logfile TEXT log file --logformat TEXT Log output format [default|logstash]. --versionShow the version and exit. --help Show this message and exit.
Commands 包括: alias Index Aliasing allocationIndex Allocation bloom Disable bloom filter cache close Close indices deleteDelete indices or snapshots openOpen indices optimizeOptimize Indices replicasReplica Count Per-shard showShow indices or snapshots snapshotTake snapshots of indices (Backup)
针对具体的 Command,还可以继续使用--help查看该子命令的帮助。比如查看close子命令的帮助,输入curator close --help ,结果如下:
在使用 1.4.0 以上版本的 Elasticsearch 前提下,curator 曾经主要的一个子命令bloom已经不再需要使用。所以,目前最常用的三个子命令,分别是close ,delete和optimize ,示例如下:
这一顿任务,结果是:
索引保存最近 5 天, 保存最近 10 天, 索引保存最近 30 天;且所有七天前的logstash-*索引都暂时关闭不用;最后对所有非当日日志做 segment 合并优化。
profiler 是 Elasticsearch 5.0 的一个新接口。通过这个功能,可以看到一个搜索聚合请求,是如何拆分成底层的 Lucene 请求,并且显示每部分的耗时情况。
启用 profiler 的方式很简单,直接在请求里加一行即可:
可以看到其中对 query 和 aggs 部分的返回是不太一样的。
query 部分包括 collectors、rewrite 和 query 部分。对复杂 query,profiler 会拆分 query 成多个基础的 TermQuery,然后每个 TermQuery 再显示各自的分阶段耗时如下:
我们可以很明显的看到聚合统计在初始化阶段、收集阶段、构建阶段、汇总阶段分别花了多少时间,遍历了多少数据。
注意其中 reduce 阶段还没实现完毕,所有都是 0。因为目前 profiler 只能在 shard 级别上做统计。
collect 阶段的耗时,有助于我们调整对应 aggs 的collect_mode参数选择。目前 Elasticsearch 支持breadth_first和depth_first两种方式。
initialise 阶段的耗时,有助于我们调整对应 aggs 的execution_hint参数选择。目前 Elasticsearch 支持map 、 global_ordinals_low_cardinality 、 global_ordinals和global_ordinals_hash四种选择。在计算离散度比较大的字段统计值时,适当调整该参数,有益于节省内存和提高计算速度。
对高离散度字段值统计性能很关注的读者,可以关注这条记录的进展。
(本文完)
文本整理自《ELKstack权威指南》
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。