分类: Mysql/postgreSQL
2009-08-04 09:08:52
第一章 MySQL体系结构
MySQL的体系结构与其他数据库相比有很大不同,使得它有广泛的用途。尽管MySQL不是最好的,但它足以在需要的环境中工作的很好,如web程序。与此同时,MySQL能增强嵌入的应用程序,数据仓储,内容索引和软件推送,高可用冗余系统,在线交易处理(OLTP)等等。
你想从MySQL获得更多,需要懂得它德设计是如何工作的,而不是与它唱反调。MySQL在很多方面具有很好的伸缩性。例如,你能够配置它运行在大多数硬件环境,它支持各种数据类型。然而,MySQL最与众不同的特点是它的存储引擎体系结构,MySQL被设计成查询过程和其他任务与数据存储分开。在MySQL5.1版本,甚至能够在存储引擎作之间作为实时插件平衡负载。这种分开技术概念可以让你基于每个表选择,你的数据如何存储,你需要的性能,特点和其他特性。
本章提供了对MySQL服务器体系结构的概况,各存储引擎的主要不同点,为什么这些不同时重要的。通过简化细节,展示例子的方法解释MySQL。对于新接触数据库的和其他数据库专家这个讨论是有用的。
MySQL的逻辑体系结构
MySQL组件间是如何工作的这个蓝图在脑子里将有助于理解MySQL服务器。图1-1展示了MySQL的逻辑结构图。图中最上层包含的这些服务不等于MySQL。这些服务器大多是基于网络的client/server工具集,或服务器需要的:连接处理,认证,安全,等等。
图1-1
MySQL服务器体系结构逻辑图
第二层是我们感兴趣的。MySQL的核心就这这儿,包括查询,分析,优化,缓存,和内建的一些功能的代码(e.g.,日期,时间,公式和加密)。这些功能都是通过在这一层的存储引擎提供的:存储过程,触发器,和视图等。
第三层包括存储引擎。这层负责存储和获得所有存储在MySQL里的数据。和在GNU/Linux里提供的各种文件系统一样,每种存储引擎都有它自己的优势和缺点。服务器通过API与存储引擎通信。这个接口API隐藏了存储引擎间的不同,也使得存储引擎在查询层更透明。这些API包括了许多底层函数,这些函数执行如“一笔交易”或“查询有主键的记录”之类的操作。存储引擎不解释SQL,存储引擎间也不通信;存储引擎仅从服务器响应请求。
连接管理和安全
在MySQL服务器进程中,每个客户端连接都获得自己的线程。连接的查询时在单独的线程里执行,该线程轮流驻留在CPU中。服务器缓存这些线程,因此不必为每个新连接创建和销毁线程。当客户端(应用程序)连接到MySQL服务器,服务器需要认证该连接。认证是基于用户名,源主机和密码。X.509认证可以用在安全套接层(SSL)连接。只要客户端一连接上来,服务器就鉴别客户端是否有权限发送查询语句的权限(e.g.,客户端是否被允许发送SELECT语句给world数据库的Country表,)。我们会在12章讲解该题目。
优化和执行
MySQL通过创建一个内部结构(分析树)来分析查询,并且来实施各种优化。这些优化包括重写查询,检测MySQL读取表、使用索引的顺序等等。你可以通过在查询语句中增加关键字给优化器一些提示,从而影响优化器的决策过程。你也可以让服务器解释各方面的优化过程。这可以让你知道MySQL服务器做了什么,给你一些参考建议,从而重新建立查询、视图并使之运行更有效率。在第四章我们详细讨论优化器。优化器并不关心表使用的特定存储引擎,但存储引擎影响服务器如何优化查询。优化器会从存储引擎得知一些有关存储引擎性能,某些操作的消耗,表数据统计等。例如,一些存储引擎支持索引类型,这些索引类型对查询非常有帮助。在第三章会更多介绍索引和视图优化。在分析查询语句之前,服务器会询问查询缓存是否能存储SELECT语句和其查询结果。如果发送了一条查询语句,而该语句在缓存里仅有唯一一条时,MySQL服务器根本不分析、优化执行该语句--它仅传回结果!在204页“MySQL查询缓存”我们讨论查询查询缓存。
并发控制
任何时候,多条语句在同一时间需要改变数据,并发控制问题就会产生。MySQL必须在两个层面解决:服务器层面和存储引擎层面。并发控制是一个大话题,一般在理论文献中有较大篇幅阐述,但本书不会介绍原理,也不讨论关于MySQL内部细节。因此,我们将给您简单的阐述关于MySQL是如何处理并发读和写操作,因此你需要继续往下读文章。我们将使用在Unix系统上的email信箱。经典的mbox文件格式很简单。在mbox邮箱的所有消息都是一封接一封地连接在一起的。这个使得读和分析邮件消息非常简单。这也使得邮件投递很简单:仅在文件末尾添加新消息即可。但两个进程在同时投递消息到同一邮箱会发生什么?显然,这可能把邮箱弄垮,在邮箱文件的尾部留下了两个交替产生的消息。一个好的邮件投递系统会使用锁来保护邮箱不垮掉。当邮箱被锁,如果客户端尝试去投递,必须等待获得锁才能投递消息。在实际情况中,这种机制工作较合理,但对并发不支持。因为在任何给定时间仅一个进程能改变邮箱,高容量邮箱,这种情况会变成问题。
读/写锁
从邮箱读不是什么问题。多用户同时读相同的邮箱也不会有什么问题。因为不改变什么,没什么错误。但如果程序正在读邮箱,某人尝试删除第25号消息,会发生什么?读者可能看到错误或不一致的邮箱。因此安全起见,从邮箱读信需要特别关注。如果你把邮箱看做是数据库的表,每个邮件看做一行,在这种环境中问题就变得容易了。邮箱其实仅是一个简单的数据库表。修改数据库表的几行和删除或改变在邮箱里的消息内容很相似。并发控制这个经典问题的解决方案很简单。处理并行读/写访问的系统实现了一个锁系统,该系统由两种类型的锁。众所周知是共享锁和排斥锁,或叫做读锁和写锁。不必担心实际的锁技术,我们只描述一下概念。在一个资源上的读锁是共享的,或这说共享非阻塞:许多客户端可以在同一时间从资源上读,并且彼此不干扰。另一方面,写锁是互斥的。写锁阻塞读锁和其他写锁--因为安全的策略是仅有一个用户在给定的时间里有写资源并且当一个用户写时保护所有的读操作。在数据库领域,任何时候都有可能发生锁定:当用户正在改变某部分数据时,MySQL必须阻止其他用户读这些数据。MySQL以透明的方式执行内部锁管理。
锁粒度
改善共享资源并发性的一种方法是仔细选择你需要锁的资源。锁需要改变的内容,而不是把整个资源都锁住。更好的是精确地锁住你将要改变的那部分数据。在某时刻最小化锁定的数据量,可以让改变在给定的资源中同时发生,只要彼此不冲突即可。问题是锁定是要消耗资源的。每个锁操作-获得锁,检测锁是否释放,释放锁等等--都是开销。如果系统花费太多的时间管理锁,而不是存储和查询数据,性能就很窘迫了。在锁消耗过载和数据安全中,锁策略是一个折中的选择,在性能中找平衡点。大多数商业数据库服务器不给你这样的选择:在表中获得底层锁,好性能和锁之间复杂的调整方式。另一方面,MySQL提供一些选择。MySQL存储引擎能实现锁策略和锁粒度。在存储引擎设计中,锁管理是非常重要的决定;在某级别固定粒度对某些用户能有更好的性能,也使存储引擎很少地适应其他用户的目的。因为MySQL提供多种存储引擎,不必需要一个单独的,通用目的解决方案。让我们看一下两种重要的锁策略。
表锁
大多数基本锁定策略MySQL都能提供,最低消耗的锁是表锁。表锁与之前描述的邮箱锁是相似的:它锁定整个表。当一个客户端希望往一个表里写(插入,删除,更新等等)时,它需要一个写锁。这让所有其他读和写操作都等待。当没人写时,可以获得读锁,不会与其他读锁冲突。表锁在特殊情况下可以获得好的性能。例如,READ LOCAL表锁允许某些类型的并发写操作。写锁比读锁有较高优先级,写锁的请求将提前到锁队列的前面即使读已经在队列中(在队列中,写锁在读锁之前,但读锁不能在写锁之前)尽管存储引擎可以管理他们的锁,MySQL自己也使用表级别的锁来适应不同的目的。例如,不管存储引擎是什么,对于ALTER TABLE语句MySQL使用表级锁定。
行锁定
提供更好并发(实行更好的负载)的锁风格就是利用行锁。作为所熟知的行级别锁可以在InnoDB、Falcon存储引擎中提供。行锁是在存储引擎实现的,而不是在MySQL服务器实现(如果需要请参考MySQL逻辑结构图。)MySQL服务器完全不知道是在存储引擎实现的锁,这章的后面会看到,也贯穿本书,存储引擎是以它们自己的方式实现锁的。
事务
在事务概念混入前,不再看数据库系统更高级特性了。一个事务就是一组被看做是原子的SQL语句,单独的一组工作。如果数据库引擎能把这个完整的SQL语句组应用到数据库,那就这样做,但如果由于崩溃或其他原因不能完全应用,那什么也不做。要么全部完成,要么什么也不做。以下会详细说明。如果你熟悉ACID事务,可以跳过在第十页的“MySQL中的事务”银行应用程序是一个经典的例子,说明为什么事务是需要的。想象有两个表的银行数据库:支票和存钱。从Jane的支票账号取¥200到他的存钱账号,你需要执行至少三个步骤:
1、确定他的支票账号余额大于¥200
2、从他的支票账号余额减去¥200
3、将¥200存入他的存钱账号
这个完整的操作将被封装在一个事务语句中,因此如果其中一步失败,任何完成的语句都将被回滚。以START TRANSACTION语句开始一个事务,要么用COMMIT做永久改变,要么用ROLLBACK放弃改变。我们这个事务例子的SQL看上去像:
1
START TRANSACTION;
2
SELECT balance FROM checking WHERE customer_id = 10233276;
3
UPDATE checking SET balance = balance - 200.00 WHERE customer_id = 10233276;
4
UPDATE savings SET balance = balance + 200.00 WHERE customer_id = 10233276;
5
COMMIT;
但事务不是我们的整个故事。
如果当正在执行语句4,数据库崩溃会发生什么?谁知道?客户可能丢失¥200.
如果另一个进程在3和4行之间闯进来,移除了整个支票账号余额?银行就给
了用户¥200都浑然不知。除非系统通过ACID测试,事务语句才是足够的。ACID主张原子性,一致性,隔离性和持久性。很好的事务处理系统是必须符合这些标准的。
原子性
一个事务必须作为单独的不可分割的一组工作,因此整个事务要么全部应用要么回滚。事务具有原子性。部分地完成事务是不可能发生的:要么全部做,要么什么都不做。
一致性
数据库总是从一个一致状态到达下一个一致状态。在我们的例子中,一致性确保在3,4行崩溃不会导致¥200从支票账户消失。因为事务是不会提交的,任何事物的改变不会反应到数据库中。
隔离性
事务的结果通常对其他事物是不可见的,除非事务完成。在我们的例子中,这会确保如果在第3行后在第4行前,银行账号在运算,会在支票账号看到¥200。当我们讨论隔离级别时,你将会懂得为什么我们说的不可见。
持久性
只要提交了,事物的改变就是永久的。这意味着记录了改变的数据,因此系统崩溃数据也不会丢失。持久性是一个非常模糊的概念,因为确实有很多的级别。有些持久性策略比另外策略提供更强大,安全的保证,当然持久不是100%的。我们讨论的持久性的意思是MySQL数据库中的“InnoDB I/O调节”(第283页)
ACID事务保证银行不会把你的钱丢失。用程序逻辑保证持久性是极其困难和不可能的。一个ACID兼容数据库服务器必须做各类复杂的你想想不到的事情来提供ACID保证。
随着增加锁的粒度,额外的安全性趋势让数据库服务器必须做更多的工作。具有ACID事务的数据库服务器比没有ACID的服务器需要更多的CPU,内存,硬盘空间。这就是我们一再说的,MySQL的存储引擎结构工作带给你的优点。你可以决定你的应用程序是否需要事务。如果你真的不需要它,对于某类查询语句非事务存储引擎可以获得更高的性能。你也能使用LOCK TABLES给你需要的保护级别而不用事务。这取决于你。