|
缓存永远不是性能优化在完美的编程世界中,缓存这个词常常在系统中扮演着差和低效的角色。使用它可能会给系统带来一些从未见过的麻烦,但世界就是这么搞笑,在某些时候,缓存却是必须存在的。真的说爱你很难,恨你也很难! 缓存,一个让程序员爱恨交织的东西。它可以加快你的访问速度,但也可能会导致你的敏感信息丢失。 在完美的编程世界中,缓存这个词常常在系统中扮演着差和低效的角色。使用它可能会给系统带来一些从未见过的麻烦,但世界就是这么搞笑,在某些时候,缓存却是必须存在的。真的说爱你很难,恨你也很难! 还记得用汇编和C语言来编写代码的那段日子,我不得不做一套自己的内存管理系统,这样我编写的代码可以在4K的RAM上运行,虽然这样,仍然是令人称赞的。如今,有许多人喜欢拼凑代码片段然后再进行调用,我曾也是这种黑客里面的一员,但后来我决定要“改过自新”,好好学习编程知识。如今我已经学习许多概念、设计模型,当我回去看以前那种面条式代码时,真心让我反胃。 在你被这种思想祸害之前,我有非常合理的理由告诉你,缓存与性能优化是不同的。事实上,它是完全相反的——缓存是避免重复工作(这是不一样的自动化)并且也是隐藏错误和阻止服务器重载的一种非常得体的做法。 下面让我们来回答一些显而易见的问题:为什么不同?这很简单,“一个欠佳的应用程序缓存仍然是欠佳的,但因为有缓存执行时间将大大减少而效率却保持不变”。 性能优化是一门艺术。它通过分析项目中遇到的瓶颈和删除它们来实现,对那些差的代码进行重构。总而言之,性能优化会让你的代码更好! 虽然缓存不是性能优化,但并不是说缓存就很差劲,我们要一分为二的去看。在对其进行优化之前我们需要理解缓存是什么?用在哪?尤其是应用在黑客和一些购物性的网站。 下面让我们看看Wikipedia是如何描述缓存的: 在计算机系统中,高速缓存是用于减少处理器访问内存所需平均时间的部件。在金字塔式存储体系中它位于自顶向下的第二层,仅次于CPU寄存器。其容量远小于内存,但速度却可以接近处理器的频率。 当处理器发出内存访问请求时,会先查看缓存内是否有请求数据。如果存在(命中),则不经访问内存直接返回该数据;如果不存在(失效),则要先把内存中的相应数据载入缓存,再将其返回处理器。 缓存之所以有效,主要是因为程序运行时对内存的访问呈现局部性(Locality)特征。这种局部性既包括空间局部性(Spatial Locality),也包括时间局部性(Temporal Locality)。有效利用这种局部性,缓存可以达到极高的命中率。 在处理器看来,缓存是一个透明部件。因此,程序员通常无法直接干预对缓存的操作。但是,确实可以根据缓存的特点对程序代码实施特定优化,从而更好地利用缓存。 所以,未来的请求可以更快?那为什么不让它们快点开始呢?这是个与应用程序和语言无关的概念,这里没有任何单一的应用程序或语言可以给高速缓存带来提升。 但这似乎并没有什么意义!下面让我们看几个例子。 $sql = "_select* FROM `database`.`table_name`"; 你会认为在MySQL中的查询语句、Memcache或者其他一些高速缓存存储会让它更快吗?当然不是,无论怎样,它仅仅是一个在系统中的变量存储,在优化时,缓存这些东西根本不会有任何影响。 下面让我们以Magento为例。使用Magento EE 1.12配置且使用nginx和Memcache来禁止全页缓存。 下面SQL语句在生成一个分类页面时使用: SELECT `e` ._*, `cat_index`.`position` AS `cat_index_position` , `price_index`.`price` , `price_index`.`tax_class_id` , `price_index`.`final_price` , IF( price_index.tier_price IS NOT NULL , LEAST( price_index.min_price, price_index.tier_price ) , price_index.min_price ) AS `minimal_price` , `price_index`.`min_price` , `price_index`.`max_price` , `price_index`.`tier_price` FROM `catalog_product_entity` AS `e` INNER JOIN `catalog_category_product_index` AS `cat_index` ON cat_index.product_id = e.entity_id AND cat_index.store_id =1 AND cat_index.visibility IN ( 2, 4 ) AND cat_index.category_id = '10' AND cat_index.is_parent =1 INNER JOIN `catalog_product_index_price` AS `price_index` ON price_index.entity_id = e.entity_id AND price_index.website_id = '1' AND price_index.customer_group_id =0 ORDER BY `cat_index`.`position` ASC LIMIT 15 显示行0 - 6(共7个,查询花费0.0017秒) _select‘e’.sku, 'cat_index'.'position' AS 'cat_index_position', 'price_index'.'price', 'price_index'.'tax_class_id', 'price_index'.'final_price', IF(price_index.tier_price IS NOT NULL, LEAST(price_index.min_price, price_index.tier_price), price_index.min_price) AS 'minimal_price', 'rice_index'.'min_price', 'price_index'.'max_price', `price_index`.`tier_price` FROM `catalog_product_entity` AS `e` INNER JOIN `catalog_category_product_index` AS `cat_index` ON cat_index.product_id=e.entity_id AND cat_index.store_id=1 AND cat_index.visibility IN(2, 4) AND cat_index.category_id='10' AND cat_index.is_parent=1 INNER JOIN `catalog_product_index_price` AS `price_index` ON price_index.entity_id = e.entity_id AND price_index.website_id = '1' AND price_index.customer_group_id = 0 ORDER BY `cat_index`.`position` ASC LIMIT 15 显示行0 - 6(共7个,查询花费 0.0015秒) 你有注意到其中的区别吗?该脚本大致使用相同的时间来生成但通过使用索引或定制MySQL可以有很大的提升。 你看到了吗?性能提升正如你看到的这样简单。下面让我们来另外一个例子: 如果你打开Mage_Core_Model_Resource_Db_Abstract 让我们来看看加载方法和实现一些缓存来提升性能: /** _*Fetches an object from the cache storage _*@param Mage_Core_Model_Abstract $object _*@return boolean | object */ protected function isThisObjectCached(Mage_Core_Model_Abstract $object) { $key = md5(json_encode($object)); $helper = Mage::helper('performance'); if ($cachedObject = $helper->fetchFromCache($key)) { return $cachedObject; } return false; } /** _*Saves an object in cache _*@param Mage_Core_Model_Abstract $object _*@return \Mage_Core_Model_Resource_Db_Abstract */ protected function saveThisObjectInCache(Mage_Core_Model_Abstract $object) { $key = md5(json_encode($object)); $helper = Mage::helper('performance'); $helper->saveObjectInCache($key, $object); return $this; } /** _*Load an object * _*@param Mage_Core_Model_Abstract $object _*@param mixed $value _*@param string $field field to load by (defaults to model id) _*@return Mage_Core_Model_Resource_Db_Abstract */ public function load(Mage_Core_Model_Abstract $object, $value, $field = null) { if ($cachedObject = $this->isThisObjectCached($object)) { $data = $cachedObject->getData(); if ($data) { $object->setData($data); } } else { if (is_null($field)) { $field = $this->getIdFieldName(); } $read = $this->_getReadAdapter(); if ($read && !is_null($value)) { $_select= $this->_getLoadSelect($field, $value, $object); $data = $read->fetchRow($select); if ($data) { $object->setData($data); } } } $this->unserializeFields($object); $this->_afterLoad($object); $this->saveThisObjectInCache($object); return $this; } 点击这里查看输出结果 正如你看到的,并没有太多的提高。甚至可以说,有了缓存,性能反而下降啦! 这到底是什么意思呢? 这意味着通过添加一个额外的层(缓存),你实际上是在增加总的处理时间,因为现在有额外的检查对象和缓存数据需要读取或保存。 在这种情况下:Memcache vs. MySQL,对我来说,性能至少不受捕获数据影响。 你为什么还会对缓存喋喋不休呢? 这到底是为什么呢?我是一个强烈倡导反向代理和缓存的程序员,不是因为我懒而是因为我是一个性能优化狂热者。每当我想到优化时,我会去增强和重构代码,而不是去隐藏错误或使用那些考虑欠佳的代码。 建筑与工程有许多共同点。在施工之前,你需要选择合适的材料、现货和会建的人去构建它。因此,如果你构建一个sh&^%$建筑,你希望通过粉刷来隐藏掉那些伪劣的工艺和材质吗?是的,你可能会当一切都没有发生过,等到你被抓住以后,游戏就玩完啦! 我们能做些什么? 下面我会提供一些非常好的文章给你参考,至于如何做,这完全取决于你。 Google Speed up your site Yahoo best practices Even faster websites Offloading with another language Use the right tools 你能做的最好事情是在你代码里面应用“童子军规则”: 如果不能把代码写的很好,那代码必须要一直保持清洁。我们已经见过许多代码随着时间的推移在腐烂和流失,所以我们必须积极预防这种退化。 美国童子军有一个简单的规则可以应用到我们的专业里:每次优化都应该让代码更整洁 责编:孔维维 微信扫一扫实时了解行业动态 微信扫一扫分享本文给好友 著作权声明:畅享网文章著作权分属畅享网、网友和合作伙伴,部分非原创文章作者信息可能有所缺失,如需补充或修改请与我们联系,工作人员会在1个工作日内配合处理。 |
最新专题 |
|