python并发3:使用asyncio编写服务器

asyncio 上一篇我们介绍了 asyncio 包,以及如何使用异步编程管理网络应用中的高并发。在这一篇,我们主要介绍使用 asyncio 包编程的两个例子。 async/await语法 我们先介绍下 async/await 语法,要不然看完这篇可能会困惑,为什么之前使用 asyncio.coroutine 装饰器 和 yield from,这里都是 用的 async 和 await? python并发2:使用asyncio处理并发 async/await 是Python3.5 的新语法,语法如下: async def read_data(db): pass async 是明确将函数声明为协程的关键字,即使没有await表达式,函数执行也会返回一个协程对象。 在协程函数内部,可以在某个表达式之前使用 await 关键字来暂停协程的执行,以等待某协程完成: async def read_data(db): data = await db.fetch('SELECT ...') 这个代码如果使用 asyncio.coroutine 装饰器语法为: @asyncio.coroutine def read_data(db): data = yield from db.fetch('SELECT ...') 这两段代码执行的结果是一样的,也就是说 可以把 asyncio.coroutine 替换为 async, yield from 替换为 await。 使用新的语法有什么好处呢: 使生成器和协程的概念更容易理解,因为语法不同 可以消除由于重构时不小心移出协程中yield 声明而导致的不明确错误,这回导致协程变成普通的生成器。 使用 asyncio 包编写服务器 这个例子主要是使用 asyncio 包 和 unicodedata 模块,实现通过规范名称查找Unicode 字符。 我们先来看一下代码: # charfinder.py import sys import re import unicodedata import pickle import warnings import itertools import functools from collections import namedtuple RE_WORD = re.compile('\w+') RE_UNICODE_NAME = re.compile('^[A-Z0-9 -]+$') RE_CODEPOINT = re.compile('U\+[0-9A-F]{4, 6}') INDEX_NAME = 'charfinder_index.pickle' MINIMUM_SAVE_LEN = 10000 CJK_UNI_PREFIX = 'CJK UNIFIED IDEOGRAPH' CJK_CMP_PREFIX = 'CJK COMPATIBILITY IDEOGRAPH' sample_chars = [ '$', # DOLLAR SIGN 'A', # LATIN CAPITAL LETTER A 'a', # LATIN SMALL LETTER A '\u20a0', # EURO-CURRENCY SIGN '\u20ac', # EURO SIGN ] CharDescription = namedtuple('CharDescription', 'code_str char name') QueryResult = namedtuple('QueryResult', 'count items') def tokenize(text): ''' :param text: :return: return iterable of uppercased words ''' for match in RE_WORD.finditer(text): yield match.group().upper() def query_type(text): text_upper = text.upper() if 'U+' in text_upper: return 'CODEPOINT' elif RE_UNICODE_NAME.match(text_upper): return 'NAME' else: return 'CHARACTERS' class UnicodeNameIndex: # unicode name 索引类 def __init__(self, chars=None): self.load(chars) def load(self, chars=None): # 加载 unicode name self.index = None if chars is None: try: with open(INDEX_NAME, 'rb') as fp: self.index = pickle.load(fp) except OSError: pass if self.index is None: self.build_index(chars) if len(self.index) > MINIMUM_SAVE_LEN: try: self.save() except OSError as exc: warnings.warn('Could not save {!r}: {}' .format(INDEX_NAME, exc)) def save(self): with open(INDEX_NAME, 'wb') as fp: pickle.dump(self.index, fp) def build_index(self, chars=None): if chars is None: chars = (chr(i) for i in range(32, sys.maxunicode)) index = {} for char in chars: try: name = unicodedata.name(char) except ValueError: continue if name.startswith(CJK_UNI_PREFIX): name = CJK_UNI_PREFIX elif name.startswith(CJK_CMP_PREFIX): name = CJK_CMP_PREFIX for word in tokenize(name): index.setdefault(word, set()).add(char) self.index = index def word_rank(self, top=None): # (len(self.index[key], key) 是一个生成器,需要用list 转成列表,要不然下边排序会报错 res = [list((len(self.index[key], key)) for key in self.index)] res.sort(key=lambda item: (-item[0], item[1])) if top is not None: res = res[:top] return res def word_report(self, top=None): for postings, key in self.word_rank(top): print('{:5} {}'.format(postings, key)) def find_chars(self, query, start=0, stop=None): stop = sys.maxsize if stop is None else stop result_sets = [] for word in tokenize(query): # tokenize 是query 的生成器 a b 会是 ['a', 'b'] 的生成器 chars = self.index.get(word) if chars is None: result_sets = [] break result_sets.append(chars) if not result_sets: return QueryResult(0, ()) result = functools.reduce(set.intersection, result_sets) result = sorted(result) # must sort to support start, stop result_iter = itertools.islice(result, start, stop) return QueryResult(len(result), (char for char in result_iter)) def describe(self, char): code_str = 'U+{:04X}'.format(ord(char)) name = unicodedata.name(char) return CharDescription(code_str, char, name) def find_descriptions(self, query, start=0, stop=None): for char in self.find_chars(query, start, stop).items: yield self.describe(char) def get_descriptions(self, chars): for char in chars: yield self.describe(char) def describe_str(self, char): return '{:7}\t{}\t{}'.format(*self.describe(char)) def find_description_strs(self, query, start=0, stop=None): for char in self.find_chars(query, start, stop).items: yield self.describe_str(char) @staticmethod # not an instance method due to concurrency def status(query, counter): if counter == 0: msg = 'No match' elif counter == 1: msg = '1 match' else: msg = '{} matches'.format(counter) return '{} for {!r}'.format(msg, query) def main(*args): index = UnicodeNameIndex() query = ' '.join(args) n = 0 for n, line in enumerate(index.find_description_strs(query), 1): print(line) print('({})'.format(index.status(query, n))) if __name__ == '__main__': if len(sys.argv) > 1: main(*sys.argv[1:]) else: print('Usage: {} word1 [word2]...'.format(sys.argv[0])) 这个模块读取Python内建的Unicode数据库,为每个字符名称中的每个单词建立索引,然后倒排索引,存入一个字典。 例如,在倒排索引中,‘SUN’ 键对应的条目是一个集合,里面是名称中包含’SUN’ 这个词的10个Unicode字符。倒排索引保存在本地一个名为charfinder_index.pickle 的文件中。如果查询多个单词,会计算从索引中所得集合的交集。 运行示例如下: ...

2017-06-30 · 5 min · 1047 words

python web 框架 Sanci 快速入门

简介 Sanic 是一个和类Flask 的基于Python3.5+的web框架,它编写的代码速度特别快。 除了像Flask 以外,Sanic 还支持以异步请求的方式处理请求。这意味着你可以使用新的 async/await 语法,编写非阻塞的快速的代码。 关于 asyncio 包的介绍,请参考之前的一篇文章 python并发2:使用asyncio处理并发 Github 地址 是 https://github.com/channelcat/sanic,感兴趣的可以去贡献代码。 既然它说速度特别快,我们先看下官方提供的 基准测试结果。 Sanic基准测试 这个测试的程序运行在 AWS 实例上,系统是Ubuntu,只使用了一个进程。 Sanic 的开发者说他们的灵感来自于这篇文章 uvloop: Blazing fast Python networking。 那我们就有必要看下uvloop是个什么库。 uvloop uvloop 是 asyncio 默认事件循环的替代品,实现的功能完整,切即插即用。uvloop是用CPython 写的,建于libuv之上。 uvloop 可以使 asyncio 更快。事实上,它至少比 nodejs、gevent 和其他 Python 异步框架要快两倍 。基于 uvloop 的 asyncio 的速度几乎接近了 Go 程序的速度。 安装 uvloop uvloop 还只能在 *nix 平台 和 Python3.5+以上版本使用。 使用pip安装: pip install uvloop 在 asyncio 代码中使用uvloop 也很简单: import asyncio import uvloop asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) 这得代码使得对任何asyncio.get_event_loop() 的调用都将返回一个uvloop实例。 ...

2017-06-25 · 4 min · 730 words

python并发2:使用asyncio处理并发

asyncio asyncio 是Python3.4 之后引入的标准库的,这个包使用事件循环驱动的协程实现并发。 asyncio 包在引入标准库之前代号 “Tulip”(郁金香),所以在网上搜索资料时,会经常看到这种花的名字。 什么是事件循环? wiki 上说:事件循环是”一种等待程序分配事件或者消息的编程架构“。基本上来说事件循环就是:”当A发生时,执行B"。或者用最简单的例子来解释这一概念就是每个浏览器中都存在的JavaScript事件循环。当你点击了某个东西(“当A发生时”),这一点击动作会发送给JavaScript的事件循环,并检查是否存在注册过的onclick 回调来处理这一点击(执行B)。只要有注册过的回调函数就会伴随点击动作的细节信息被执行。事件循环被认为是一种虚幻是因为它不停的手机事件并通过循环来发如何应对这些事件。 对 Python 来说,用来提供事件循环的 asyncio 被加入标准库中。asyncio 重点解决网络服务中的问题,事件循环在这里将来自套接字(socket)的 I/O 已经准备好读和/或写作为“当A发生时”(通过selectors模块)。除了 GUI 和 I/O,事件循环也经常用于在别的线程或子进程中执行代码,并将事件循环作为调节机制(例如,合作式多任务)。如果你恰好理解 Python 的 GIL,事件循环对于需要释放 GIL 的地方很有用。 线程与协程 我们先看两断代码,分别用 threading 模块和asyncio 包实现的一段代码。 # sinner_thread.py import threading import itertools import time import sys class Signal: # 这个类定义一个可变对象,用于从外部控制线程 go = True def spin(msg, signal): # 这个函数会在单独的线程中运行,signal 参数是前边定义的Signal类的实例 write, flush = sys.stdout.write, sys.stdout.flush for char in itertools.cycle('|/-\\'): # itertools.cycle 函数从指定的序列中反复不断地生成元素 status = char + ' ' + msg write(status) flush() write('\x08' * len(status)) # 使用退格符把光标移回行首 time.sleep(.1) # 每 0.1 秒刷新一次 if not signal.go: # 如果 go属性不是 True,退出循环 break write(' ' * len(status) + '\x08' * len(status)) # 使用空格清除状态消息,把光标移回开头 def slow_function(): # 模拟耗时操作 # 假装等待I/O一段时间 time.sleep(3) # 调用sleep 会阻塞主线程,这么做事为了释放GIL,创建从属线程 return 42 def supervisor(): # 这个函数设置从属线程,显示线程对象,运行耗时计算,最后杀死进程 signal = Signal() spinner = threading.Thread(target=spin, args=('thinking!', signal)) print('spinner object:', spinner) # 显示线程对象 输出 spinner object: <Thread(Thread-1, initial)> spinner.start() # 启动从属进程 result = slow_function() # 运行slow_function 行数,阻塞主线程。同时丛书线程以动画形式旋转指针 signal.go = False spinner.join() # 等待spinner 线程结束 return result def main(): result = supervisor() print('Answer', result) if __name__ == '__main__': main() 执行一下,结果大致是这个样子: ...

2017-06-23 · 8 min · 1522 words

python并发 1:使用 futures 处理并发

作为Python程序员,平时很少使用并发编程,偶尔使用也只需要派生出一批独立的线程,然后放到队列中,批量执行。所以,不夸张的说,虽然我知道线程、进程、并行、并发的概念,但每次使用的时候可能还需要再打开文档回顾一下。 现在这一篇还是 《流畅的python》读书笔记,译者在这里把future 翻译为“期物”,我觉得不太合适,既然future不能找到一个合适的词汇,暂时还是直接使用 future 吧。 concurrent.futures future 是一种对象,表示异步执行的操作。这个概念是 concurrent.futures模块和asyncio包的基础。 concurrent.futures 模块是Python3.2 引入的,对于Python2x 版本,Python2.5 以上的版本可以安装 futures 包来使用这个模块。 HUGOMORE42 从Python3.4起,标准库中有两个为Future的类:concurrent.futures.Future 和 asyncio.Future。这两个类作用相同:两个Future类的实例都表示可能已经完成或未完成的延迟计算。 Future 封装待完成的操作,可放入队列,完成的状态可以查询,得到结果(或抛出异常)后可以获取结果(或异常)。 我们知道,如果程序中包含I/O操作,程序会有很高的延迟,CPU会处于等待状态,这时如果我们不使用并发会浪费很多时间。 示例 我们先举个例子: 下边是有两段代码,主要功能都是从网上下载人口前20的国际的国旗: 第一段代码(flagss.py)是依序下载:下载完一个图片后保存到硬盘,然后请求下一张图片; 第二段代码(flagss_threadpool.py)使用 concurrent.futures 模块,批量下载10张图片。 运行分别运行两段代码3次,结果如下: images.py 的结果如下 $ python flags.py BD BR CD CN DE EG ET FR ID IN IR JP MX NG PH PK RU TR US VN 20 flags downloaded in 6.18s $ python flags.py BD BR CD CN DE EG ET FR ID IN IR JP MX NG PH PK RU TR US VN 20 flags downloaded in 5.67s $ python flags.py BD BR CD CN DE EG ET FR ID IN IR JP MX NG PH PK RU TR US VN 20 flags downloaded in 6.55s 可以看到,依次下载10张图片,平均需要6秒 ...

2017-06-17 · 5 min · 995 words

python协程3:用仿真实验学习协程

前两篇我们已经介绍了python 协程的使用和yield from 的原理,这一篇,我们用一个例子来揭示如何使用协程在单线程中管理并发活动。 什么是离散事件仿真 Wiki上的定义是: 离散事件仿真将系统随时间的变化抽象成一系列的离散时间点上的事件,通过按照事件时间顺序处理事件来演进,是一种事件驱动的仿真世界观。离散事件仿真将系统的变化看做一个事件,因此系统任何的变化都只能是通过处理相应的事件来实现,在两个相邻的事件之间,系统状态维持前一个事件发生后的状态不变。 人话说就是一种把系统建模成一系列事件的仿真系统。在离散事件仿真中,仿真“钟”向前推进的量不是固定的,而是直接推进到下一个事件模型的模拟时间。 假设我们抽象模拟出租车的运营过程,其中一个事件是乘客上车,下一个事件则是乘客下车。不管乘客做了5分钟还是50分钟,一旦下车,仿真钟就会更新,指向此次运营的结束时间。 事件?是不是想到了协程! 协程恰好为实现离散事件仿真提供了合理的抽象。 HUGOMORE42 第一门面向对象的语音 Simula 引入协程这个概念就是为了支持仿真。 Simpy 是一个实现离散事件仿真的Python包,通过一个协程表示离散事件仿真系统的各个进程。 出租车对运营仿真 仿真程序会创建几辆出租车,每辆出租车会拉几个乘客,然后回家。出租车会首先驶离车库,四处徘徊,寻找乘客;拉到乘客后,行程开始;乘客下车后,继续四处徘徊。 徘徊和行程所用的时间使用指数分布生成,我们将时间设为分钟数,以便显示清楚。 完整代码如下:(taxi_sim.py) #! -*- coding: utf-8 -*- import random import collections import queue import argparse DEFAULT_NUMBER_OF_TAXIS = 3 DEFAULT_END_TIME = 180 SEARCH_DURATION = 5 TRIP_DURATION = 20 DEPARTURE_INTERAVAL = 5 # time 是事件发生的仿真时间,proc 是出租车进程实例的编号,action是描述活动的字符串 Event = collections.namedtuple('Event', 'time proc action') # 开始 出租车进程 # 每辆出租车调用一次taxi_process 函数,创建一个生成器对象,表示各辆出租车的运营过程。 def taxi_process(ident, trips, start_time=0): ''' 每次状态变化时向创建事件,把控制权交给仿真器 :param ident: 出租车编号 :param trips: 出租车回家前的行程数量 :param start_time: 离开车库的时间 :return: ''' time = yield Event(start_time, ident, 'leave garage') # 产出的第一个Event for i in range(trips): # 每次行程都会执行一遍这个代码块 # 产出一个Event实例,表示拉到了乘客 协程在这里暂停 等待下一次send() 激活 time = yield Event(time, ident, 'pick up passenger') # 产出一个Event实例,表示乘客下车 协程在这里暂停 等待下一次send() 激活 time = yield Event(time, ident, 'drop off passenger') # 指定的行程数量完成后,for 循环结束,最后产出 'going home' 事件。协程最后一次暂停 yield Event(time, ident, 'going home') # 协程执行到最后 抛出StopIteration 异常 def compute_duration(previous_action): '''使用指数分布计算操作的耗时''' if previous_action in ['leave garage', 'drop off passenger']: # 新状态是四处徘徊 interval = SEARCH_DURATION elif previous_action == 'pick up passenger': # 新状态是开始行程 interval = TRIP_DURATION elif previous_action == 'going home': interval = 1 else: raise ValueError('Unkonw previous_action: %s' % previous_action) return int(random.expovariate(1/interval)) + 1 # 开始仿真 class Simulator: def __init__(self, procs_map): self.events = queue.PriorityQueue() # 带优先级的队列 会按时间正向排序 self.procs = dict(procs_map) # 从获取的procs_map 参数中创建本地副本,为了不修改用户传入的值 def run(self, end_time): ''' 调度并显示事件,直到时间结束 :param end_time: 结束时间 只需要指定一个参数 :return: ''' # 调度各辆出租车的第一个事件 for iden, proc in sorted(self.procs.items()): first_event = next(proc) # 预激协程 并产出一个 Event 对象 self.events.put(first_event) # 把各个事件加到self.events 属性表示的 PriorityQueue对象中 # 此次仿真的主循环 sim_time = 0 # 把 sim_time 归0 while sim_time < end_time: if self.events.empty(): # 事件全部完成后退出循环 print('*** end of event ***') break current_event = self.events.get() # 获取优先级最高(time 属性最小)的事件 sim_time, proc_id, previous_action = current_event # 更新 sim_time print('taxi:', proc_id, proc_id * ' ', current_event) active_proc = self.procs[proc_id] # 从self.procs 字典中获取表示当前活动的出租车协程 next_time = sim_time + compute_duration(previous_action) try: next_event = active_proc.send(next_time) # 把计算得到的时间发送给出租车协程。协程会产出下一个事件,或者抛出 StopIteration except StopIteration: del self.procs[proc_id] # 如果有异常 表示已经退出, 删除这个协程 else: self.events.put(next_event) # 如果没有异常,把next_event 加入到队列 else: # 如果超时 则走到这里 msg = '*** end of simulation time: {} event pendding ***' print(msg.format(self.events.qsize())) def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS, seed=None): '''初始化随机生成器,构建过程,运行仿真程序''' if seed is not None: random.seed(seed) # 获取可复现的结果 # 构建taxis 字典。值是三个参数不同的生成器对象。 taxis = {i: taxi_process(i, (i + 1) * 2, i*DEPARTURE_INTERAVAL) for i in range(num_taxis)} sim = Simulator(taxis) sim.run(end_time) if __name__ == '__main__': parser = argparse.ArgumentParser(description='Taxi fleet simulator.') parser.add_argument('-e', '--end-time', type=int, default=DEFAULT_END_TIME, help='simulation end time; default=%s' % DEFAULT_END_TIME) parser.add_argument('-t', '--taxis', type=int, default=DEFAULT_NUMBER_OF_TAXIS, help='number of taxis running; default = %s' % DEFAULT_NUMBER_OF_TAXIS) parser.add_argument('-s', '--seed', type=int, default=None, help='random generator seed (for testing)') args = parser.parse_args() main(args.end_time, args.taxis, args.seed) 运行程序, ...

2017-06-15 · 4 min · 669 words

python协程2:yield from 从入门到精通

上一篇python协程1:yield的使用介绍了: 生成器作为协程使用时的行为和状态 使用装饰器预激协程 调用方如何使用生成器对象的 .throw(…) 和 .close() 方法控制协程 这一篇将介绍: 协程终止时如何返回值 yield新句法的用途和语义 HUGOMORE42 让协程返回值 先看一个例子: 这段代码会返回最终均值的结果,每次激活协程时不会产出移动平均值,而是最后一次返回。 #! -*- coding: utf-8 -*- from collections import namedtuple Result = namedtuple('Result', 'count average') def averager(): total = 0.0 count = 0 average = None while True: term = yield if term is None: break # 为了返回值,协程必须正常终止;这里是退出条件 total += term count += 1 average = total/count # 返回一个namedtuple,包含count和average两个字段。在python3.3前,如果生成器返回值,会报错 return Result(count, average) 我们调用这段代码,结果如下 >>> coro_avg = averager() >>> next(coro_avg) >>> coro_avg.send(20) # 并没有返回值 >>> coro_avg.send(30) >>> coro_avg.send(40) >>> coro_avg.send(None) # 发送None终止循环,导致协程结束。生成器对象会抛出StopIteration异常。异常对象的value属性保存着返回值。 Traceback (most recent call last): ... StopIteration: Result(count=3, average=30) return 表达式的值会传给调用方,赋值给StopIteration 异常的一个属性。这样做虽然看着别扭,但为了保留生成器对象耗尽时抛出StopIteration异常的行为,也可以理解。 ...

2017-06-14 · 5 min · 858 words

python协程1:协程 10分钟入门

最近找到一本python好书《流畅的python》,是到现在为止看到的对python高级特性讲述最详细的一本。 看了协程一章,做个读书笔记,加深印象。 协程定义 协程的底层架构是在pep342 中定义,并在python2.5 实现的。 HUGOMORE42 python2.5 中,yield关键字可以在表达式中使用,而且生成器API中增加了 .send(value)方法。生成器可以使用.send(…)方法发送数据,发送的数据会成为生成器函数中yield表达式的值。 协程是指一个过程,这个过程与调用方协作,产出有调用方提供的值。因此,生成器可以作为协程使用。 除了 .send(…)方法,pep342 和添加了 .throw(…)(让调用方抛出异常,在生成器中处理)和.close()(终止生成器)方法。 python3.3后,pep380对生成器函数做了两处改动: 生成器可以返回一个值;以前,如果生成器中给return语句提供值,会抛出SyntaxError异常。 引入yield from 语法,使用它可以把复杂的生成器重构成小型的嵌套生成器,省去之前把生成器的工作委托给子生成器所需的大量模板代码。 协程生成器的基本行为 首先说明一下,协程有四个状态,可以使用inspect.getgeneratorstate(…)函数确定: GEN_CREATED # 等待开始执行 GEN_RUNNING # 解释器正在执行(只有在多线程应用中才能看到这个状态) GEN_SUSPENDED # 在yield表达式处暂停 GEN_CLOSED # 执行结束 #! -*- coding: utf-8 -*- import inspect # 协程使用生成器函数定义:定义体中有yield关键字。 def simple_coroutine(): print('-> coroutine started') # yield 在表达式中使用;如果协程只需要从客户那里接收数据,yield关键字右边不需要加表达式(yield默认返回None) x = yield print('-> coroutine received:', x) my_coro = simple_coroutine() my_coro # 和创建生成器的方式一样,调用函数得到生成器对象。 # 协程处于 GEN_CREATED (等待开始状态) print(inspect.getgeneratorstate(my_coro)) my_coro.send(None) # 首先要调用next()函数,因为生成器还没有启动,没有在yield语句处暂停,所以开始无法发送数据 # 发送 None 可以达到相同的效果 my_coro.send(None) next(my_coro) # 此时协程处于 GEN_SUSPENDED (在yield表达式处暂停) print(inspect.getgeneratorstate(my_coro)) # 调用这个方法后,协程定义体中的yield表达式会计算出42;现在协程会恢复,一直运行到下一个yield表达式,或者终止。 my_coro.send(42) print(inspect.getgeneratorstate(my_coro)) 运行上述代码,输出结果如下 ...

2017-06-12 · 4 min · 644 words

Amazon DynamoDB 入门8:删除项目

上一篇介绍了DynamoDB 的更新,这一篇将会介绍项目删除操作和dynamoab-py 从表中删除数据 在 SQL 中,DELETE 语句从表中删除一个或多个行。DynamoDB 使用 DeleteItem 操作一次删除一个项目。 SQL 在 SQL 中,可使用 DELETE 语句删除一个或多个行。WHERE 子句确定要修改的行。示例如下: HUGOMORE42 DELETE FROM Music WHERE Artist = ‘The Acme Band’ AND SongTitle = ‘Look Out, World’; 我们可以修改 WHERE 子句以删除多个行。例如,删除某个特殊艺术家的所有歌曲,如下所示: DELETE FROM Music WHERE Artist = ‘The Acme Band’ Note 如果省略 WHERE 子句,则数据库会尝试从表中删除所有行。 DynamoDB 在 DynamoDB 中,可使用 DeleteItem 操作修改单个项目。 (http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/APIReference/API_DeleteItem.html?shortFooter=true)[API 语法如下]: { "ConditionExpression": "string", "ExpressionAttributeNames": { "string" : "string" }, "ExpressionAttributeValues": { "string" : { "B": blob, "BOOL": boolean, "BS": [ blob ], "L": [ "AttributeValue" ], "M": { "string" : "AttributeValue" }, "N": "string", "NS": [ "string" ], "NULL": boolean, "S": "string", "SS": [ "string" ] } }, "Key": { "string" : { "B": blob, "BOOL": boolean, "BS": [ blob ], "L": [ "AttributeValue" ], "M": { "string" : "AttributeValue" }, "N": "string", "NS": [ "string" ], "NULL": boolean, "S": "string", "SS": [ "string" ] } }, "ReturnConsumedCapacity": "string", "ReturnItemCollectionMetrics": "string", "ReturnValues": "string", "TableName": "string" } 参数说明: ...

2017-03-03 · 3 min · 435 words

Amazon DynamoDB 入门7:项目更新

上一节介绍了DynamoDB 的查询,本来计划这一节介绍使用索引的查询,不过随机看到了更新操作,就先写更新操作吧 update (修改表中的数据) SQL 语言提供用于修改数据的 UPDATE 语句。DynamoDB 使用 UpdateItem 操作完成类似的任务。 SQL 在 SQL 中,可使用 UPDATE 语句修改一个或多个行。SET 子句为一个或多个列指定新值,WHERE 子句确定修改的行。示例如下: UPDATE Music SET RecordLabel = 'Global Records' WHERE Artist = 'No One You Know' AND SongTitle = 'Call Me Today'; HUGOMORE42 如果任何行均不匹配 WHERE 子句,则 UPDATE 语句不起作用。 DynamoDB 在 DynamoDB 中,可使用 UpdateItem 操作修改单个项目。 API 语法如下: { "AttributeUpdates": { "string" : { "Action": "string", "Value": { "B": blob, "BOOL": boolean, "BS": [ blob ], "L": [ "AttributeValue" ], "M": { "string" : "AttributeValue" }, "N": "string", "NS": [ "string" ], "NULL": boolean, "S": "string", "SS": [ "string" ] } } }, "ConditionalOperator": "string", "ConditionExpression": "string", "Expected": { "string" : { "AttributeValueList": [ { "B": blob, "BOOL": boolean, "BS": [ blob ], "L": [ "AttributeValue" ], "M": { "string" : "AttributeValue" }, "N": "string", "NS": [ "string" ], "NULL": boolean, "S": "string", "SS": [ "string" ] } ], "ComparisonOperator": "string", "Exists": boolean, "Value": { "B": blob, "BOOL": boolean, "BS": [ blob ], "L": [ "AttributeValue" ], "M": { "string" : "AttributeValue" }, "N": "string", "NS": [ "string" ], "NULL": boolean, "S": "string", "SS": [ "string" ] } } }, "ExpressionAttributeNames": { "string" : "string" }, "ExpressionAttributeValues": { "string" : { "B": blob, "BOOL": boolean, "BS": [ blob ], "L": [ "AttributeValue" ], "M": { "string" : "AttributeValue" }, "N": "string", "NS": [ "string" ], "NULL": boolean, "S": "string", "SS": [ "string" ] } }, "Key": { "string" : { "B": blob, "BOOL": boolean, "BS": [ blob ], "L": [ "AttributeValue" ], "M": { "string" : "AttributeValue" }, "N": "string", "NS": [ "string" ], "NULL": boolean, "S": "string", "SS": [ "string" ] } }, "ReturnConsumedCapacity": "string", "ReturnItemCollectionMetrics": "string", "ReturnValues": "string", "TableName": "string", "UpdateExpression": "string" } 参数说明: ...

2017-02-07 · 4 min · 816 words

Amazon DynamoDB 入门6:query 和 scan

上一节我们介绍了DynamoDB索引的创建及管理,这一节我们将介绍query(查询)和scan(扫描)的使用。 查询Query SQL 可使用 SELECT 语句查询关键列、非关键列或任意组合。WHERE 子句确定返回的行。 DynamoDB Query 操作提供对存储数据的物理位置的快速高效访问。 可以将 Query 用于任何具有复合主键(分区键和排序键)的表。这里的表必须指定分区键的相等条件,并且可以选择性为排序键提供另一个条件。 KeyConditionExpression 参数指定要查询的键值。 HUGOMORE42 可使用可选 FilterExpression 在结果中的找出某些符号条件的项目。 在 DynamoDB 中,必须使用 ExpressionAttributeValues 作为表达式参数(例如,KeyConditionExpression和 FilterExpression)中的占位符。这类似于在关系数据库中使用绑定变量,在运行时将实际值代入 SELECT语句。 下边是query的语法: response = table.query( IndexName='string', Select='ALL_ATTRIBUTES'|'ALL_PROJECTED_ATTRIBUTES'|'SPECIFIC_ATTRIBUTES'|'COUNT', AttributesToGet=[ 'string', ], Limit=123, ConsistentRead=True|False, ConditionalOperator='AND'|'OR', ScanIndexForward=True|False, ExclusiveStartKey={ 'string': 'string'|123|Binary(b'bytes')|True|None|set(['string'])|set([123])|set([Binary(b'bytes')])|[]|{} }, ReturnConsumedCapacity='INDEXES'|'TOTAL'|'NONE', ProjectionExpression='string', FilterExpression=Attr('myattribute').eq('myvalue'), KeyConditionExpression=Key('mykey').eq('myvalue'), ExpressionAttributeNames={ 'string': 'string' }, ExpressionAttributeValues={ 'string': 'string'|123|Binary(b'bytes')|True|None|set(['string'])|set([123])|set([Binary(b'bytes')])|[]|{} } ) 参数说明: ...

2017-01-31 · 4 min · 766 words