内存管理可以分为三个层次,自底向上分别是: 操作系统内核的内存管理 glibc层使用系统调用维护的内存管理算法 应用程序从glibc动态分配内存后,根据应用程序本身的程序特性进行优化, 比如使用引用计数std::shared_ptr,apache的内存池方式等等。 应具有以下特性: 额外的空间损耗尽量少 分配速度尽可能快 尽量避免内存碎片 缓存本地化友好 通用性,兼容性,可移植性,易调试 目前大部分服务端程序使用glibc提供的malloc/free系列函数,而glibc使用的ptmalloc2在性能上远远弱后于google的tcmalloc和facebook的jemalloc。 而且后两者只需要使用LD_PRELOAD环境变量启动程序即可,甚至并不需要重新编译。 glibc ptmalloc2 ptmalloc2即是我们当前使用的glibc malloc版本。 ptmalloc原理 x86_64 下 Linux 进程的默认地址空间, 对 heap 的操作, 操作系统提供了brk()系统调用,设置了Heap的上边界; 对 mmap 映射区域的操作,操作系 统 供了 mmap()和 munmap()函数。 因为系统调用的代价很高,不可能每次申请内存都从内核分配空间,尤其是对于小内存分配。 而且因为mmap的区域容易被munmap释放,所以一般大内存采用mmap(),小内存使用brk()。 多线程支持 Ptmalloc2有一个主分配区(main arena), 有多个非主分配区。 非主分配区只能使用mmap向操作系统批发申请HEAP_MAX_SIZE(64位系统为64MB)大小的虚拟内存。 当某个线程调用malloc的时候,会先查看线程私有变量中是否已经存在一个分配区,如果存在则尝试加锁,如果加锁失败则遍历arena链表试图获取一个没加锁的arena, 如果依然获取不到则创建一个新的非主分配区。 free()的时候也要获取锁。分配小块内存容易产生碎片,ptmalloc在整理合并的时候也要对arena做加锁操作。在线程多的时候,锁的开销就会增大。 ptmalloc内存管理 用户请求分配的内存在ptmalloc中使用chunk表示, 每个chunk至少需要8个字节额外的开销。 用户free掉的内存不会马上归还操作系统,ptmalloc会统一管理heap和mmap区域的空闲chunk,避免了频繁的系统调用。 ptmalloc 将相似大小的 chunk 用双向链表链接起来, 这样的一个链表被称为一个 bin。Ptmalloc 一共 维护了 128 个 bin,并使用一个数组来存储这些 bin 数组中的第一个为 unsorted bin, 数组中从 2 开始编号的前 64 个 bin 称为 small bins, 同一个small bin中的chunk具有相同的大小。small bins后面的bin被称作large bins。
列式存储(Column-based)是相对于传统关系型数据库的行式存储(Row-based)来说的。简单来说两者的区别就是如何组织表。 将表放入存储系统中有两种方法,而我们绝大部分是采用行存储的。行存储法是将各行放入连续的物理位置,这很像传统的记录和文件系统。列存储法是将数据按照列存储到数据库中,与行存储类似, 应用行式存储的数据库系统称为行式数据库,同理应用列式存储的数据库系统称为列式数据库。随着列式数据库的发展,传统的行式数据库加入了列式存储的支持,形成具有两种存储方式的数据库系统。 传统的关系型数据库,如Oracle、DB2、MySQL、SQL SERVER等采用行式存储法,当然传统的关系型数据库也在不断发展中。随着Oracle 12c推出了in memory组件,使得Oracle数据库具有了双模式数据存放方式,从而能够实现对混合类型应用的支持:传统的以行形式保存的数据满足OLTP应用;列形式保存的数据满足以查询为主的OLAP应用。 新兴的Hbase、HP Vertica、EMC Greenplum等分布式数据库采用列式存储,当然这些数据库也有对行式存储的支持比如HP Vertica。 随着传统关系型数据库与新兴的分布式数据库不断的发展,列式存储与行式存储会不断融合,数据库系统会呈现双模式数据存放方式,这也是商业竞争的需要。 列式存储的主要优点之一就是可以大幅降低系统的I/O,尤其是在海量数据查询时,I/O向来是系统的主要瓶颈之一。行式更适合OLTP,比如传统的基于增删改查操作的应用。列式更适合OLAP,非常适合于在数据仓库领域发挥作用,比如数据分析、海量存储和商业智能;涉及不经常更新的数据。 由于设计上的不同,列式数据库在并行查询处理和压缩上更有优势。而且数据是以列为单元存储,完全不用考虑数据建模或者说建模更简单了。要查询计算哪些列上的数据,直接读取列就行。
MySQL Proxy处于客户端应用程序和MySQL服务器之间,通过截断、改变并转发客户端和后端数据库之间的通信来实现其功能,这和WinGate之类的网络代理服务器的基本思想是一样的。代理服务器是和TCP/IP协议打交道,而要理解MySQL Proxy的工作机制,同样要清楚MySQL客户端和服务器之间的通信协议,MySQL Protocol包括认证和查询两个基本过程: 认证过程包括: 客户端向服务器发起连接请求 服务器向客户端发送握手信息 客户端向服务器发送认证请求 服务器向客户端发送认证结果 如果认证通过,则进入查询过程: 客户端向服务器发起查询请求 服务器向客户端返回查询结果 当然,这只是一个粗略的描述,每个过程中发送的包都是有固定格式的,想详细了解MySQL Protocol的同学,可以去这里看看。MySQL Proxy要做的,就是介入协议的各个过程。首先MySQL Proxy以服务器的身份接受客户端请求,根据配置对这些请求进行分析处理,然后以客户端的身份转发给相应的后端数据库服务器,再接受服务器的信息,返回给客户端。所以MySQL Proxy需要同时实现客户端和服务器的协议。 由于要对客户端发送过来的SQL语句进行分析,还需要包含一个SQL解析器。可以说MySQL Proxy相当于一个轻量级的MySQL了,实际上,MySQL Proxy的admin server是可以接受SQL来查询状态信息的。 MySQL Proxy通过lua脚本来控制连接转发的机制。主要的函数都是配合MySQL Protocol各个过程的,这一点从函数名上就能看出来: connect_server() read_handshake() read_auth() read_auth_result() read_query() read_query_result() 至于为什么采用lua脚本语言,我想这是因为MySQL Proxy中采用了wormhole存储引擎的关系吧,这个虫洞存储引擎很有意思,数据的存储格式就是一段lua脚本 通过这几个入口函数我们可以控制mysql-proxy的一些行为。
MySQL InnoDB存储引擎,实现的是基于多版本的并发控制协议——MVCC (Multi-Version Concurrency Control) (注:与MVCC相对的,是基于锁的并发控制,Lock-Based Concurrency Control)。MVCC最大的好处,相信也是耳熟能详:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能,这也是为什么现阶段,几乎所有的RDBMS,都支持了MVCC。
LSM被设计来提供比传统的B+树或者ISAM更好的写操作吞吐量,通过消去随机的本地更新操作来达到这个目标。 那么为什么这是一个好的方法呢?这个问题的本质还是磁盘随机操作慢,顺序读写快的老问题。这二种操作存在巨大的差距,无论是磁盘还是SSD。 顺序读写磁盘(不管是SATA还是SSD)快于随机读写主存,而且快至少三个数量级。这说明我们要避免随机读写,最好设计成顺序读写。