Set Up

下面是我们服务器所需的一些样板代码:

"""NoSQL database written in Python"""

# Standard library imports

import socket

HOST = 'localhost'

PORT = 50505

SOCKET = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

STATS = {

'PUT': {'success': 0, 'error': 0},

'GET': {'success': 0, 'error': 0},

'GETLIST': {'success': 0, 'error': 0},

'PUTLIST': {'success': 0, 'error': 0},

'INCREMENT': {'success': 0, 'error': 0},

'APPEND': {'success': 0, 'error': 0},

'DELETE': {'success': 0, 'error': 0},

'STATS': {'success': 0, 'error': 0},

}

很容易看到, 上面的只是一个包的导入和一些数据的初始化。

Set up(Cont'd)

接下来我会跳过一些代码, 以便能够继续展示上面准备部分剩余的代码。 注意它涉及到了一些尚不存在的一些函数, 不过没关系, 我们会在后面涉及。 在完整版(将会呈现在最后)中, 所有内容都会被有序编排。 这里是剩余的安装代码:

COMMAND_HANDERS = {

'PUT': handle_put,

'GET': handle_get,

'GETLIST': handle_getlist,

'PUTLIST': handle_putlist,

'INCREMENT': handle_increment,

'APPEND': handle_append,

'DELETE': handle_delete,

'STATS': handle_stats,

}

DATA = {}

def main():

"""Main entry point for script"""

SOCKET.bind(HOST, PORT)

SOCKET.listen(1)

while 1:

connection, address = SOCKET.accept()

print('New connection from [{}]'.format(address))

data = connection.recv(4096).decode()

command, key, value = parse_message(data)

if command == 'STATS':

response = handle_stats()

elif command in ('GET', 'GETLIST', 'INCREMENT', 'DELETE'):

response = COMMAND_HANDERS[command](key)

elif command in (

'PUT',

'PUTLIST',

'APPEND', ):

response = COMMAND_HANDERS[command](key, value)

else:

response = (False, 'Unknown command type {}'.format(command))

update_stats(command, response[0])

connection.sandall('{};{}'.format(response[0], response[1]))

connection.close()

if __name__ == '__main__':

main()

我们创建了 COMMAND_HANDLERS, 它常被称为是一个 查找表 (look-up table) . COMMAND_HANDLERS 的工作是将命令与用于处理该命令的函数进行关联起来。 比如说, 如果我们收到一个 GET 命令, COMMAND_HANDLERS[command](key) 就等同于说 handle_get(key) . 记住,在 Python 中, 函数可以被认为是一个值,并且可以像其他任何值一样被存储在一个 dict 中。

在上面的代码中, 虽然有些命令请求的参数相同,但是我仍决定分开处理每个命令。 尽管可以简单粗暴地强制所有的 handle_ 函数接受一个 key 和一个 value , 但是我希望这些处理函数条理能够更加有条理, 更加容易测试,同时减少出现错误的可能性。

注意 socket 相关的代码已是十分极简。 虽然整个服务器基于 TCP/IP 通信, 但是并没有太多底层的网络交互代码。

最后还须需要注意的一小点: DATA 字典, 因为这个点并不十分重要, 因而你很可能会遗漏它。 DATA 就是实际用来存储的 key-value pair, 正是它们实际构成了我们的数据库。

Command Parser

下面来看一些 命令解析器 (command parser) , 它负责解释接收到的消息:

def parse_message(data):

"""Return a tuple containing the command, the key, and (optionally) the

value cast to the appropriate type."""

command, key, value, value_type = data.strip().split(';')

if value_type:

if value_type == 'LIST':

value = value.split(',')

elif value_type == 'INT':

value = int(value)

else:

value = str(value)

else:

value = None

return command, key, value

这里我们可以看到发生了类型转换 (type conversion). 如果希望值是一个 list, 我们可以通过对 string 调用 str.split(',') 来得到我们想要的值。 对于 int, 我们可以简单地使用参数为 string 的 int() 即可。 对于字符串与 str() 也是同样的道理。

Command Handlers

下面是命令处理器 (command handler) 的代码. 它们都十分直观,易于理解。 注意到虽然有很多的错误检查, 但是也并不是面面俱到, 十分庞杂。 在你阅读的过程中,如果发现有任何错误请移步 这里 进行讨论.

def update_stats(command, success):

"""Update the STATS dict with info about if executing *command* was a

*success*"""

if success:

STATS[command]['success'] += 1

else:

STATS[command]['error'] += 1

def handle_put(key, value):

"""Return a tuple containing True and the message to send back to the

client."""

DATA[key] = value

return (True, 'key [{}] set to [{}]'.format(key, value))

def handle_get(key):

"""Return a tuple containing True if the key exists and the message to send

back to the client"""

if key not in DATA:

return (False, 'Error: Key [{}] not found'.format(key))

else:

return (True, DATA[key])

def handle_putlist(key, value):

"""Return a tuple containing True if the command succeeded and the message

to send back to the client."""

return handle_put(key, value)

def handle_putlist(key, value):

"""Return a tuple containing True if the command succeeded and the message

to send back to the client"""

return handle_put(key, value)

def handle_getlist(key):

"""Return a tuple containing True if the key contained a list and the

message to send back to the client."""

return_value = exists, value = handle_get(key)

if not exists:

return return_value

elif not isinstance(value, list):

return (False, 'ERROR: Key [{}] contains non-list value ([{}])'.format(

key, value))

else:

return return_value

def handle_increment(key):

"""Return a tuple containing True if the key's value could be incremented

and the message to send back to the client."""

return_value = exists, value = handle_get(key)

if not exists:

return return_value

elif not isinstance(list_value, list):

return (False, 'ERROR: Key [{}] contains non-list value ([{}])'.format(

key, value))

else:

DATA[key].append(value)

return (True, 'Key [{}] had value [{}] appended'.format(key, value))

def handle_delete(key):

"""Return a tuple containing True if the key could be deleted and the

message to send back to the client."""

if key not in DATA:

return (

False,

'ERROR: Key [{}] not found and could not be deleted.'.format(key))

else:

del DATA[key]

def handle_stats():

"""Return a tuple containing True and the contents of the STATS dict."""

return (True, str(STATS))

有两点需要注意: 多重赋值 (multiple assignment) 和代码重用. 有些函数仅仅是为了更加有逻辑性而对已有函数的简单包装而已, 比如 handle_get 和 handle_getlist . 由于我们有时仅仅是需要一个已有函数的返回值,而其他时候却需要检查该函数到底返回了什么内容, 这时候就会使用 多重赋值 。

来看一下 handle_append . 如果我们尝试调用 handle_get 但是 key 并不存在时, 那么我们简单地返回 handle_get 所返回的内容。 此外, 我们还希望能够将 handle_get 返回的 tuple 作为一个单独的返回值进行引用。 那么当 key 不存在的时候, 我们就可以简单地使用 return return_value .

如果它 确实存在 , 那么我们需要检查该返回值。并且, 我们也希望能够将 handle_get 的返回值作为单独的变量进行引用。 为了能够处理上述两种情况,同时考虑需要分开处理结果的情形,我们使用了多重赋值。 如此一来, 就不必书写多行代码, 同时能够保持代码清晰。 return_value = exists, list_value = handle_get(key) 能够显式地表明我们将要以至少两种不同的方式引用 handle_get 的返回值。

How Is This a Database?

上面的程序显然并非一个 RDBMS, 但却绝对称得上是一个 NoSQL 数据库。它如此易于创建的原因是我们并没有任何与 数据 (data) 的实际交互。 我们只是做了极简的类型检查,存储用户所发送的任何内容。 如果需要存储更加结构化的数据, 我们可能需要针对数据库创建一个 schema 用于存储和检索数据。

既然 NoSQL 数据库更容易写, 更容易维护,更容易实现, 那么我们为什么不是只使用 mongoDB 就好了? 当然是有原因的, 还是那句话,有得必有失, 我们需要在 NoSQL 数据库所提供的数据灵活性 (data flexibility) 基础上权衡数据库的可搜索性 (searchability).

Querying Data

假如我们上面的 NoSQL 数据库来存储早前的 Car 数据。 那么我们可能会使用 VIN 作为 key, 使用一个列表作为每列的值, 也就是说, 2134AFGER245267 = ['Lexus', 'RX350', 2013, Black] . 当然了, 我们已经丢掉了列表中每个索引的 涵义 (meaning) . 我们只需要知道在某个地方索引 1 存储了汽车的 Model , 索引 2 存储了 Year.

糟糕的事情来了, 当我们想要执行先前的查询语句时会发生什么? 找到 1994 年所有车的颜色将会变得噩梦一般。 我们必须遍历 DATA 中的 每一个值 来确认这个值是否存储了 car 数据亦或根本是其他不相关的数据, 比如说检查索引 2, 看索引 2 的值是否等于 1994,接着再继续取索引 3 的值. 这比 table scan 还要糟糕,因为它不仅要扫描每一行数据,还需要应用一些复杂的规则来回答查询。

NoSQL 数据库的作者当然也意识到了这些问题,(鉴于查询是一个非常有用的 feature) 他们也想出了一些方法来使得查询变得不那么 “遥不可及”。一个方法是结构化所使用的数据,比如 JSON, 允许引用其他行来表示关系。 同时, 大部分 NoSQL 数据库都有名字空间 (namespace) 的概念, 单一类型的数据可以被存储在数据库中该类型所独有的 "section" 中,这使得查询引擎能够利用所要查询数据的 "shape" 信息。

当然了,尽管为了增强可查询性已经存在 (并且实现了)了一些更加复杂的方法, 但是在存储更少量的 schema 与增强可查询性之间做出妥协始终是一个不可逃避的问题。 本例中我们的数据库仅支持通过 key 进行查询。 如果我们需要支持更加丰富的查询, 那么事情就会变得复杂的多了。

Summary

至此, 希望 "NoSQL" 这个概念已然十分清晰。 我们学习了一点 SQL, 并且了解了 RDBMS 是如何工作的。 我们看到了如何从一个 RDBMS 中检索数据 (使用 SQL 查询 (query)). 通过搭建了一个玩具级别的 NoSQL 数据库, 了解了在可查询性与简洁性之间面临的一些问题, 还讨论了一些数据库作者应对这些问题时所采用的一些方法。

即便是简单的 key-value 存储, 关于数据库的知识也是浩瀚无穷。虽然我们仅仅是探讨了其中的星星点点, 但是仍然希望你已经了解了 NoSQL 到底指的是什么, 它是如何工作的, 什么时候用比较好。

22/2<12

python数据库操作nosql_用Python写一个NoSQL数据库相关推荐

  1. python提取数据库nosql_用 Python 写一个 NoSQL 数据库

    本文译自 What is a NoSQL Database? Learn By Writing One In Python. 完整的示例代码已经放到了 GitHub 上, 请 点击这里, 这仅是一个极 ...

  2. python编写数据库连接工具_详解使用Python写一个向数据库填充数据的小工具(推荐)...

    一. 背景 公司又要做一个新项目,是一个合作型项目,我们公司出web展示服务,合作伙伴线下提供展示数据. 而且本次项目是数据统计展示为主要功能,并没有研发对应的数据接入接口,所有展示数据源均来自数据库 ...

  3. 用 Python 的GUI 库 PySimpleGUI,写一个肥胖指数计算工具,Python开发就是这么简单

    Python 的 GUI 库 PySimpleGUI,写一个简单的有界面的小工具很简单!这里我们来写一个计算肥胖指数(BMI)的小工具.首先,通过百度得到 BMI 指数的计算公式为: 体重 / (身高 ...

  4. 自己写一个微型数据库_“最国际化的微型机构:”两名伦敦训练营的毕业生如何建造了一个远程…...

    自己写一个微型数据库 by Rebecca Radding 由丽贝卡·拉丁(Rebecca Radding) "最国际化的微型机构:"两名伦敦训练营毕业生如何与加沙的软件工程师建立 ...

  5. DML语言(数据库操作语言)和DQL语言(数据库查询语言)

    DML语言(数据库操作语言)和DQL语言(数据库查询语言) 1.DML语言(Data Manipulation Language) 1.1.添加语句(insert) INSERT INTO `teac ...

  6. 数据库入门:如何选择SQL和NoSQL数据库

    数据是数据科学所有子领域的基础.无论您要建造的产品用途如何,都需要获取和分析数据.通常,您需要的这些数据存储在DBMS(数据库管理系统)中. 本文来自<数据黑客>,登录官网可精彩资讯和文章 ...

  7. python写登录界面跳转至数据库操作_pycharm+jdango+python实现创建web工程并实现简单登陆...

    本文通过pycharm和流行的python web开发框架jdango以及python3.6实现创建web工程并且实现简单登陆的功能. Pycharm下载不再细说,python下载也不再说,jdang ...

  8. python数据库操作sqlite_使用Python对SQLite数据库操作

    原博文 2017-04-05 15:25 − SQLite是一种嵌入式数据库,它的数据库就是一个文件.由于SQLite本身是C写的,而且体积很小,所以,经常被集成到各种应用程序中,甚至在IOS和And ...

  9. 用 python+tkinter+有道云API 写一个自用的背单词软件

    目录 导语 程序功能 需要用的工具 需要安装的python包 程序代码 功能类函数Functions 键盘响应 窗体设置 Sql连接 Gui程序打包 结语 参考文献 导语 最近在学英语,新单词需要反复 ...

最新文章

  1. APUE(第七章)进程环境
  2. document.getElementsByTagName()方法的返回值
  3. 一个后端开发人员的node.js学习笔记(一)安装与第一个服务器
  4. JS获取用户控件中的子控件Id
  5. Spring Boot + EasyExcel 导入导出,好用到爆!
  6. 上传失败 已保存至草稿_特大福利 清睿口语100成都会议专家讲座视频已上传至口语100网站...
  7. 诡异的编码和字节长度
  8. mysql2008优化_SQL server 2008 数据库优化常用脚本
  9. 华为 MateBook 14 Linux 版真机:搭载深度 Linux 系统
  10. access insert语句怎么写_被缠上了,小王问我怎么在 Spring Boot 中使用 JDBC 连接 MySQL
  11. RocketMQ源码 — 二、 NameServer
  12. 5个常用的Java微服务开源框架推荐
  13. 隐藏水滴屏的软件_手机隐藏水滴屏的APP
  14. Scala语言精华版笔记整理
  15. 13.6.1 PDF 偏执狂
  16. 2020-08-15
  17. [转] 在天堂与地狱之间——清华浪子梦断中关村
  18. ps的两种填充效果快捷键
  19. 太极安装的应用打开闪退_BUG:通过太极阴创建应用什么值得买的过程中太极闪退,应用创建失败...
  20. Allegro PCB Designer 16.6打开brd文件空白的方法

热门文章

  1. 做一个可以和时空分类的神经网络
  2. 比较双曲正切tanh与sigmoid激活函数的性能差异
  3. cmd安装pip_离线情况下怎么安装numpy、pandas和matplotlib?一步一步教你
  4. 【控制】《鲁棒控制-线性矩阵不等式处理方法》-俞立老师-第5章-不确定系统的分析与综合
  5. 3.1 基础模型-深度学习第五课《序列模型》-Stanford吴恩达教授
  6. 在Linux中设置UMASK值
  7. 01、DFT-全面了解如何测试一颗芯片
  8. Linux下dislocate命令用法,五个超酷Linux命令
  9. 二维大地电磁有限元数值模拟矩形+线性插值
  10. 十二、linux LED初始化