IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

最新文章

采集自各技术站点的近期文章。

IT DevOps/ 2013-07-31 12:51:39 / 累计浏览 3,110

vi 编辑文件时"Terminal too wide"问题的解决

这篇文章讲的是使用vi编辑器时遇到的一个经典问题——在终端执行vi命令后,突然弹出一行“Terminal too wide”报错,导致无法正常编辑。 作者从实际遇到的这个报错场景切入,指出了问题的根源:这是由于终端环境的默认列数(columns)设置超过了某个平台允许的最大值。例如,在作者的电脑上,通过 `stty -a` 命令可以看到默认设置了171列。当通过SSH等方式连接到远程主机,或在特定环境下,这个过大的列数设置就会触发vi的保护机制,拒绝打开文件。 解决方法其实非常直接:在出现报错的终端中,运行 `stty columns 132` 命令,将列数调整到一个安全的范围内(比如常见的132列),然后再尝试用vi打开文件,问题即可解决。文章也提到,用户可以进一步修改本地终端的缺省列数设置以避免此问题反复发生。这是一个典型的、由终端环境配置不兼容引发的小麻烦,解决它只需一行简单的命令。

本机暂存
IT DevOps/ 2013-07-30 13:50:50 / 累计浏览 8,547

找回linux丢失的磁盘空间

这篇讲的是服务器磁盘空间莫名“消失”的一次典型排查。作者发现 `df` 命令显示磁盘使用率接近 100%,但用 `du` 统计具体目录占用时,两者结果却相差甚远,这显然不正常。 在排除了常见的“挂载点覆盖”问题后,作者将矛头指向了另一个经典场景:已删除的文件仍被进程占用,导致其占用的空间无法释放,且 `du` 也统计不到。通过 `lsof | grep deleted` 命令,果然揪出了一个躲在后台、持续写入的巨大日志文件。 解决方法很直接:终止那个持有文件句柄的进程,磁盘空间随即开始逐步释放。重启该进程后,日志也恢复正常写入。整个过程清晰地展示了 Linux 下磁盘空间“丢失”的一个常见原因及其高效定位与解决方法,对于运维人员非常有参考价值。

本机暂存
IT 后端/ 2013-07-30 13:50:15 / 累计浏览 2,651

ext4+delalloc造成单次写延迟增加的分析

这篇讲的是淘宝内核组在将线上系统升级到Ext4文件系统后,发现应用写操作延迟异常增大的故障。根源在于Ext4的新特性“延迟分配”(delalloc)。 简单来说,delalloc为了优化后续的顺序访问性能,将原本每次写操作都会进行的磁盘块分配过程,推迟到了系统批量回写数据时才进行。这导致了一个关键的锁竞争问题:回写进程在批量分配磁盘块时需要持有排他写锁(i_data_sem),这个过程可能耗时较长(例如约30秒一次)。如果此时有应用程序发起新的写操作,它就必须等待这把锁释放,从而导致单次写操作的延迟被显著拉高。 作者通过fio工具进行了量化测试:开启delalloc后,虽然写操作的平均延迟更低(5.86微秒 vs 7.00微秒),但最大延迟却飙升到了193毫秒,是关闭时(16毫秒)的10倍以上。这清晰地说明了delalloc“集中处理”带来的长尾延迟问题。 对于使用Buffer IO进行追加写、不主动刷新数据且对延迟敏感的应用,这个问题会尤为突出。文章给出的解决方法是在挂载时加上`nodelloc`参数来关闭此特性。

本机暂存
IT 设计/ 2013-07-30 13:46:42 / 累计浏览 1,909

万般设计为阅读【译文】

这篇文章从网页设计师常见的“字体审美陷阱”切入,直指一个核心矛盾:许多设计师沉迷于选择漂亮字体和实现炫酷的OpenType特性,却忽略了排版最根本的目的——服务于阅读本身。 作者认为,优秀的阅读体验建立在深入的项目调研之上。他引导读者跳出自我审美,去关注三个关键维度:首先是“内容”,需要评估文章的可理解性、阅读时间和结构元素;其次是“环境”,必须面对Windows系统字体渲染、多分辨率屏幕适配等现实挑战;最后是“用户”,要考虑包括阅读障碍者在内的广泛群体的真实需求。文中不仅提到了Flesch易读性测试等具体评估工具,还对比了衬线与无衬线字体、应用界面与长文阅读的不同设计策略。 这篇文章的价值在于,它将排版设计从一种视觉艺术拉回到了严谨的功能工程范畴。它提醒设计师,真正的设计功力往往体现在那些“看不见”的细节中——比如确保文字在不同设备上清晰可辨,或是让复杂内容通过合理的节奏变得易于消化。最终,好的排版不是为了让设计圈惊叹,而是为了让普通读者能毫无障碍地获取信息。

本机暂存
IT 设计/ 2013-07-30 13:43:57 / 累计浏览 2,739

复杂产品的响应式设计【流程篇】

这篇讲的是,如何让一个拥有十多个页面的复杂产品(以“玩客”为例)真正、系统地实现全站响应式设计。文章没有停留在理念层面,而是给出了从信息架构到最终测试的完整六步协作流程。 作者首先强调响应式是“设计先行”,流程始于交互设计师明确内容策略与页面分类(如列表页、详情页、操作页)。关键思路是“移动优先”,先设计手机端框架,再拓展至平板和PC端,以此梳理出清晰的响应模式与流体栅格系统。 流程中一个创新的实践是“风格拼贴稿”。在完成PC端模块设计后,视觉和前端不是等待全部设计定稿,而是提前用控件、组件拼贴出模拟页面,统一定义风格并实现组件库代码。这极大提升了多设计师、多前端协作的效率与一致性,也便于后期维护。文章最后指出,在确定核心框架与风格后,拓展其他设备设计稿的工作量远比预想的小,并提醒需尽早与开发协商服务端响应(RESS)策略以优化性能。 整个流程为跨职能团队提供了清晰的行动路线图,证明复杂产品的响应式设计有章可循。

本机暂存
IT 后端/ 2013-07-30 13:41:13 / 累计浏览 3,894

开源PHP监控扩展:witness简介

这篇讲的是一个专为PHP环境设计的开源监控扩展——witness。它瞄准的是PHP多进程、多机器部署架构下,难以追踪和排查特定用户请求问题的痛点。当线上出现只影响个别用户的故障时,传统加日志的方式往往效率低下且可能引入新问题。 witness的解决方案巧妙而直接:它作为一个底层扩展嵌入PHP引擎,无需修改业务代码。核心能力在于可以通过设置特定的cookie,对来自目标用户的请求进行精准监控。它提供了两种核心模式:trace模式记录完整的函数调用流,像“拍视频”一样还原过程;dump模式则抓取当前调用栈的详细状态,如同“拍照片”保留瞬间细节。 文章详细介绍了系统的三层架构(扩展、数据传输、数据展示),以及具体的安装、配置步骤。扩展以配置项控制行为,如监控深度、是否记录内置函数等,灵活度很高。数据最终会汇总,便于后续可视化分析。 总的来说,witness提供了一套轻量且高性能的非侵入式方案,让PHP开发者能在复杂分布式环境中,更精准、高效地定位那些“幽灵般”的个别用户问题。项目已在GitHub开源。

本机暂存
IT 开发者/ 2013-07-30 13:39:02 / 累计浏览 13,914

面向“接口”编程和面向“实现”编程

这篇讲的是编程中一个经典却容易被忽视的原则:我们应该面向“接口”编程,而不是面向“实现”编程。 作者从重读《设计模式》一书出发,结合自己对 Rust 等新语言的思考,通过一个生动的例子对比了这两种方式。如果直接写一个 `start_fire` 函数并让它接收具体的 `Log` 类型参数,那么想传入同样具有 `burn()` 方法的 `Book` 对象时就会报错,因为函数绑定的是具体实现类型。这种方式导致了代码重复和僵化。 而“面向接口”编程则提供了优雅的解决方案。文章定义了 `Burnable` 接口(在 Rust 中是 trait),让 `Log` 和 `Book` 都实现它。于是 `start_fire` 函数可以泛化为接受任何满足 `Burnable` 接口的类型 `T`。这样,无论是木头还是书,甚至是未来新增的、实现了该接口的“纸张”对象,都能无缝地传入该函数,代码的复用性和扩展性大大增强。 这个对比清晰地展示了:面向实现导致紧耦合,而面向接口则通过抽象带来了灵活性。虽然并非所有场景都适用,但遵循这一原则能帮助我们写出更易于维护和扩展的代码,这正是其强大价值所在。

本机暂存
IT 设计/ 2013-07-30 13:37:55 / 累计浏览 2,244

解决问题,而不是做产品

这篇讲的是淘宝商品体系里“品牌”这个属性引发的一场持久战。 作者从一个行业玩笑切入:专家解决问题的同时,往往也创造出新问题。淘宝“类目+属性”体系中,品牌属性就是一个典型例子。一个运营小二能认的品牌有限,但全网品牌有几万个,这迫使品牌属性必须按类目拆分(男装品牌、女装品牌等),由不同小二维护。结果引发了三大混乱:不同类目下品牌属性的数据库ID不一致;“耐克”在男装和女装类目下成了两个互不相干的值;同一品牌(如阿迪达斯)的写法千奇百怪,从“阿迪达斯”到“adidas”不等。 为解决问题,团队曾发起“属性归一”的大规模人肉清理,通过模糊匹配合并历史数据。但这只能解决存量,增量问题接踵而至——即便规范了“英文/中文”格式,小二为图方便仍会随意新建属性值。最终,他们采用了一个“看起来很傻”的方案:在品牌管理这个节点上,从最初只允许一位小二输入,到后来交由一个专职团队负责。这从流程上卡住了混乱的源头,但卖家自主输入带来的品牌值仍需持续清理。 文章的核心观点在于:产品经理的根本责任是解决问题,而不一定是开发新功能。面对品牌管理这种复杂场景,有时一个看似笨拙但能直接切断问题根源的流程设计,比追求一个完美的产品方案更有效。它提醒我们,在技术或产品决策中,要勇于回归本质,用最直接的方式化解核心矛盾。

本机暂存
IT 后端/ 2013-07-30 13:36:38 / 累计浏览 5,422

7个示例科普CPU Cache

这篇文章从一个有趣的角度切入:用7个直观的C#代码实验,揭示了CPU缓存(Cache)如何深刻影响程序性能。作者并非空谈理论,而是带着读者一步步“看到”硬件的工作方式。 文章开篇就通过两个循环运行时间几乎相同的“反直觉”案例,点明了关键:程序的瓶颈往往在内存访问而非计算本身。随后,通过调整循环步长的实验,清晰地展示了“缓存行”(Cache Line)的概念——CPU以64字节块为单位读写内存,这直接解释了为何步长在16以内时性能恒定。 实验进一步深入。通过改变数组大小,文章用性能图表直观呈现了L1、L2缓存的容量阈值,程序运行时间在数据超出缓存大小时急剧变慢。接着,两个对比循环揭示了“指令级并发”的奥秘:操作间的依赖关系决定了CPU能否并行执行指令。 文章最后探讨了更为进阶的“缓存关联性”问题,解释了直接映射、N路组关联等设计如何在效率和冲突之间取得平衡,并说明了物理地址如何决定缓存槽的竞争关系。 总体来说,这篇译文将抽象的计算机体系结构知识,转化为了可运行、可观察的代码案例与性能图表。它没有停留在“缓存很快”的表面结论,而是带你探查缓存行、容量、关联性这些具体机制如何在代码层面产生实际影响,对于理解性能优化非常有启发。

本机暂存
IT DevOps/ 2013-07-29 23:17:18 / 累计浏览 6,457

Linux下如何知道文件被那个进程写

这篇讲的是一个运维中常见的棘手场景:Linux下文件持续增长,但用lsof等工具却找不到对应的写入进程。作者从一位同学的实际困扰出发,指出问题的普遍性,并给出了一个基于内核视角的解决方案。 核心思路是,既然用户态工具难以追踪(可能是进程很快打开又关闭了文件),那就直接在内核的虚拟文件系统层进行监控。文章推荐使用SystemTap工具包中的inodewatch.stp脚本,它通过探测vfs.write事件,能根据文件所在的设备号(major/minor)和inode号,精准定位到正在执行写操作的进程。 作者随后用一个生动的实验进行了演示:用dd命令在后台持续写入一个测试文件,通过stat命令获取其inode和设备信息,然后运行stap脚本。终端立刻输出了正在执行vfs_write的dd进程信息(进程名、PID),瞬间“破案”。整个排查过程清晰直观,展现了SystemTap在内核级故障排查中的强大能力。

本机暂存
IT 后端/ 2013-07-29 23:14:57 / 累计浏览 2,879

HAProxy几个重要的结构体

这篇讲的是HAProxy高性能代理背后的数据结构“骨架”。作者从上篇的连接建立流程出发,这次深入剖析了几个支撑其运行的核心结构体,尤其是session和task。 对于管理每一次连接的session,文章剥离了HTTP等上层细节,展示了它如何通过嵌入的双向链表节点将所有会话串联起来,形成一个全局列表。对于驱动事件循环的task,讲解则更为深入:它借助了HAProxy自研的ebtree来管理任务队列。通过判断task内部ebtree节点的leaf_p指针是否为空,就能高效地知道一个任务是在等待队列还是运行队列中。文章还贴出了相关的内联函数代码,展示了如何进行队列的添加与删除操作。 整篇文章不泛泛而谈,而是紧扣“如何用简洁数据结构实现高效管理”这条主线。通过精简的结构体定义和队列操作示意,清晰地揭示了HAProxy将连接状态与异步事件调度解耦的设计思想,对于想理解现代网络服务器内部实现的读者来说,是一次扎实的源码解读。

本机暂存
IT 前端/ 2013-07-29 23:13:05 / 累计浏览 4,862

Javascript触屏手势库-jTouch

这篇讲的是作者因项目代码混乱而受刺激,在业余时间封装的一个触屏手势库——jTouch。它专注于为触屏浏览器提供一套完整的手势识别方案。 文章开篇就坦诚地分享了作者的创作动机,那种对整洁代码的追求很能引起开发者共鸣。jTouch目前主要针对iPad测试,支持tap(单击)、doubletap(双击)、longtap(长按)、hold(拖拽)、swipe(滑动)、flip(轻拂)、pinch(捏合)和rotate(旋转)这八种核心手势。 作者没有停留在功能列表,而是详细说明了每个手势回调函数中能获取到的关键数据。比如,swipe和flip会返回方向(direction)和移动距离(x, y);pinch会返回缩放类型(in/out)和比例(scale);rotate则会返回方向和旋转角度(rotation)。这些细节对于开发者在回调中实现具体交互逻辑至关重要。 文章还提供了链式调用的简洁语法示例,并附带了几个生动的在线演示,包括一个触摸平滑移动的轮播效果,以及一个模拟iPad图标长按抖动、双击复位的界面。这些示例直观展示了库的实用性,也为想实现类似效果的开发者提供了直接的参考思路。

本机暂存
IT AI/ 2013-07-29 23:11:36 / 累计浏览 3,886

如何计算两个文档的相似度(三)

这篇讲的是《如何计算两个文档的相似度》系列文章的实战篇。作者从上一节的gensim基础用法出发,这一次要用“课程图谱”的真实课程数据,来实际验证和改进文档相似度计算的方法,并引入了NLTK这一专业的自然语言处理工具进行文本预处理。 核心思路是利用NLTK解决真实英文文本中的“脏”问题。作者展示了,如果只是简单地将单词小写化,标点符号和单词会粘在一起,影响计算质量。因此,引入了NLTK的`word_tokenize`函数进行精细分词,将“texts.”这样的组合拆分为“texts”和“.”。更关键的一步是使用NLTK内置的英文停用词表(共127个词,如“the”, “is”, “and”),过滤掉这些高频但对主题区分贡献低的词汇。 为了让验证可复现,文章提供了完整的Coursera课程数据集,包含379门课程。数据集结构清晰,每行是“课程名\t课程简介\t课程详情”,且已清除HTML标签。摘要中展示了加载数据和进行NLTK处理的初始步骤代码,体现了从数据准备到工具应用的完整实践流程。

本机暂存
IT 设计/ 2013-07-29 23:09:00 / 累计浏览 2,342

#我的错误案例#一个关于提醒的设计

这篇讲的是一个关于“凌晨房态”的产品设计踩坑故事。作者从快捷酒店行业的一个实际痛点出发:凌晨0-4点预订的用户大多想立刻入住,但酒店系统默认将订单归为当天,导致入住时间混乱。为了解决这个问题,产品在用户预订时增加了一个“请注意入住时间”的提示。 然而,这个看似合理的提醒,在作者亲自凌晨测试后被发现是个“错误案例”。这个提示打断了用户的操作流,让用户从“我只想马上睡觉”变成了“我还得确认你能不能让我马上睡觉”,反而催生了电话确认这一笨重环节,背离了产品应有的流畅体验。作者的朋友“一语中的”,点醒了这个设计的核心矛盾。 最终,他们调整了提醒策略:只在用户主动切换日期到当天时才提示,而对默认的“立刻入住”流程保持自信,不进行干扰。文章由此得出几个务实的启示:必须亲自上手体验才能发现真实痛点;过度提示是对用户和自身设计的不自信;以及跳出思维定式的重要性。这是一个从具体错误中提炼出普适性设计原则的典型复盘。

本机暂存
IT 后端/ 2013-07-29 22:58:09 / 累计浏览 3,185

FUSE源码剖析

这篇讲的是如何通过源码剖析来理解FUSE(用户空间文件系统)的内部工作原理。作者以FUSE-2.9.2版本的代码为基础,没有停留在概念介绍,而是直接切入核心,详细拆解了一个文件写操作在内核与用户空间之间完整往返的8个步骤。 文章清晰地梳理了从应用层发起write系统调用开始,请求如何经由VFS层传递给FUSE内核模块,被放入请求队列并等待,再到用户空间的守护进程轮询获取请求、执行实际操作,最后将结果同步返回内核的整个链条。这个视角生动展示了FUSE作为“桥梁”的实现机制。 为了支撑流程讲解,文章系统地介绍了内核侧与用户侧的关键数据结构,如管理通信连接的fuse\_conn、代表单次请求的fuse\_req等,勾勒出了FUSE框架的数据骨架。此外,文章还剖析了FUSE内核模块的加载注册过程,以及用户态程序通过mount命令将自定义文件系统挂载到内核的流程,从底层揭示了用户态文件系统得以运行的根基。 通过这样自顶向下与自底向上结合的剖析,文章将FUSE看似复杂的跨空间协作,还原为一组清晰的数据结构和函数调用,为理解这类“中间件”的设计思想提供了绝佳范例。

本机暂存
IT 数据库/ 2013-07-29 22:55:30 / 累计浏览 4,185

Impala与Hive的比较

这篇文章深入对比了Hadoop生态中两款重要的SQL查询工具:Impala与Hive。它们虽然共享HDFS/HBase存储和相同的元数据,但设计目标截然不同。 核心差异在于查询引擎的架构。Hive将查询转换为一连串的MapReduce任务,采用“推”式数据流和依赖外存的中间结果落盘,适合长时间、稳定的批处理作业。而Impala受Google Dremel启发,彻底绕开了MapReduce,其分布式查询引擎直接生成执行计划树,并以“拉”式流传输中间数据、最大化使用内存,大幅降低了延迟,专为交互式分析设计。 文章详细拆解了Impala的组件与查询流程,并指出其多项优化技术,比如使用LLVM进行运行时代码生成、利用SSE4.2指令集以及更优的I/O调度。不过,Impala在容错和处理超大数据集时存在限制。因此,一个高效的实践是:先用Hive进行耗时的数据清洗与转换,再让分析师在处理后的数据集上利用Impala进行快速、反复的探索与验证。

本机暂存
IT DevOps/ 2013-07-29 22:54:55 / 累计浏览 2,024

自动增量升级方案的设计及实现

这篇讲的是如何通过一套轻量级Shell脚本,实现业务应用(尤其是Web项目)基于SVN版本库的自动增量升级。 文章开篇直击痛点:运维与开发人员常面临增量升级时文件拷贝遗漏、rsync无法执行自定义脚本、手工编写升级脚本效率低下且易出错等问题。作者对比了几种常见方法后,提出了一种更优的方案——自动增量升级。 其核心思路分两步走。第一步是打包,开发人员执行`gen_manifest.sh`自动生成从版本库中导出的增量文件清单,经人工检查、修剪并可添加自定义脚本(如重启服务)后,由`gen_patch.sh`生成升级补丁包。第二步是升级,运维人员执行`patch.sh`应用补丁,该脚本会自动备份变更文件并执行清单中的定制操作,出现问题时可快速回滚。 方案的最大优势在于完全自动化和高度可定制。它无需额外工具,仅靠几个脚本就完成了从差异分析、打包到升级、回滚的闭环,并通过可插拔的方式支持升级时自动执行服务重启等运维操作。作者在文中提供了完整的脚本下载地址与清晰的三步操作流程(生成清单、生成补丁、执行升级),将一套设计思想落地为可直接使用的工具,切实解决了一线开发运维的繁琐负担。

本机暂存
IT DevOps/ 2013-07-28 15:51:11 / 累计浏览 6,018

Bash如何取得当前正在执行的脚本的绝对路径?

这篇讲的是Shell脚本中一个看似简单却容易掉坑里的问题:如何可靠地获取当前执行脚本的绝对路径。 作者从实际开发中经常需要使用脚本自身路径的场景出发,重点澄清了两种广为流传但并不正确的“常见误区”。一个是直接使用 `pwd` 命令,它只能获取当前的工作目录,与脚本所在位置无关;另一个是过度依赖特殊变量 `$0`,它的值会随 bash 的调用方式而变化,不一定就是脚本的绝对路径。 文章的核心价值在于对比和辨析。它详细解释了为什么这些方法会失效,并给出了一个经过验证的正确解决方案:`basepath=$(cd \`dirname $0\`; pwd)`。这个方案通过组合 `dirname` 和 `cd` 再加上 `pwd`,巧妙地规避了 `$0` 可能不完整的问题,确保无论从哪个目录调用脚本,都能稳定地返回脚本自身所在的绝对路径。对于编写需要灵活部署、不依赖用户预配置的工具脚本来说,这个技巧非常实用。

本机暂存
IT 开发者/ 2013-07-28 15:50:15 / 累计浏览 4,324

做正确的事情,等着被开除

这篇讲的是一种在技术公司里“反常识”的生存哲学。作者从谷歌工程师陳一鳴的一句话“做正确的事情,等着被开除”出发,剖析了现代科技公司普遍存在的“精神分裂”:一方面用严格的流程、考核与框架来规范团队,另一方面又殷切期待员工能打破这些框架,去冒险、快行动,做出真正的创新。 文章的核心观点是,卓越的成就往往诞生于规则之外。它鼓励技术人员建立强大的内心判断,在面临流程僵化、技术债务累积或需要快速验证时,敢于为了团队和产品的长期利益站出来,做出那些“正确但可能违反当前政策”的决策——比如为了快速上线而承担可控风险,或者为了长期可维护性而放慢速度进行重构。作者将这种行为定义为真正的职业精神:做你被雇来该做的事。 这对技术人员的启发在于,职业成长不仅是技术精进,更是责任承担与风险判断力的修炼。失败了不可怕,关键是要从结果中学习,并且绝不犯同样的错误。最好的工程师,往往是那些懂得在合适的时机,勇敢打破规则的人。

本机暂存
IT AI/ 2013-07-28 15:46:01 / 累计浏览 3,558

php扩展中如何定义线程安全的全局对象

作者在开发一个PHP图片裁剪扩展(tclip)时,为了提升效率,希望将OpenCV的人脸识别分类器(CascadeClassifier)作为全局对象,在扩展初始化时加载一次供后续重复使用。这引出了一个具体的技术问题:如何在PHP扩展中定义线程安全的全局对象。 文章直击踩坑点:最初尝试直接在模块全局变量中声明CascadeClassifier对象,却遭遇了“‘CascadeClassifier’ does not name a type”的编译错误,因为该类并未在对应的头文件中引入。作者调整方案,改用`void *`指针来声明全局变量,并在C源文件中定义一个静态的CascadeClassifier实例。在模块初始化函数(`PHP_MINIT_FUNCTION`)中完成配置文件的加载与实例化后,将实例的地址赋给那个`void *`类型的全局指针。 核心的解决思路在于,使用`void *`绕开了头文件依赖问题,并将对象实例的实际生命周期管理在静态作用域。而在扩展的实际函数中使用时,则需要通过`TCLIP_G()`宏访问该指针并强制类型转换回`CascadeClassifier *`。文章特别指出,要确保扩展的线程安全,就必须通过这类模块全局变量宏来访问数据。整个过程为在PHP C扩展中安全复用大型配置对象提供了一个可行的实践路径。

本机暂存