数据结构和算法在存储中的经典应用

数据结构和算法在存储中的经典应用

Algorithms + Data Structures = Programs —— Niklaus Wirth 先搬出写了 Pascal、又拿了图灵奖的大师的名言,这里不讨论该断言的合理性,只作为本文的引子。在数据库领域,究其原理,绕不开数据存储。本文从几个经典应用出发,管窥数据结构在存储中所起到的作用。 引入 通常,像电商交易、支付系统的后端服务,往往是 I/O 密集型,I/O 性能会制约整个服务的性能。因此后端工程师往往会非常关注数据库的读写 TPS,IOPS 等监控指标。在特定的技术环节,比如为数据库表设计索引,用 Kafka 为秒杀场景提供大并发写的吞吐,这些大家非常熟悉的技术,其实都包含了对数据结构的运用。 磁盘 I/O 对数据库的读写本质是磁盘 I/O(不考虑 Redis 等内存数据库)。在讲存储中的数据结构之前,先简单介绍磁盘 I/O 特性对数据库设计的影响,详细的解释可以阅读美团技术博客 《磁盘 I/O 那些事》[1]。 传统意义上数据库需要将数据以文件的形式存储到磁盘上,对数据库的操作可以简单抽象为对磁盘文件的读写,而磁盘 I/O 耗时会影响数据库的读写性能。一次磁盘 I/O 可以拆分为:磁头寻道时间 seek time、扇区旋转延迟时间 latency time、数据传输时间 transmission time,参考下图示意。 磁头寻道:磁头移动到数据所在磁道位置,参考值 3-15ms; 扇区旋转:通过盘片的旋转,将数据所在扇区移动到磁头下方,参考值 2-5ms; 数据传输:数据通过系统总线从磁盘加载到内存的时间,通常小于 1ms; 寻道时间 > 旋转延迟 >> 数据传输,所以磁盘 I/O 耗时 ≈ seek time + latency time,IOPS = 1000 / (seek time + latency time)。 我们已经知道,磁盘的顺序 I/O 比随机 I/O 更快(参考下图),原因在于顺序 I/O 磁头几乎不用换道,或者换道的时间很短;而随机 I/O 磁头则会频繁换道。对随机 I/O 而言,7200 转的磁盘,随机 I/O 的 IOPS 通常为 70 ~ 80。而对顺序 I/O,比如读取一块连续存储的文件,理想的情况是在一次 I/O 后就可以顺序读写,其 IOPS 会非常高。因此包括数据库在内,但凡涉及存储,都会考虑更多利用顺序 I/O 来提高性能。 相对于读,写的性能瓶颈会更凸显,因此对存储的设计会优先考虑提升写性能。由于文件系统保证了文件是顺序写入,所以可以采用追加写入文件的方式实现顺序写。由于是对文件的增量追加写入,所以在数据读取时,需要倒过来检索,从最新的文件逐个往回查,比如通过简单的二分,二叉搜索树等算法进一步优化,减少 I/O 次数,归根结底性能优化

Docker run 命令参数及使用

Docker run 命令参数及使用

Docker run 命令参数及使用 Docker run :创建一个新的容器并运行一个命令 语法 docker run [OPTIONS] IMAGE [COMMAND] [ARG...] OPTIONS说明: 01.[root@www ~]# docker run --help 02. 03.Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...] 04. 05.Run a command in a new container 06. 07. -a, --attach=[] Attach to STDIN, STDOUT or STDERR 08. --add-host=[] Add a custom host-to-IP mapping (host:ip) 09. --blkio-weight=0 Block IO (relative weight), between 10 and 1000 10. --cpu-shares=0 CPU shares (relative weight) 11. --cap-add=[] Add Linux capabilities 12. --cap-drop=[] Drop Linux capabilities 13. --cgroup-parent= Optional parent cgroup for the container 14. --cidfile= Write the container ID to the file 15. --cpu-period=0 Limit CPU CFS (Completely Fair Scheduler) period 16. --cpu-quota=0 Limit CPU CFS (Completely Fair Scheduler) quota 17. --cpuset-cpus= CPUs in which to allow execution (0-3, 0,1) 18. --cpuset-mems= MEMs in which to allow execution (0-3, 0,1) 19. -d, --detach=false Run container in background and print container ID(后台运行) 20. --device=[] Add a host device to the container 21. --disable-content-trust=true Skip image verification 22. --dns=[] Set custom DNS servers 23. --dns-opt=[] Set DNS options 24. --dns-search=[] Set custom DNS search domains 25. -e, --env=[] Set environment variables(设置环境变量) 26. --entrypoint= Overwrite the default ENTRYPOINT of the image 27. --env-file=[] Read in a file of environment variables 28. --expose=[] Expose a port or a range of ports 29. --group-add=[] Add add

腾讯面试:MySQL事务与MVCC如何实现的隔离级别?

腾讯面试:MySQL事务与MVCC如何实现的隔离级别?

前言 其实数据库章节基本上的知识点我都写过一遍了,包括这篇事务和MVCC的,但是国庆期间我翻阅资料的时候我发现之前写的还差点意思,例子举得也差点意思,那我就根据我自己最新的理解,加上之前的总结相当于重写了,希望你也有新的收获。 数据库事务介绍 事务的四大特性(ACID) 原子性(atomicity): 事务的最小工作单元,要么全成功,要么全失败。 一致性(consistency): 事务开始和结束后,数据库的完整性不会被破坏。 隔离性(isolation): 不同事务之间互不影响,四种隔离级别为RU(读未提交)、RC(读已提交)、RR(可重复读)、SERIALIZABLE (串行化)。 持久性(durability): 事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失。 事务的隔离级别 读未提交(Read UnCommitted/RU) 又称为脏读,一个事务可以读取到另一个事务未提交的数据。这种隔离级别岁最不安全的一种,因为未提交的事务是存在回滚的情况。 读已提交(Read Committed/RC) 又称为不可重复读,一个事务因为读取到另一个事务已提交的修改数据,导致在当前事务的不同时间读取同一条数据获取的结果不一致。 举个例子,在下面的例子中就会发现SessionA在一个事务期间两次查询的数据不一样。原因就是在于当前隔离级别为 RC,SessionA的事务可以读取到SessionB提交的最新数据。 发生时间 SessionA SessionB 1 begin; 2 select * from user where id=1;(张三) 3 update user set name=‘李四’ where id=1;(默认隐式提交事务) 4 select * from user where id=1;(李四) 5 update user set name=‘王二’ where id=1;(默认隐式提交事务) 6 select * from user where id=1;(王二) 可重复读(Repeatable Read/RR) 又称为幻读,一个事物读可以读取到其他事务提交的数据,但是在RR隔离级别下,当前读取此条数据只可读取一次,在当前事务中,不论读取多少次,数据任然是第一次读取的值,不会因为在第一次读取之后,其他事务再修改提交此数据而产生改变。因此也成为幻读,因为读出来的数据并不一定就是最新的数据。 举个例子:在SessionA中第一次读取数据时,后续其他事务修改提交数据,不会再影响到SessionA读取的数据值。此为可重复读。 发生时间 SessionA SessionB 1 begin; 2 select * from user where id=1;(张三) 3 update user set name=‘李四’ where id=1; (默认隐式提交事务) 4 select * from user where id=1;(张三) 5 update user set name=‘王二’ where id=1;(

☕【JVM原理探索】分析堆外内存(Direct Memory)使用和分析

☕【JVM原理探索】分析堆外内存(Direct Memory)使用和分析

这是我参与更文挑战的第19天,活动详情查看: 更文挑战 堆外内存 堆外内存,其实就是不受JVM控制的内存。简单来说,除了堆栈内存,剩下的就都是堆外内存了(当然,这是从Java运行时内存的角度来看),堆外内存直接受操作系统管理,而不是虚拟机。而使用堆外内存的原因, 相比于堆内内存有几个优势: 减少了垃圾回收的工作,因为垃圾回收会暂停其他的工作(可能使用多线程或者时间片的方式,根本感觉不到) 堆外内存是直接受操作系统管理的,而不是JVM,因此使用堆外内存的话,就可以保持一个比较小的堆内内存,减少垃圾回收对程序性能的影响。 就是提高IO操作的效率!这里就涉及用户态与内核态,以及内核缓冲区的概念,如果从堆内向磁盘写数据,数据会被先复制到堆外内存,即内核缓冲区,然后再由OS写入磁盘,但使用堆外内存的话则可以避免这个复制操作。 堆内内存其实就是用户进程的【进程缓冲区】,属于用户态;堆外内存由操作系统管理【内核缓冲区】,属于内核态。 自然也有不好的一面: 堆外内存难以控制,如果内存泄漏,那么很难排查 堆外内存相对来说,不适合存储很复杂的对象。一般简单的对象或者扁平化的比较适合。 因为是操作系统的内存机制,所以需要通过本地方法进行分配,较为复杂和缓慢 直接内存使用 堆外内存通过java.nio的ByteBuffer来创建,调用allocateDirect方法申请即可。 可以通过设置-XX:MaxDirectMemorySize=10M控制堆外内存的大小。 堆外内存的垃圾回收 由于堆外内存并不直接控制于JVM,因此只能等到full GC的时候才能垃圾回收!Full GC,一般发生在年老代垃圾回收以及调用System.gc的时候,这样肯定不能满足我们的需求! 手动的控制回收堆外内存了!其中sun.nio其实是java.nio的内部实现。 package xing.test; import java.nio.ByteBuffer; import sun.nio.ch.DirectBuffer; public class NonHeapTest { public static void clean(final ByteBuffer byteBuffer) { if (byteBuffer.isDirect()) { ((DirectBuffer)byteBuffer).cleaner().clean(); } } public static void sleep(long i) { try { Thread.sleep(i); }catch(Exception e) { /*skip*/ } } public static void main(String []args) throws Exception { ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024 * 200); System.out.println("start"); sleep(5000); c

原来10张图就可以搞懂分布式链路追踪系统原理

原来10张图就可以搞懂分布式链路追踪系统原理

分布式系统为什么需要链路追踪? 随着互联网业务快速扩展,软件架构也日益变得复杂,为了适应海量用户高并发请求,系统中越来越多的组件开始走向分布式化,如单体架构拆分为微服务、服务内缓存变为分布式缓存、服务组件通信变为分布式消息,这些组件共同构成了繁杂的分布式网络。 微服务架构(极简版) 假如现在有一个系统部署了成千上万个服务,用户通过浏览器在主界面上下单一箱茅台酒,结果系统给用户提示:系统内部错误,相信用户是很崩溃的。 运营人员将问题抛给开发人员定位,开发人员只知道有异常,但是这个异常具体是由哪个微服务引起的就需要逐个服务排查了。 界面出现异常难以排查后台服务 开发人员借助日志逐个排查的效率是非常低的,那有没有更好的解决方案了? 答案是引入链路追踪系统。 什么是链路追踪? 分布式链路追踪就是将一次分布式请求还原成调用链路,将一次分布式请求的调用情况集中展示,比如各个服务节点上的耗时、请求具体到达哪台机器上、每个服务节点的请求状态等等。 链路跟踪主要功能: 故障快速定位:可以通过调用链结合业务日志快速定位错误信息。 链路性能可视化:各个阶段链路耗时、服务依赖关系可以通过可视化界面展现出来。 链路分析:通过分析链路耗时、服务依赖关系可以得到用户的行为路径,汇总分析应用在很多业务场景。 链路追踪基本原理 链路追踪系统(可能)最早是由Goggle公开发布的一篇论文 《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》 被大家广泛熟悉,所以各位技术大牛们如果有黑武器不要藏起来赶紧去发表论文吧。 在这篇著名的论文中主要讲述了Dapper链路追踪系统的基本原理和关键技术点。接下来挑几个重点的技术点详细给大家介绍一下。 Trace Trace的含义比较直观,就是链路,指一个请求经过所有服务的路径,可以用下面树状的图形表示。 traceId串联请求形成链路 图中一条完整的链路是:chrome -> 服务A -> 服务B -> 服务C -> 服务D -> 服务E -> 服务C -> 服务A -> chrome。服务间经过的局部链路构成了一条完整的链路,其中每一条局部链路都用一个全局唯一的traceid来标识。 Span 在上图中可以看出来请求经过了服务A,同时服务A又调用了服务B和服务C,但是先调的服务B还是服务C呢?从图中很难看出来,只有通过查看源码才知道顺序。 为了表达这种父子关系引入了Span的概念。 同一层级parent id相同,span id不同,span id从小到大表示请求的顺序,从下图中可以很明显看出

联系我们

联系电话

4000-640-466

联系邮箱

service@f-li.cn

办公地址

上海黄浦区外滩源1号

谢谢,您的信息已成功发送。
请填写信息。