作者简介,普修罗双战士,一直追求不断学习和成长,在技术的道路上持续探索和实践。
多年互联网行业从业经验,历任核心研发工程师,项目技术负责人。
欢迎 点赞✍评论⭐收藏
大数据知识专栏学习
大数据知识云集 | 访问地址 | 备注 |
---|---|---|
大数据知识点(1) | https://blog.csdn.net/m0_50308467/article/details/134989969 | 大数据专栏 |
大数据知识点(2) | https://blog.csdn.net/m0_50308467/article/details/135109787 | 大数据专栏 |
大数据知识点(3) | https://blog.csdn.net/m0_50308467/article/details/135164698 | 大数据专栏 |
大数据知识点(4) | https://blog.csdn.net/m0_50308467/article/details/135164812 | 大数据专栏 |
大数据知识点(5) | https://blog.csdn.net/m0_50308467/article/details/135164812 | 大数据专栏 |
大数据知识点(6) | https://blog.csdn.net/m0_50308467/article/details/135313184 | 大数据专栏 |
大数据知识点(7) | https://blog.csdn.net/m0_50308467/article/details/135322179 | 大数据专栏 |
大数据知识点(8) | https://blog.csdn.net/m0_50308467/article/details/135323118 | 大数据专栏 |
以下是一些常见的Hive版本:
版本 | 发行日期 | 注释 |
---|---|---|
Hive 0.13.0 | 2014年3月 | 该版本引入了最新的Hive-on-Tez执行引擎 |
Hive 1.2.0 | 2015年7月 | 添加了许多新功能和改进,包括CBO(Cost-Based Optimizer) |
Hive 2.3.0 | 2017年4月 | 支持ACID(原子性、一致性、隔离性和耐用性)表操作 |
Hive 3.1.0 | 2018年6月 | 引入了Hive-on-Spark执行引擎,以及更好的性能和优化 |
Hive 3.1.2 | 2019年8月 | 修复了一些BUG并进行了一些改进 |
Hive 3.1.4 | 2020年6月 | 改进了性能和稳定性,修复了一些重要的问题 |
Hive 3.1.5 | 2021年1月 | 修复了一些Bug,并进行了一些改进 |
Hive 4.0.0 | 2022年7月 | 添加了许多新功能和改进,包括Hive Indexes和SQL兼容性 |
这只是一部分Hive版本的列表,仅用于参考。请注意,随着时间的推移,Hive不断进行更新和改进,可能会有新的版本发布。你可以在Hive官方网站或源代码仓库中找到更多关于不同版本的详细信息。
数据倾斜是指在数据处理过程中,某些特定的数据分布不均匀,导致计算资源无法充分利用或某些节点负载过重的情况。数据倾斜通常是由以下几个原因导致的:
1. 数据分布不均匀:数据在分布式系统中的分布不均匀,某些键值(或分区)的数据量远大于其他键值,导致负载不平衡。
2. 键冲突:某些键值可能存在冲突,即多个键值被哈希到同一个分区/节点,造成该分区/节点负载过重。
3. 数据倾斜操作:某些特定的数据操作(如JOIN、GROUP BY等)可能引发数据倾斜,特定键值集中在某个操作中。
针对数据倾斜的问题,可以采取一些解决办法来进行优化:
1. 预处理和数据调整:
数据预处理:对数据进行预处理和清洗,确保数据的分布较为均匀。
数据重分区:根据键值进行数据重分区,将数据均匀分布到不同的分区,以平衡负载。
2. 使用随机前缀:
3. 使用Salting技术:
4. 倾斜数据单独处理:
针对特定的倾斜键值或操作,可以对其进行单独处理。例如,对于JOIN操作,可以将倾斜键的数据单独拆分成多个任务,以减轻倾斜的影响。
使用广播变量:将小数据集广播到所有节点上,避免特定操作中的倾斜影响。
5. 动态优化:
6. 数据倾斜限制与优化:
数据倾斜限制:设置阈值,当某个分区/节点负载超过阈值时,限制该分区/节点的处理数据量,将其分配给其他节点进行处理。
数据倾斜优化:基于统计信息和采样分析,将倾斜较严重的键值进行重分布、过滤或提前处理。
这些解决办法可以根据具体的数据倾斜情况进行选择和组合使用,以达到负载均衡和更好的性能。然而,解决数据倾斜问题可能是一项挑战性的任务,需要根据具体的场景和数据特点进行调整和优化。
Hive使用分区是为了在大规模数据集上提高查询效率和简化数据管理。通过将数据划分为不同的分区,可以减少需要扫描的数据量,从而提高查询性能和降低IO开销。
在Hive中,可以使用以下几种类型的分区:
1. 静态分区(Static Partitioning):
2. 动态分区(Dynamic Partitioning):
3. 分桶(Bucketing):
分区和分桶可以结合使用,以更好地组织和管理数据。通过避免全局扫描,分区和分桶可以显著提高查询效率,并支持更复杂的数据分析操作。
需要注意的是,分区和分桶都需要在创建表时进行定义,并选择适当的分区键和分桶列。数据导入和查询时需要遵循相应的分区和分桶策略,以便正确获得各个分区或桶的数据。
在Hbase中,rowKey的设计非常重要,不仅影响到HBase数据的访问效率,而且还关系到数据分布的均衡性和可扩展性。以下是一些rowKey设计的最佳实践:
1. 选择唯一的ID:
为了保证rowKey的唯一性,可以选择使用全局唯一ID,比如使用UUID或时间戳作为rowKey。
选择自然键时,确保数据的分布不会倾斜,可以使用哈希函数对其哈希,让数据分布均匀。
2. 排序合理:
HBase中的行是一个有序的字节数组。为了提高扫描和遍历的效率,通常将rowKey按照字典序进行排序,保证相邻的行在物理存储上也是相邻的。
另外,如果有范围查询的需求,可以将相关字段包含在rowKey中,并将其排序,以便更高效地检索数据。
3. 避免随机写:
将频繁更新的数据分散到多个Region中,可以减少HBase系统的压力。
可以使用“随机前缀”的方法,让HBase自动完成数据分散。
对于列簇的设计,需要考虑以下两个方面:
1. 区分数据频率和访问模式:
列簇应该根据数据使用频率和访问模式进行设计,更频繁地访问的数据应该放在比较小的列簇中。
尽量避免在同一个列族中存储访问频率、访问量差别很大的数据,避免一些数据扫描的开销。
2. 避免“超级列族”和长列簇:
不建议过多使用“超级列族”,这会带来一些维护和查询效率上的问题。在设计时应该尽量少用或者避免使用。
长列簇会导致数据文件上的行数变少,使得随机读和其他操作的效率降低,应该避免。
总的来说,rowKey和列簇的设计需要根据具体的场景来决定。需要根据数据的属性和业务需求,对其进行适当的优化和调整,以达到更好的性能和可扩展性。
Varhadoop是一个用于管理Hadoop集群上的进程标识符(PID)和日志的工具。它的主要用途是为了简化和优化Hadoop集群的运维和调试工作。
Varhadoop的功能包括:
1. 进程标识符(PID)管理:
Varhadoop可以自动记录和管理Hadoop集群中各个组件(如NameNode、DataNode、ResourceManager、NodeManager等)的进程标识符(PID)。
Varhadoop可以启动、停止、重启和监控这些组件的进程,并能够自动处理进程的异常退出和错误。
2. 日志管理:
Varhadoop能够自动聚合、收集和处理Hadoop集群中各个组件的日志,包括标准输出和错误日志。
Varhadoop提供日志检索、统计和过滤等功能,使得对Hadoop集群的日志进行管理和分析更加方便和高效。
3. 健康度检测:
Varhadoop能够自动监控Hadoop集群中各个组件的运行状态,检测并报告任何错误或异常情况,帮助管理员及时发现和解决问题。
Varhadoop还提供对Hadoop集群资源利用率、吞吐量和性能等指标的监控和统计。
总的来说,Varhadoop是一个集中化、自动化和集成化的工具,它可以帮助管理员更轻松地管理和维护Hadoop集群,提高集群的可靠性、稳定性和性能。通过Varhadoop,管理员可以更好地监控和管理Hadoop集群的各个组件,并快速定位和解决潜在的问题。
当Hadoop集群中的NameNode发生故障或意外关闭时,需要进行NameNode的恢复过程。恢复过程主要包括以下几个步骤:
1. 启动NameNode进程:
首先,需要启动NameNode进程,可以使用Hadoop的脚本或工具来启动NameNode。
在启动过程中,NameNode会从持久化存储(如本地磁盘或远程存储系统)中读取之前保存的元数据。
2. 恢复内存数据结构:
一旦NameNode进程启动,它会使用之前保存的内存数据结构(如FsImage和Edit Logs)来构建命名空间的内存表示。
FsImage是一个镜像文件,包含文件系统的完整状态,而Edit Logs是NameNode将文件系统中的任何更改追加到日志文件中。
3. 日志重放(Log Replay):
在内存数据结构恢复完成后,NameNode会对Edit Logs进行重放操作,将其中的操作重新应用到内存数据结构中。
通过重放日志,NameNode可以将之前未完成的写操作和元数据更改应用到命名空间内存表示中,确保数据的一致性。
4. 完成恢复:
一旦Edit Logs的日志重放完成,NameNode恢复过程就基本完成了。
此时,NameNode会向DataNodes发送心跳请求,以获取存储在它们上面的块报告,从而获取数据块的位置和状态信息。
5. 客户端连接与操作:
一旦NameNode恢复,客户端就可以与其重新建立连接,并继续进行文件系统的读写操作。
此时,NameNode会为每个客户端请求提供最新的文件系统视图,以保持集群中的一致性。
需要注意的是,NameNode的恢复过程可能需要较长的时间,特别是如果Edit Logs非常大或系统的负载较高时。因此,在设计Hadoop集群时,可以采取一些措施来加速和优化NameNode的恢复过程,如增加硬件资源、优化网络连接等。此外,建议使用备份和高可用性机制来防止单点故障,并提供更可靠的NameNode恢复能力。
在Spark中,可以通过调用cache()
方法将RDD(弹性分布式数据集)缓存在内存中,以便在后续操作中快速访问。然而,并不是所有的RDD都适合进行缓存,以下是一些常见的情况下适合缓存的RDD:
1. 频繁重用的RDD:
如果一个RDD在后续的多个操作中被频繁使用,那么将其缓存在内存中可以避免重复计算,提高性能。
例如,多次对同一个RDD进行转换或多个action操作依赖于同一个RDD的结果。
2. 窄依赖的RDD:
窄依赖的RDD是指子RDD只有一个父RDD,这样的RDD在计算过程中可以更高效地重用数据,因为不需要进行Shuffle操作。
缓存窄依赖的RDD可以避免重复计算,并加速后续操作。
3. 迭代算法中的中间数据:
在迭代算法中,可能会多次使用相同的数据进行迭代计算,将迭代过程中产生的中间结果缓存可以提高迭代算法的性能。
例如,在机器学习的迭代算法中,将迭代过程中的特征矩阵或模型参数缓存可以加速算法的收敛。
需要注意的是,缓存RDD会占用集群的内存资源,因此需要根据集群的可用内存和数据大小进行合理的缓存管理,避免因为内存不足导致缓存失效或性能下降。
另外,RDD的缓存也可以通过不同级别的持久化进行,包括MEMORY_ONLY
(缓存在内存中)、MEMORY_AND_DISK
(缓存在内存和磁盘中)、MEMORY_ONLY_SER
(序列化对象的内存缓存)等选项,根据实际情况选择适合的缓存级别。
Hive是建立在Hadoop之上的数据仓库工具,它提供了一种类似于SQL的查询语言(HiveQL)来处理和分析大规模数据。Hive使用元数据来描述数据表、分区、列等信息,并支持多种方式来保存元数据。下面是Hive常用的元数据存储方式和它们的优点:
1. 嵌入式Derby数据库方式:
Hive可以使用内置的Derby数据库来保存元数据,默认情况下会把元数据存储在本地文件系统的/metastore_db
目录下。
优点:简单、易用,不需要额外的配置和依赖。
缺点:不适合高并发和大规模的生产环境,性能和扩展性较差。
2. 外部数据库方式:
Hive还支持使用外部数据库(如MySQL、PostgreSQL等)来保存元数据。
优点:能够支持更高的并发和扩展性,适用于生产环境。
缺点:配置和管理外部数据库可能需要额外的工作量,增加了部署和维护成本。
3. 远程数据库方式:
Hive可以通过配置Hive Metastore来连接到远程的数据库服务器,将元数据存储在远程数据库中。
优点:能够集中管理多个Hive实例共享的元数据,方便统一管理和维护。
缺点:需要配置和管理远程数据库服务器,可能会增加网络延迟和依赖性。
4. 自定义元数据存储方式:
Hive还提供了自定义元数据存储方式的扩展机制,可以根据特定需求实现自定义的元数据存储方案。
优点:灵活自定义,可以根据实际需求选择合适的元数据存储方式。
缺点:需要开发和维护自定义存储方案,工作量可能较大。
需要根据实际情况和需求选择适合的元数据存储方式。对于小规模和测试环境,嵌入式Derby数据库方式是最简单方便的选择;而对于生产环境和大规模部署,使用外部数据库或远程数据库方式能够提供更好的性能和扩展性。此外,自定义元数据存储方式适用于具有特殊需求的场景。
以下是各种 Hive 元数据存储方式的优点和缺点的表格说明:
存储方式 | 优点 | 缺点 |
---|---|---|
嵌入式 Derby 数据库方式 | 简单、易用 | 不适合高并发和大规模生产环境,性能和扩展性较差 |
外部数据库方式 | 支持高并发和扩展性 | 需要额外的配置和管理外部数据库,增加部署和维护成本 |
远程数据库方式 | 集中管理多个 Hive 实例共享的元数据,方便统一管理和维护 | 需要配置和管理远程数据库服务器,可能增加网络延迟和依赖性 |
自定义元数据存储方式 | 灵活自定义 | 需要开发和维护自定义存储方案,可能工作量较大 |
Hive提供了一个基于SQL的授权模式,在授权方面主要涉及以下三个层次:
1. 数据库级别(Database level):
用户可以对整个数据库进行授权,包括创建、删除、修改和查询等操作。
2. 表级别(Table level):
用户可以对单个表进行授权,包括对表的读、写、修改、删除等操作,也可以在表级别上定义列层次授权(Column level)。
3. HDFS文件级别(HDFS file level):
Hive表通常是存储在HDFS上的,因此用户可以在HDFS文件级别上进行授权,包括读写文件、创建和删除目录等操作。
在Hive中,授权的语法格式如下:
GRANT <privilege> ON <object> TO <user>[, <user> ...] [WITH GRANT OPTION]
其中,
表示授权的权限,如SELECT、INSERT、UPDATE、DELETE等;表示授权的对象,可以是数据库、表或文件等;
表示被授权的用户或角色名;WITH GRANT OPTION
表示授予被授权者将权限授予其他用户或角色的权利。
例如,授权用户user1
对数据库mydb
进行SELECT和INSERT操作的语句如下:
GRANT SELECT, INSERT ON DATABASE mydb TO user1;
Hive还支持授权撤销(REVOKE)操作,用于收回已有的授权,其语法与GRANT类似:
REVOKE ON
例如,撤销用户user1
对数据库mydb
进行SELECT和INSERT操作的语句如下:
REVOKE SELECT, INSERT ON DATABASE mydb FROM user1;
通过授权和撤销操作,可以在Hive中实现对不同用户和角色的精细化权限管理,确保数据安全和隐私。
在Hadoop中,有几个地方使用了缓存机制,它们的作用如下:
1. HDFS 缓存:
HDFS支持在数据节点(DataNode)上设置缓存目录,即通过dfs.datanode.data.dir
配置项指定的目录。这个缓存机制可以帮助提高数据读取的效率。当客户端请求读取某个文件时,如果该文件已经被缓存到数据节点上,那么读取操作可以直接在本地缓存中进行,提高读取速度。
2. MapReduce 输出缓存:
在MapReduce任务中,可以将任务的输出缓存到本地磁盘或内存中,这样可以避免重复计算和IO开销。输出缓存可以通过配置mapreduce.job.output.compression
和mapreduce.job.output.compress.codec
来启用。
3. MapReduce 分布式缓存:
分布式缓存是将一些数据或文件缓存在各个节点上,使得任务可以从本地加载这些数据而不需要通过网络传输。这样可以减少网络传输的开销和数据的读取延迟。分布式缓存可以通过DistributedCache
类实现,可以通过addCacheFile()
方法将文件添加到缓存中,然后可以在Mapper和Reducer任务中使用。
这些缓存机制的作用是提高数据访问和计算效率,减少IO开销和网络传输开销,从而加速数据处理和计算的速度。通过合理使用缓存机制,可以提高Hadoop的性能和效率。
Hadoop由许多不同的组件和进程组成,每个组件和进程都有不同的名称。以下是一些常见的Hadoop进程名称:
1. NameNode:
NameNode是HDFS的主要组件之一,负责管理文件系统的命名空间和元数据。NameNode的进程名称为namenode
。
2. DataNode:
DataNode是HDFS的另一个关键组件,负责存储和管理实际的数据块。每个数据节点上都运行着一个DataNode进程,其进程名称为datanode
。
3. SecondaryNameNode:
SecondaryNameNode是NameNode的辅助节点,用于定期合并fsimage和edits日志,以减少NameNode的恢复时间。SecondaryNameNode的进程名称为secondarynamenode
。
4. ResourceManager:
ResourceManager是YARN的主要组件之一,负责资源的分配和管理。ResourceManager的进程名称为resourcemanager
。
5. NodeManager:
NodeManager是YARN的节点管理器,负责在每个节点上启动和监视容器。每个节点上都运行着一个NodeManager进程,其进程名称为nodemanager
。
6. JobTracker:
JobTracker是Hadoop 1.x版本中的资源管理器,负责作业的调度和任务的跟踪。JobTracker的进程名称为jobtracker
。
7. TaskTracker:
TaskTracker是Hadoop 1.x版本中的任务跟踪器,负责执行和监控作业的任务。每个节点上都运行着一个TaskTracker进程,其进程名称为tasktracker
。
注意:上述进程名称是Hadoop的默认命名,实际部署中可以根据需求进行相应的命名和配置。
HDFS(Hadoop分布式文件系统)是Hadoop的核心组成部分之一。其主要作用是在分布式环境下存储和管理大量的数据。以下是HDFS的读写流程:
1. HDFS读流程:
(1)客户端向NameNode发送读请求:客户端想要读取某个文件时,首先要知道文件的块分布情况,因此它会向NameNode发送文件的路径和读取请求。
(2)NameNode返回块的位置信息:NameNode会查询它的元数据信息(保存在内存中)来找到特定文件的每个块的位置,并返回块所在的数据节点列表给客户端。
(3)客户端发送读请求到最近的数据节点:客户端收到块所在的数据节点列表后,会选择最近的一个数据节点来发送读请求。
(4)数据节点返回所需数据块:数据节点收到读请求后,会将数据块从磁盘读取到内存中,并将数据块发送给客户端。
(5)客户端将数据块拼接成完整文件:客户端收到块后,会将各个块拼接在一起,以生成完整的文件。
2. HDFS写流程:
(1)客户端向NameNode发送写请求:客户端要写入文件时,会向NameNode发送文件的路径和写入请求。
(2)NameNode返回可用数据节点列表:NameNode会返回一组可以存储文件的数据节点列表给客户端。
(3)客户端选择数据节点:客户端会从可用节点列表中选择一个数据节点来写入数据。
(4)客户端向数据节点发送写请求:客户端向目标数据节点发送写入请求,并将要写入的数据块传输给节点。
(5)数据节点确认块写入:数据节点收到写请求后,将副本写入磁盘,并通过数据节点确认写入操作的完成。
(6)数据节点向下一个数据节点复制块:如果有多个数据节点,那么数据节点会将数据块通过管道逐步传递给下一个数据节点,直到所有的数据块副本都被写入并确认。
(7)NameNode更新元数据:每个块被成功地写入后,NameNode会更新文件元数据信息,记录存储块的位置和复制因子等信息。
HDFS系统的读写流程较为复杂,但通过逐步了解每个步骤,可以更好地理解HDFS的运行机制。
在Hadoop中,Hadoop_PID_DIR是一个环境变量,用于指定Hadoop进程的PID文件存放目录。PID代表进程ID(Process ID),它是操作系统分配给正在运行的每个进程的唯一标识符。
PID文件是一个文本文件,包含了Hadoop进程在操作系统中分配的进程ID。当启动Hadoop进程时,Hadoop会将进程ID写入PID文件,以便后续的管理和控制操作。
使用PID文件可以方便地通过PID文件所在目录来查找和管理Hadoop进程。 例如,可以使用PID文件检查进程是否正在运行,也可以使用PID文件中的进程ID来手动终止某个特定的Hadoop进程。
总之,Hadoop_PID_DIR中的PID指的是进程ID,而Hadoop_PID_DIR则指定了存放Hadoop进程PID文件的目录。
RDD(Resilient Distributed Dataset)是Apache Spark中的核心抽象,用于分布式计算。尽管RDD是强大和灵活的,但它也有一些缺陷,包括:
1. 内存开销:由于RDD需要在内存中存储数据,因此对于大规模数据集来说,RDD可能占用大量的内存空间。
2. 磁盘IO:当内存无法满足需求时,RDD可能需要将数据写入磁盘进行持久化,这会引入额外的磁盘IO操作,可能导致性能下降。
3. 垃圾回收:在RDD中,每个转换操作都会生成一个新的RDD,这会导致内存中存在大量的临时对象,增加了垃圾回收的开销。
4. 不支持复杂的数据类型:RDD对于复杂的数据类型,如图形和树状结构,并不友好,需要自定义序列化和反序列化的操作。
5. 对磁盘存储敏感:由于RDD的特性,当发生故障导致丢失一部分数据时,需要重新计算所有的转换操作,而且可能需要从磁盘读取丢失的数据,降低了容错性和性能。
6. 难以优化:RDD是一个黑盒子,不像传统的查询优化器可以通过统计信息进行优化。因此,无法在编译时对RDD进行优化,需要通过手动调整和重构代码来改善性能。
尽管RDD存在一些缺陷,但Spark团队已经意识到这些问题,并在后续版本中引入了其他数据结构和优化技术,如DataFrame和Dataset,以解决这些问题。
要在2.5亿个整数中找出不重复的整数,可以使用哈希算法。具体实现逻辑如下:
将所有的整数分成若干组,每组分别进行哈希计算。
对于每个哈希值,只保留出现次数为1的整数。
将每个组中不重复的整数合并起来,即可得到全部不重复的整数。
以下是Java实现代码:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.BitSet;
public class FindUniqueNumbers {
public static void main(String[] args) throws IOException {
int n = 250000000; // 整数总数
int m = 10000000; // 每组最大整数数目
int k = n / m; // 总共分为的组数
BitSet bitSet = new BitSet(n);
for (int i = 0; i < k; i++) {
int[] nums = new int[m];
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
for (int j = 0; j < m; j++) {
String line = reader.readLine();
int num = Integer.parseInt(line);
nums[j] = num;
bitSet.set(num);
}
reader.close();
for (int num : nums) {
bitSet.clear(num);
}
}
for (int i = 0; i < n; i++) {
if (bitSet.get(i)) {
System.out.println(i);
}
}
}
}
以下是Python实现代码:
import mmap
n = 250000000 # 整数总数
m = 10000000 # 每组最大整数数目
k = n // m # 总共分为的组数
bit_set = bytearray(n // 8 + 1)
for i in range(k):
with open("data.txt", "r+b") as f:
mm = mmap.mmap(f.fileno(), length=m*4, offset=i*m*4)
nums = []
for j in range(m):
num = int.from_bytes(mm.read(4), byteorder='big')
nums.append(num)
offset = num // 8
bit = 1 << (num % 8)
bit_set[offset] |= bit
for num in nums:
offset = num // 8
bit = 1 << (num % 8)
bit_set[offset] &= ~bit
for i in range(n):
offset = i // 8
bit = 1 << (i % 8)
if bit_set[offset] & bit:
print(i)
以上代码实现了在2.5亿个整数中找出不重复的整数,保证了不重复的整数只出现1次,并且使用了哈希算法,使得内存占用非常小,可以在内存不足的情况下运行。
以下是使用PHP实现在2.5亿个整数中找出不重复的整数的代码:
function findUniqueNumbers($filename, $n, $m) {
$k = $n / $m; // 总共分为的组数
$bitSet = array_fill(0, ($n / 8) + 1, 0);
for ($i = 0; $i < $k; $i++) {
$nums = array();
$file = fopen($filename, 'rb');
fseek($file, $i * $m * 4);
for ($j = 0; $j < $m; $j++) {
$num = unpack('l', fread($file, 4))[1];
$nums[] = $num;
$offset = $num >> 3;
$bit = 1 << ($num % 8);
$bitSet[$offset] |= $bit;
}
fclose($file);
foreach ($nums as $num) {
$offset = $num >> 3;
$bit = 1 << ($num % 8);
$bitSet[$offset] &= ~$bit;
}
}
$uniqueNumbers = array();
for ($i = 0; $i < $n; $i++) {
$offset = $i >> 3;
$bit = 1 << ($i % 8);
if ($bitSet[$offset] & $bit) {
$uniqueNumbers[] = $i;
}
}
return $uniqueNumbers;
}
$filename = 'data.txt';
$n = 250000000; // 整数总数
$m = 10000000; // 每组最大整数数目
$uniqueNumbers = findUniqueNumbers($filename, $n, $m);
foreach ($uniqueNumbers as $number) {
echo $number . "\n";
}
上述代码使用了与之前的Java和Python版本相同的思想和逻辑。在每组整数中,使用一个位数组(bitSet)来表示整数的出现情况,再根据位数组找出不重复的整数。由于PHP的数组索引可以是任意整数,所以bitSet数组中的索引使用整数的方式,而不是字节偏移。最后输出所有不重复的整数。
请将2.5亿个整数保存在名为"data.txt"的文件中,并确保文件中的整数数据以4字节长的二进制形式存储。