本页面介绍了 memcache 用法的 Python 代码示例。Memcache 是一个高性能的分布式内存对象缓存系统,可让您快速访问缓存数据。如需详细了解 Memcache,请参阅 Memcache 概览。
Memcache 模式
Memcache 通常与以下模式配合使用:
- 应用接收来自用户或应用的查询。
- 应用检查满足该查询所需的数据是否在 memcache 中。
- 如果数据在 memcache 中,则应用会使用该数据。
- 如果数据不在 memcache 中,则应用会查询数据存储区并将结果存储在 memcache 中以用于将来的请求。
以下伪代码表示一个典型的 memcache 请求:
def get_data():
data = memcache.get('key')
if data is not None:
return data
else:
data = query_for_data()
memcache.add('key', data, 60)
return data
Ndb 在内部使用 memcache 加速查询。不过,如果您希望,则还可以明确添加 memcache 调用来进一步控制加速查询。
缓存数据
以下示例演示了使用 Python API 在 Memcache 中设置值的几种方式。
# Add a value if it doesn't exist in the cache
# with a cache expiration of 1 hour.
memcache.add(key="weather_USA_98105", value="raining", time=3600)
# Set several values, overwriting any existing values for these keys.
memcache.set_multi(
{"USA_98115": "cloudy", "USA_94105": "foggy", "USA_94043": "sunny"},
key_prefix="weather_",
time=3600
)
# Atomically increment an integer value.
memcache.set(key="counter", value=0)
memcache.incr("counter")
memcache.incr("counter")
memcache.incr("counter")
如需详细了解 add()
、set_multi()
和 set()
方法,请参阅 Memcache Python API 文档。
修改 guestbook.py 以使用 Memcache
入门指南中的留言板应用会根据每个请求来查询数据存储区(通过 ndb,因此它已在某种程度上受益于 memcache 加速)。您可以在查询数据存储区之前,修改留言板应用以明确使用 memcache。
首先,我们将导入 Memcache 模块,并创建方法以在运行查询前检查 Memcache。
def get_greetings(self, guestbook_name):
"""
get_greetings()
Checks the cache to see if there are cached greetings.
If not, call render_greetings and set the cache
Args:
guestbook_name: Guestbook entity group key (string).
Returns:
A string of HTML containing greetings.
"""
greetings = memcache.get('{}:greetings'.format(guestbook_name))
if greetings is None:
greetings = self.render_greetings(guestbook_name)
try:
added = memcache.add(
'{}:greetings'.format(guestbook_name), greetings, 10)
if not added:
logging.error('Memcache set failed.')
except ValueError:
logging.error('Memcache set failed - data larger than 1MB')
return greetings
接下来,我们将分开页面 HTML 的查询和创建过程。如果缓存未命中,我们将调用此方法来查询数据存储区并构建将在 Memcache 中存储的 HTML 字符串。
def render_greetings(self, guestbook_name):
"""
render_greetings()
Queries the database for greetings, iterate through the
results and create the HTML.
Args:
guestbook_name: Guestbook entity group key (string).
Returns:
A string of HTML containing greetings
"""
greetings = ndb.gql('SELECT * '
'FROM Greeting '
'WHERE ANCESTOR IS :1 '
'ORDER BY date DESC LIMIT 10',
guestbook_key(guestbook_name))
output = cStringIO.StringIO()
for greeting in greetings:
if greeting.author:
output.write('<b>{}</b> wrote:'.format(greeting.author))
else:
output.write('An anonymous person wrote:')
output.write('<blockquote>{}</blockquote>'.format(
cgi.escape(greeting.content)))
return output.getvalue()
最后,我们将更新 MainPage
处理程序,以调用 get_greetings() 方法并显示有关缓存命中或未命中的次数的一些统计信息。
import cgi
import cStringIO
import logging
import urllib
from google.appengine.api import memcache
from google.appengine.api import users
from google.appengine.ext import ndb
import webapp2
class Greeting(ndb.Model):
"""Models an individual Guestbook entry with author, content, and date."""
author = ndb.StringProperty()
content = ndb.StringProperty()
date = ndb.DateTimeProperty(auto_now_add=True)
def guestbook_key(guestbook_name=None):
"""Constructs a Datastore key for a Guestbook entity with guestbook_name"""
return ndb.Key('Guestbook', guestbook_name or 'default_guestbook')
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.out.write('<html><body>')
guestbook_name = self.request.get('guestbook_name')
greetings = self.get_greetings(guestbook_name)
stats = memcache.get_stats()
self.response.write('<b>Cache Hits:{}</b><br>'.format(stats['hits']))
self.response.write('<b>Cache Misses:{}</b><br><br>'.format(
stats['misses']))
self.response.write(greetings)
self.response.write("""
<form action="/sign?{}" method="post">
<div><textarea name="content" rows="3" cols="60"></textarea></div>
<div><input type="submit" value="Sign Guestbook"></div>
</form>
<hr>
<form>Guestbook name: <input value="{}" name="guestbook_name">
<input type="submit" value="switch"></form>
</body>
</html>""".format(urllib.urlencode({'guestbook_name': guestbook_name}),
cgi.escape(guestbook_name)))
def get_greetings(self, guestbook_name):
"""
get_greetings()
Checks the cache to see if there are cached greetings.
If not, call render_greetings and set the cache
Args:
guestbook_name: Guestbook entity group key (string).
Returns:
A string of HTML containing greetings.
"""
greetings = memcache.get('{}:greetings'.format(guestbook_name))
if greetings is None:
greetings = self.render_greetings(guestbook_name)
try:
added = memcache.add(
'{}:greetings'.format(guestbook_name), greetings, 10)
if not added:
logging.error('Memcache set failed.')
except ValueError:
logging.error('Memcache set failed - data larger than 1MB')
return greetings
def render_greetings(self, guestbook_name):
"""
render_greetings()
Queries the database for greetings, iterate through the
results and create the HTML.
Args:
guestbook_name: Guestbook entity group key (string).
Returns:
A string of HTML containing greetings
"""
greetings = ndb.gql('SELECT * '
'FROM Greeting '
'WHERE ANCESTOR IS :1 '
'ORDER BY date DESC LIMIT 10',
guestbook_key(guestbook_name))
output = cStringIO.StringIO()
for greeting in greetings:
if greeting.author:
output.write('<b>{}</b> wrote:'.format(greeting.author))
else:
output.write('An anonymous person wrote:')
output.write('<blockquote>{}</blockquote>'.format(
cgi.escape(greeting.content)))
return output.getvalue()
class Guestbook(webapp2.RequestHandler):
def post(self):
# We set the same parent key on the 'Greeting' to ensure each greeting
# is in the same entity group. Queries across the single entity group
# are strongly consistent. However, the write rate to a single entity
# group is limited to ~1/second.
guestbook_name = self.request.get('guestbook_name')
greeting = Greeting(parent=guestbook_key(guestbook_name))
if users.get_current_user():
greeting.author = users.get_current_user().nickname()
greeting.content = self.request.get('content')
greeting.put()
memcache.delete('{}:greetings'.format(guestbook_name))
self.redirect('/?' +
urllib.urlencode({'guestbook_name': guestbook_name}))
app = webapp2.WSGIApplication([('/', MainPage),
('/sign', Guestbook)],
debug=True)