PostgreSQL基础知识

PostgreSQL简介

PostgreSQL是一个强大的开源对象关系数据库系统,它使用并扩展了SQL语言,并结合了许多功能,可以安全地存储和扩展最复杂的数据工作负载。PostgreSQL的起源可以追溯到1986年,是加州大学伯克利分校POSTGRES项目的一部分,在核心平台上有超过35年的积极开发。

PostgreSQL以其经验证的体系结构、可靠性、数据完整性、强大的功能集、可扩展性以及软件背后的开源社区致力于始终如一地提供高性能和创新的解决方案而赢得了良好的声誉。 PostgreSQL 提供了多版本并行控制,支持几乎所有 SQL 构件(包括子查询,事务和用户定义类型和函数), PostgreSQL已经成为许多人和组织选择的开源关系数据库。

PostgreSQL特性

1.完整的SQL兼容性:PostgreSQL完全符合SQL标准,并提供了广泛的SQL功能,包括复杂的查询、子查询、联合查询、聚合函数等。它还支持许多标准和非标准的SQL扩展,使得开发人员能够轻松地编写复杂的SQL查询和操作。

2.丰富的数据类型支持:PostgreSQL提供了多种数据类型,包括整型、浮点型、字符型等基本数据类型,以及数组、JSON、XML、几何类型等高级数据类型。此外,它还支持用户自定义数据类型,使得开发人员能够根据具体需求定义自己的数据结构。

3.多版本并发控制MVCC:PostgreSQL使用MVCC来处理并发事务。它允许多个事务同时对数据库进行读写操作,而不会相互阻塞。MVCC还可以提供快照读取功能,即使在写入事务进行时,读取事务也能看到一致的数据库状态。

4.扩展性:PostgreSQL具有良好的扩展性,可以通过编写自定义函数、存储过程、插件等方式来扩展其功能。它还支持通过扩展机制来添加新的功能和数据类型,使得用户能够根据实际需求灵活地扩展数据库的功能。

5.高级索引技术:PostgreSQL支持多种类型的索引,包括B-tree、哈希、GiST、GIN等,以满足不同类型的查询需求。它还支持多列索引、表达式索引等高级索引技术,可以提高查询性能并减少查询时间。

6.安全性:PostgreSQL提供了多层次的安全性保护机制,包括SSL支持、访问控制列表、行级安全等。这些功能可以确保数据库的数据在传输和存储过程中得到充分的保护,防止未经授权的访问和数据泄露。

7.复杂查询和分析功能:PostgreSQL提供了丰富的查询和分析功能,包括窗口函数、通用表表达式(CTE)、递归查询等。这些功能使得开发人员能够轻松地进行复杂的数据分析和报告生成,满足各种业务需求。

8.高可用性和容错性:PostgreSQL支持流复制和逻辑复制等多种复制技术,以实现数据的备份和容灾。同时,它还提供了自动故障转移和故障恢复功能,保证了系统的高可用性和容错性。

9.全文搜索和地理信息系统支持: PostgreSQL内置了全文搜索引擎和地理信息系统(GIS)支持,使得开发人员能够构建具有全文搜索和地理定位功能的应用程序。这些功能可以应用于各种场景,如搜索引擎、位置服务、地图应用等。

10.活跃的社区支持:PostgreSQL拥有一个活跃的开源社区,提供了丰富的文档、教程、论坛和邮件列表等资源,开发人员可以从中获取帮助和支持。社区的不断贡献和改进保证了 PostgreSQL 的持续发展和优化。

PostgreSQL内存结构

1.共享缓冲区:共享缓冲区是PostgreSQL中最核心的内存结构之一。它用于缓存数据库中的数据块,以减少频繁的磁盘I/O操作。这些数据块的大小通常为8KB,每个数据块在共享缓冲区中有一个对应的“页”,它们可以被多个并发的数据库进程访问。

2.工作内存:工作内存用于执行查询、排序、哈希等操作时的临时内存空间。在查询执行期间,PostgreSQL会动态分配一些内存用于排序、哈希和其他临时数据结构的操作,每个查询都可以使用一定量的工作内存。

3.后台进程内存:后台进程内存包括了各种PostgreSQL后台进程(如自动清理进程、统计收集器等)运行时所使用的内存。这些进程会在后台执行各种管理和维护任务,需要分配一定的内存来存储状态信息、临时数据等。

4.连接内存:连接内存用于维护客户端连接状态和处理客户端请求的内存空间。每个客户端连接都会分配一定的连接内存,用于存储会话状态、查询计划、结果集等信息。

5.事务控制内存:事务控制内存用于存储事务控制信息和数据结构,如事务状态、事务日志、事务快照等。它在数据库执行事务管理和并发控制时发挥着重要作用,确保事务的一致性和隔离性。

6.动态管理内存:PostgreSQL使用动态内存分配器来管理和分配其他类型的内存,如动态字符串、临时数据结构等。动态管理内存的大小和行为可以通过参数进行调整,以适应不同的数据库工作负载和性能需求。

PostgreSQL工作者进程

PostgreSQL工作者进程又叫PostgreSQL后台进程,主要负责执行各种后台任务以支持数据库运行的操作系统进程。这些进程在数据库启动时创建,并在整个数据库实例的生命周期中持续运行。

Postmaster

Postmaster进程是PostgreSQL数据库管理系统中非常重要的一个后台进程,负责管理整个PostgreSQL实例的启动、运行和关闭过程。Postmaster接受客户端连接请求,并为每个连接创建一个独立的后端进程来处理客户端发送的SQL查询。进程还负责将客户端连接分配给空闲的后端进程,以实现负载均衡和最大化利用系统资源。Postmaster进程会响应各种系统信号,例如关闭数据库实例、重新加载配置、优雅关闭等。当某个后台进程发生异常或出错时,Postmaster负责处理错误情况,尝试恢复正常运行或记录错误日志以供管理员分析。

WalWriter

WalWriter进程是PostgreSQL数据库中的一个重要后台进程,负责将 WAL 日志缓冲区中的数据持久化到磁盘上的WAL文件中。WAL 是 PostgreSQL 中的一种关键技术,用于确保数据库的持久性和事务的原子性。WAL 日志记录了对数据库进行的所有修改操作,包括数据的插入、更新和删除,以及系统的重做和回滚信息。WalWriter 进程通过一定的算法和策略来控制数据写入的频率和方式,以确保系统性能和数据一致性之间的平衡。它会根据系统负载和IO性能等因素来动态调整写入速度,以避免过度压力导致系统性能下降。

Checkpointer

Checkpointer进程负责定期将内存中的数据写入到磁盘上的数据文件中,以确保数据库的一致性和持久性。PostgreSQL 使用了一个称为共享缓冲区的内存区域来存储数据库中的数据页当数据被修改时,它首先被写入到这个内存缓冲区中,而不是直接写入到磁盘上的数据文件中。Checkpointer 进程的主要任务之一就是将修改过的数据页定期刷新到磁盘上的数据文件中,以确保数据的持久性。在数据库发生故障或异常情况时,Checkpointer 进程的正确运行对于数据库的故障恢复和数据完整性至关重要。它会确保所有修改过的数据页都被及时刷新到磁盘上,以避免数据丢失或损坏。Checkpointer进程的设计旨在优化后台写入操作,以减少系统对磁盘的访问次数和IO负载,从而提高数据库的性能和吞吐量。它会根据实际的系统负载和IO性能等因素来动态调整数据页刷新的策略和频率。

Background Writer

Background Writer进程类似于Checkpointer进程,它也负责管理和优化数据页的刷新操作,以确保数据库的一致性、持久性和高性能运行。与Checkpointer不同,它不是定期执行刷新操作,而是根据需要和系统负载来动态调整刷新策略。Background Writer进程的设计旨在优化IO操作,减少系统的IO负载和磁盘访问次数。它会根据系统负载和性能需求等因素来动态调整数据页刷新的策略和频率。Background Writer进程还负责管理共享缓冲区中的数据页,以确保系统的LRU策略能够得到有效执行,优化内存使用和数据访问性能。它采用延迟刷新的策略,即不会立即将所有脏数据页刷新到磁盘,而是等待一段时间后才执行刷新操作,以提高系统的吞吐量和性能。

Autovacuum

Autovacuum进程负责自动管理和执行表的自动化清理和优化操作,以确保数据库的性能、空间利用率和可用性。由于 MVCC机制的存在,表中的数据会随着时间的推移逐渐膨胀,导致表的大小不断增加而性能下降。Autovacuum进程可以定期清理并回收已经过时的数据版本,防止表的膨胀问题。除了清理废弃数据外,Autovacuum进程还负责更新表的统计信息,包括表中的行数、索引信息等。这些统计信息对于查询优化器选择合适的执行计划非常重要,通过自动更新可以确保查询性能的最佳化,还会根据数据库的使用情况和表的变化情况动态调整清理和优化的策略,以适应不同表的访问模式和数据变化的频率。虽然Autovacuum进程对于数据库性能和空间利用率是非常有益的,但是在执行清理和优化操作时也会对系统造成一定的负载。因此,管理员需要根据实际情况调整Autovacuum的配置参数,以平衡性能和资源消耗之间的关系。

Stats Collector

Stats Collector 进程负责收集和维护数据库对象的统计信息,以支持查询优化器生成最佳的查询执行计划。Stats Collector进程负责定期收集数据库中各种对象的统计信息,包括表、索引、列等。这些统计信息包括但不限于行数、唯一值数量、NULL 值数量、数据分布情况等。将收集到的统计信息存储在系统表pg_statistic中,以便后续查询优化器使用。它不仅在数据库对象发生变化时收集统计信息,还会根据配置的参数定期更新已有的统计信息,以反映数据库对象的最新状态。这种动态更新保证了查询优化器在生成执行计划时能够使用最准确的统计信息。这些统计信息是 PostgreSQL 查询优化器生成最佳执行计划的重要依据之一。

Background Worker

Background Worker进程是一种特殊的后台进程,用于执行各种管理和辅助任务,以提高数据库的性能、可用性和管理效率。Background Worker进程负责执行那些需要异步执行的任务,这些任务通常是与数据库管理、监控、清理等相关的,例如定期的统计信息收集、异步的数据备份和恢复、后台任务的处理等。每个Background Worker进程都是独立运行的,它们不会阻塞主数据库进程或其他后台进程的正常运行。这种设计确保了数据库系统的稳定性和可靠性,即使某个后台任务出现问题也不会影响整个数据库的正常运行。Background Worker进程的运行由数据库系统自动管理和调度,管理员可以通过系统视图和日志文件来监控这些后台任务的运行情况,包括运行状态、执行时长、资源消耗等信息。

PostgreSQL主备复制

PostgreSQL主备复制机制是一种用于创建可靠的数据备份和故障容错解决方案的机制。其核心部分包括主服务器(Master)和备用服务器(Slave)之间的数据同步。这种机制可以提供数据冗余、负载均衡、故障恢复和数据分发等功能。

主服务器负责接收客户端的写操作(insert、update、delete)并记录这些操作的WAL(Write-Ahead Logging)日志,然后再应用到数据库中。备用服务器通过从主服务器复制WAL日志并应用到本地数据库,保持与主服务器数据一致。

备用服务器的数据更新通常会有一定的延迟,这取决于复制方式、网络延迟以及备用服务器的负载等因素。常用的复制方式是流复制和日志复制,流复制是通过在主服务器和备用服务器之间建立持久的流连接,主服务器将WAL日志实时流式传输到备用服务器,备用服务器即时应用这些日志来保持数据同步。而日志文件复制是主服务器周期性地将WAL日志文件复制到备用服务器上,备用服务器将这些日志应用到本地数据库中。在流复制中,通常可以实现较低的延迟,而在日志文件复制中,延迟可能会更高。

在主备复制环境中,可以配置自动故障转移机制,当主服务器发生故障时,自动将备用服务器提升为新的主服务器,以确保系统的连续性和可用性。通过使用多个备用服务器,并且定期进行备份,可以提高系统的容错性和灾难恢复能力,确保系统的可用性和可靠性。

主备复制还可以用于负载均衡,通过将读操作分发到备用服务器上来减轻主服务器的压力。

PostgreSQL索引机制详细介绍

PostgreSQL的索引机制是数据库系统中的关键组成部分,用于加快数据检索的速度和提高查询性能。

B-树索引

B-树索引是PostgreSQL中默认的索引类型,它使用B-树(Balanced Tree,平衡树)数据结构来加速数据检索。B-树是一种平衡树结构,其中每个节点通常具有多个子节点,这些子节点按照节点的键值有序排列。每个节点中的键值用于将树的搜索空间分割成更小的部分,以便快速定位目标值。

B-树保持平衡,即树的高度相对较小且相对均衡,这样可以保证在最坏情况下的查找时间复杂度为O(log n)。每个节点通常具有多个子节点,使得B-树在处理大量数据时更高效。

当在表的列上创建B-树索引时,数据库系统会从表中提取数据,并按照索引键值构建B-树结构。插入新数据时,数据库系统会更新B-树索引以反映新的数据结构。删除数据时,也会从索引中删除相应的键值。定期执行索引重建和优化操作可以确保索引的性能和效率。

哈希索引

PostgreSQL中的哈希索引是一种特殊的索引类型,用于加速等值查询(= 操作符)的性能。它使用哈希表(Hash Table)数据结构来组织索引数据。哈希表通过哈希函数将键值映射到哈希桶(Hash Bucket)中,以便快速查找目标键值。

哈希索引适用于等值查询操作,可以以接近常数时间复杂度(O(1))快速定位目标值。但是,相比B-树索引,哈希索引不适用于范围查询或排序操作。

当在表的列上创建哈希索引时,数据库系统会从表中提取数据,并根据定义的哈希函数构建哈希表。构建过程中,数据库系统会根据哈希函数计算每个键值的哈希码,并将键值存储在相应的哈希桶中。

在哈希索引中,多个键值可能会映射到同一个哈希桶,这种情况被称为“碰撞”。在哈希碰撞较多时,性能会产生影响。PostgreSQL使用开放地址法(Open Addressing)或链地址法(Chaining)等技术来处理哈希碰撞,确保在发生碰撞时仍然能够正确地定位到目标键值。但是,当数据量较大或者哈希函数选择不当时,哈希索引的性能还是可能会低于B-树索引。

GiST索引

GiST索引是一种多用途的索引类型,可用于支持各种不同的查询操作,例如空间数据类型、全文搜索等。

GiST索引是一种通用搜索树结构,允许基于自定义的搜索策略构建索引,每个GiST索引节点通常包含多个子节点,这些子节点可以根据不同的搜索策略组织,例如R树、B树等。

GiST索引可用于支持多种不同类型的数据,例如空间数据类型(如点、线、多边形)、全文搜索等。适用于需要支持复杂查询操作的场景,例如空间数据类型的范围查询、全文搜索中的相似度搜索等。GiST索引允许开发人员定义自己的搜索策略,以便根据特定的数据类型和查询需求构建索引。

当在表的列上创建GiST索引时,数据库系统会调用相应的搜索策略函数来构建索引,构建过程中,索引节点会根据定义的搜索策略组织数据,并确保索引的平衡性和正确性。

与其他索引类型类似,GiST索引也需要定期进行碎片整理和优化以维持其性能和效率。

SP-GiST索引

SP-GiST索引是GiST索引的一种变体,适用于空间分区数据结构。它是一种多级空间划分的数据结构,类似于R树或者Quad树,用于处理具有空间维度的数据。它将数据空间划分为多个区域,每个区域可以包含一个或多个数据项,从而实现高效的数据检索和查询操作。SP-GiST索引支持各种空间分区方法和数据结构,可以根据具体的应用场景进行定制和优化。

创建SP-GiST索引时,需要定义相应的索引操作符类和分割方法来实现数据的分区和检索。索引的构建过程涉及将数据按照空间特征进行适当的划分和组织,以便快速地进行查询操作。

处理地理空间数据时,SP-GiST索引可以用于加速空间范围查询、最近邻查询等操作。对于包含文本信息的数据,SP-GiST索引可以用于加速全文搜索和相似度匹配等操作。

构建SP-GiST索引时,需要根据具体的数据类型和查询需求选择合适的分割方法和操作符类,以实现最佳的索引性能。对于大规模数据集和复杂查询场景,可能需要对SP-GiST索引进行定期的优化和调整,以确保查询性能的稳定和高效。

GIN索引

GIN索引是基于倒排索引的数据结构,用于快速查找包含特定词条或值的文档或数据项,通过维护词条或值与对应文档或数据项之间的映射关系来实现快速的查找和过滤。GIN索引可以用于支持全文搜索、数组类型、JSONB等多种复杂数据类型的查询。

GIN索引的一个显著特点是其通用性。它不需要知道为之加速的操作是什么,而是使用为特定数据类型定义的客户端策略。这允许客户数据类型的开发带着合适的访问方法,并由该数据类型的专家实现,而不是数据库专家。

GIN接口具有高度的抽象性,只需要访问方法实现者实现被访问的数据类型的语意。GIN层本身负责处理并发性、日志和搜索树结构。为了运行GIN访问方法,需要实现四个(或五个)用户定义的方法,这些方法定义了树里面的键字的行为、键字与索引值之间的关系以及可索引的查询之间的关系。

当在表的列上创建GIN索引时,数据库系统会根据数据类型调用相应的倒排索引构建函数来构建索引。构建过程中,数据库系统会提取词条或值,并构建倒排列表以支持快速的查找和过滤操作。

与其他索引类型类似,GIN索引也需要定期进行碎片整理和优化以维持其性能和效率。

BRIN索引

BRIN索引是基于块范围的索引结构,将数据分为多个块,并为每个块维护一组摘要信息,用于快速确定包含特定范围值的数据块。BRIN索引不存储每个数据项的索引信息,而是存储每个块的摘要信息,因此在存储上占用空间较少,适合处理大数据量的有序数据。

BRIN索引的优势在于它适合处理大量数据,并且在某些特定的查询模式下(如范围查询)可以非常高效。然而,由于其工作原理,它可能不适合处理频繁删除操作的数据表,因为这可能导致索引的频繁重建。

当在表的列上创建BRIN索引时,数据库系统会将数据按照指定的块大小分组,并为每个数据块计算摘要信息,摘要信息通常包括每个数据块的最小值和最大值等统计信息。BRIN索引主要用于支持有序数据类型,例如日期、时间等。

由于BRIN索引不存储每个数据项的索引信息,因此不需要像B树索引那样频繁进行碎片整理和优化。

多列索引

PostgreSQL中的多列索引是一种索引类型,用于同时对表中的多个列创建索引,以支持复合查询和多条件查询。

多列索引可以同时考虑多个列的值,以支持复合查询操作。多列索引可以优化多条件查询,提高查询性能。

索引是根据索引中列的顺序来存储数据的。当执行查询时,PostgreSQL会尝试使用多列索引来加速查询过程,特别是当查询条件与索引中的列顺序相匹配时。如果查询条件仅涉及索引中的部分列,并且这些列是索引的前导列(即位于索引定义的最前面的列),那么索引仍然可以被有效利用。

当在表的多个列上创建索引时,数据库系统会根据指定的列顺序构建索引,多列索引可以按照指定的列顺序进行联合索引构建。

表达式索引

PostgreSQL中的表达式索引是一种特殊类型的索引,允许基于表达式的值创建索引,以支持特定的查询需求。

表达式索引是基于特定表达式计算结果的索引结构,而不是简单地索引表中的列值。表达式可以是任意合法的SQL表达式,包括列名、函数调用、运算符、常量等。

创建表达式索引时,需要指定要计算的表达式,数据库系统会计算表达式并将结果存储在索引中。可以将表达式索引与普通列索引一样使用CREATE INDEX语句来创建。

表达式索引的性能受到表达式计算的影响,如果表达式计算较为复杂或涉及大量数据,则可能影响索引的性能。应谨慎选择需要索引的表达式,以确保索引能够有效地提高查询性能。

PostgreSQL处理半结构化数据

Json

PostgreSQL提供了对JSON数据的原生支持,包括JSON和JSONB两种数据类型。JSONB是一种二进制格式的JSON,它支持索引,因此查询性能更高。PostgreSQL提供了丰富的JSON函数和操作符,使得用户可以在SQL查询中直接对JSON数据进行提取、修改和验证等操作。

XML

PostgreSQL也提供了对XML数据的支持。用户可以定义XML类型的列来存储XML文档,并使用XPath查询语言来提取XML数据中的特定元素或属性。PostgreSQL支持在查询中使用XPath函数和操作符,从而实现对XML数据的灵活处理。XML数据类型可以与GIN索引结合使用,以加速XML查询操作。

键值对

在PostgreSQL中,键值对可以通过多种方式进行处理。一种常见的方法是使用hstore扩展,它允许在单个值中存储键值对。然而,hstore扩展仅支持文本字符串作为键和值。为了使用hstore,首先需要启用该扩展,然后在表中定义hstore类型的列。插入和查询hstore数据需要使用特定的语法和函数。

另一种处理键值对的方法是使用JSON或JSONB数据类型。由于JSON本质上是由键值对组成的,因此它非常适合存储和查询键值对数据。

数组

PostgreSQL支持一维或多维的数组,数组的元素可以是任何数据类型,包括用户自定义的类型。数组类型在PostgreSQL中是一个内置的数据类型,不需要额外的扩展或配置。用户可以创建包含数组的表,并对数组进行各种操作,如插入、更新、查询等。数组类型在处理需要存储多个同类型值的场景时非常有用,如标签、权限列表等。

PostgreSQL FDW机制

PostgreSQL FDW(Foreign Data Wrapper)是PostgreSQL数据库中的一种机制,允许用户在数据库中访问和查询外部数据源的数据,就好像它们是本地表一样。这种机制使得PostgreSQL可以与其他数据库、文件系统、Web服务等外部数据源进行集成,无需将数据复制到本地数据库。

FDW支持多种外部数据源,包括但不限于其他SQL数据库(如MySQL、Oracle等)、NoSQL数据库,以及CSV或JSON等文件格式。通过安装和配置适当的FDW扩展(如mysql_fdw、oracle_fdw等),PostgreSQL可以连接到这些外部数据源并进行数据访问和操作。

FDW机制由以下四个核心组件构成:

1.FDW(Foreign Data Wrapper):Foreign Data Wrapper是PostgreSQL中用于实现外部数据源连接和查询的扩展模块,它提供了与外部数据源进行通信的接口。每种外部数据源都需要对应的FDW来实现与PostgreSQL的集成。PostgreSQL自带了一些常见的FDW,如“postgres_fdw”用于连接其他PostgreSQL数据库,“mysql_fdw”用于连接MySQL数据库,也支持用户编写自定义的FDW来支持特定的外部数据源。

2.外部服务器(Foreign Server):外部服务器是连接到外部数据源的实体,它定义了与外部数据源的连接信息,如主机名、端口、数据库名称等。在PostgreSQL中,外部服务器通过create server语句进行定义。

3.用户映射(User Mapping):用户映射将数据库用户映射到外部数据源的用户或角色,用于权限管理和身份认证。通过用户映射,可以指定数据库用户在外部数据源上的身份认证信息。用户映射使用create user mapping语句创建。

4.外部表(Foreign Table):外部表是在数据库中定义的表,实际上对应于外部数据源中的数据。外部表的结构和数据存储在外部数据源中,而在PostgreSQL中只定义了表的元数据信息。外部表通过create foreign table语句创建,可以指定外部数据源中的表结构、列映射关系等。

这些核心组件共同构成了PostgreSQL FDW机制的基础,使得PostgreSQL能够与各种外部数据源进行集成,实现跨数据库、跨平台的数据访问和查询。

PostgreSQL的优缺点

优点:

1.PostgreSQL支持大量的SQL标准,包括复杂查询、视图、触发器、事务完整性、外键约束等,同时也支持许多扩展功能,与各种应用和工具具有良好的兼容性。它还提供了一系列高级功能,如全文搜索、地理空间数据支持、数组和JSON数据类型等,使得PostgreSQL能够满足各种复杂的应用需求。

2.PostgreSQL以其高可靠性和稳定性著称。它采用多版本并发控制(MVCC)技术,能够处理高并发访问和大规模数据集,保证数据的一致性和可靠性。这种特性使得PostgreSQL在数据完整性要求极高的行业中成为首选的数据库管理系统。

3.PostgreSQL具有模块化架构,允许用户通过扩展来添加新的数据类型、函数、操作符等。此外,它还支持自定义聚合函数和索引类型,使得数据库系统可以根据具体需求进行定制和优化。

缺点:

1.由于其功能和灵活性,PostgreSQL在某些情况下可能会消耗较多的系统资源。对于性能要求较高的应用,可能需要更多的硬件资源支持。

2.由于PostgreSQL支持大量的功能和特性,这也使得它的系统相对复杂。在某些情况下,用户可能需要深入了解其内部机制才能有效地解决问题或进行性能优化。

3.PostgreSQL在某些情况下可能对存储空间的管理不够高效,特别是在处理大量文本数据或大型二进制数据时。

  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值