mysql的nosql功能_“NoSQL”的定义、作⽤和使⽤⽅法详细说
明
这仅是⼀个极简的demo,旨在动⼿了解概念.
NoSQL这个词在近些年正变得随处可见.但是到底“NoSQL”指的是什么?它是如何并且为什么这么有⽤?
在本⽂,我们将会通过纯
Python(我⽐较喜欢叫它,“轻结构化的伪代码”)写⼀个NoSQL数据库来回答这些问题.
OldSQL
很多情况下,SQL已经成为“数据库”(database)的⼀个同义词.实际上,SQL是StrcturedQueryLanguage的⾸字母缩写,⽽并⾮指数据库技术本⾝.更确切地说,它所指的是从RDBMS(关系型数据库管理系统,RelationalDatabaseManagementSystem)中检索数据的⼀门语
⾔.MySQL,MSSQLServer和Oracle都属于RDBMS的其中⼀员.
RDBMS中的R,即“Relational”(有关系,关联的),是其中内容最丰富的部分.数据通过表(table)进⾏组织,每张表都是⼀些由类型(type)相关联的列(column)构成.所有表,列及其类的类型被称为数据库的schema(架构或模式).schema通过每张表的描述信息完整刻画了数据库的结构.⽐如,⼀张叫做Car的表可能有以下⼀些列:
Make:astring
Model:astring
Year:afour-digitnumber;alternatively,adate
Color:astring
VIN(VehicleIdentificationNumber):astring
在⼀张表中,每个单⼀的条⽬叫做⼀⾏(row),或者⼀条记录(record).为了区分每条记录,通常会定义⼀个主键(primarykey).表中的主键是其中⼀列,它能够唯⼀标识每⼀⾏.在表Car中,VIN是⼀个天然的主键选择,因为它能够保证每辆车具有唯⼀的标识.两个不同的⾏可能会在
Make,Model,Year和Color列上有相同的值,但是对于不同的车⽽⾔,肯定会有不同的VIN.反之,只要两⾏拥有同⼀个VIN,我们不必去检查其他列就可以认为这两⾏指的的就是同⼀辆车.
Querying
SQL能够让我们通过对数据库进⾏query(查询)来获取有⽤的信息.查询简单来说,查询就是⽤⼀个结构化语⾔向RDBMS提问,并将其返回的⾏解释为问题的答案.假设数据库表⽰了美国所有的注册车辆,为了获取所有的记录,我们可以通过在数据库上进⾏如下的SQL查询:
SELECTMake,ModelFROMCar;
将SQL⼤致翻译成中⽂:
“SELECT”:“向我展⽰”java集合转化为数组
“Make,Model”:“Make和Model的值”
“FROMCar”:“对表Car中的每⼀⾏”
也就是,“向我展⽰表Car每⼀⾏中Make和Model的值”.执⾏查询后,我们将会得到⼀些查询的结果,其中每个都是Make和Model.如果我们仅关⼼在1994年注册的车的颜⾊,那么可以:
SELECTColorFROMCarWHEREYear=1994;round函数万元
此时,我们会得到⼀个类似如下的列表:
Black
Red
Red
White
Blue
Black
White
Yellow
最后,我们可以通过使⽤表的(primarykey)主键,这⾥就是VIN来指定查询⼀辆车:
SELECT*FROMCarWHEREVIN='2134AFGER245267'
上⾯这条查询语句会返回所指定车辆的属性信息.
navicat mysql免费版主键被定义为唯⼀不可重复的.也就是说,带有某⼀指定VIN的车辆在表中⾄多只能出现⼀次.这⼀点⾮常重要,为什么?来看⼀个例⼦:
Relations
假设我们正在经营⼀个汽车修理的业务.除了其他⼀些必要的事情,我们还需要追踪⼀辆车的服务历史,即在该辆车上所有的修整记录.那么我们可能会创建包含以下⼀些列的ServiceHistory表:
VIN|Make|Model|Year|Color|ServicePerformed|Mechanic|Price|Date
这样,每次当车辆维修以后,我们就在表中添加新的⼀⾏,并写⼊该次服务我们做了⼀些什么事情,是哪位维修⼯,花费多少和服务时间等.
但是等⼀下,我们都知道,对于同⼀辆车⽽⾔,所有车辆⾃⾝信息有关的列是不变的。也就是说,如果把我的Black2014LexusRX350修整10次的话,那么即使Make,Model,Year和Color这些信息并不会改变,每⼀次仍然重复记录了这些信息.与⽆效的重复记录相⽐,⼀个更合理的做法是对此类信息只存储⼀次,并在有需要的时候进⾏查询。
那么该怎么做呢?我们可以创建第⼆张表:Vehicle,它有如下⼀些列:
VIN|Make|Model|Year|Color
这样⼀来,对于ServiceHistory表,我们可以精简为如下⼀些列:
VIN|ServicePerformed|Mechanic|Price|Date
你可能会问,为什么VIN会在两张表中同时出现?因为我们需要有⼀个⽅式来确认在ServiceHistory表的这辆车指的就是Vehicle表中的那辆车,也就是需要确认两张表中的两条记录所表⽰的是同⼀辆车。
这样的话,我们仅需要为每辆车的⾃⾝信息存储⼀次即可.每次当车辆过来维修的时候,我们就在ServiceHistory表中创建新的⼀⾏,⽽不必在Vehicle表中添加新的记录。毕竟,它们指的是同⼀辆车。
我们可以通过SQL查询语句来展开Vehicle与ServiceHistory两张表中包含的隐式关系:
SELECTVehicle.Model,Vehicle.YearFROMVehicle,ServiceHistoryWHEREVehicle.VIN=ServiceHistory.VINANDServiceHistory.Pric
该查询旨在查维修费⽤⼤于$75.00的所有车辆的Model和Year.注意到我们是通过匹配Vehicle与ServiceHistory表中的VIN值来筛选满
⾜条件的记录.返回的将是两张表中符合条件的⼀些记录,⽽“Vehicle.Model”与“Vehicle.Year”,表⽰我们只想要Vehicle表中的这两列.
如果我们的数据库没有索引(indexes)(正确的应该是indices),上⾯的查询就需要执⾏表扫描(tablescan)来定位匹配查询要求的⾏。
tablescan是按照顺序对表中的每⼀⾏进⾏依次检查,⽽这通常会⾮常的慢。实际上,tablescan实际上是所有查询中最慢的。mysqlschema作用
可以通过对列加索引来避免扫描表。我们可以把索引看做⼀种数据结构,它能够通过预排序让我们在被索引的列上快速地到⼀个指定的值(或指定范围内的⼀些值).也就是说,如果我们在Price列上有⼀个索引,那么就不需要⼀⾏⼀⾏地对整个表进⾏扫描来判断其价格是否⼤于75.00,⽽是只需要使⽤包含在索引中的信息“跳”到第⼀个价格⾼于75.00的那⼀⾏,并返回随后的每⼀⾏(由于索引是有序的,因此这些
⾏的价格⾄少是75.00)。
当应对⼤量的数据时,索引是提⾼查询速度不可或缺的⼀个⼯具。当然,跟所有的事情⼀样,有得必有失,使⽤索引会导致⼀些额外的消
耗:索引的数据结构会消耗内存,⽽这些内存本可⽤于数据库中存储数据。这就需要我们权衡其利弊,寻求⼀个折中的办法,但是为经常查
询的列加索引是⾮常常见的做法。
TheClearBox
得益于数据库能够检查⼀张表的schema(描述了每列包含了什么类型的数据),像索引这样的⾼级特性才能够实现,并且能够基于数据做出⼀
winform用户控件个合理的决策。也就是说,对于⼀个数据库⽽⾔,⼀张表其实是⼀个“⿊盒”(或者说透明的盒⼦)的反义词?
当我们谈到NoSQL数据库的时候要牢牢记住这⼀点。当涉及query不同类型数据库引擎的能⼒时,这也是其中⾮常重要的⼀部分。
Schemas
我们已经知道,⼀张表的schema,描述了列的名字及其所包含数据的类型。它还包括了其他⼀些信息,⽐如哪些列可以为空,哪些列不允许
有重复值,以及其他对表中列的所有限制信息。在任意时刻⼀张表只能有⼀个schema,并且表中的所有⾏必须遵守schema的规定。
这是⼀个⾮常重要的约束条件。假设你有⼀张数据库的表,⾥⾯有数以百万计的消费者信息。你的销售团队想要添加额外的⼀些信息(⽐
如,⽤户的年龄),以期提⾼他们邮件营销算法的准确度。这就需要来alter(更改)现有的表—添加新的⼀列。我们还需要决定是否表中的每⼀
ascii码字符转换
⾏都要求该列必须有⼀个值。通常情况下,让⼀个列有值是⼗分有道理的,但是这么做的话可能会需要⼀些我们⽆法轻易获得的信息(⽐如
数据库中每个⽤户的年龄)。因此在这个层⾯上,也需要有些权衡之策。
此外,对⼀个⼤型数据库做⼀些改变通常并不是⼀件⼩事。为了以防出现错误,有⼀个回滚⽅案⾮常重要。但即使是如此,⼀旦当schema
做出改变后,我们也并不总是能够撤销这些变动。schema的维护可能是DBA⼯作中最困难的部分之⼀。
Key/ValueStores
在“NoSQL”这个词存在前,像memcached这样的键/值数据存储(Key/ValueDataStores)⽆须tableschema也可提供数据存储的功
能。实际上,在K/V存储时,根本没有“表(table)”的概念。只有键(keys)与值(values).如果键值存储听起来⽐较熟悉的话,那可能是因为这个概念的构建原则与Python的dict与set相⼀致:使⽤hashtable(哈希表)来提供基于键的快速数据查询。⼀个基于Python的最原始的NoSQL数据库,简单来说就是⼀个⼤的字典(dictionary).
为了理解它的⼯作原理,亲⾃动⼿写⼀个吧!⾸先来看⼀下⼀些简单的设计想法:
⼀个Python的dict作为主要的数据存储
仅⽀持string类型作为键(key)
⽀持存储integer,string和list
⼀个使⽤ASCLLstring的简单TCP/IP服务器⽤来传递消息
⼀些像INCREMENT,DELETE,APPEND和STATS这样的⾼级命令(command)
有⼀个基于ASCII的TCP/IP接⼝的数据存储有⼀个好处,那就是我们使⽤简单的telnet程序即可与服务器进⾏交互,并不需要特殊的客户端(尽管这是⼀个⾮常好的练习并且只需要15⾏代码即可完成)。
对于我们发送到服务器及其它的返回信息,我们需要⼀个“有线格式”。下⾯是⼀个简单的说明:
CommandsSupported
PUT
参数:Key,Value
⽬的:向数据库中插⼊⼀条新的条⽬(entry)
GET
参数:Key
⽬的:从数据库中检索⼀个已存储的值
PUTLIST
参数:Key,Value
⽬的:向数据库中插⼊⼀个新的列表条⽬
APPEND
参数:Key,Value
⽬的:向数据库中⼀个已有的列表添加⼀个新的元素
INCREMENT
参数:key
⽬的:增长数据库的中⼀个整型值
DELETE
参数:Key
⽬的:从数据库中删除⼀个条⽬
STATS
参数:⽆(N/A)
⽬的:请求每个执⾏命令的成功/失败的统计信息
现在我们来定义消息的⾃⾝结构。
MessageStructure
RequestMessages
⼀条请求消息(RequestMessage)包含了⼀个命令(command),⼀个键(key),⼀个值(value),⼀个值的类型(type).后三个取决于消息类型,是可选项,⾮必须。;被⽤作是分隔符。即使并没有包含上述可选项,但是在消息中仍然必须有三个;字符。
COMMAND;[KEY];[VALUE];[VALUETYPE]
COMMAND是上⾯列表中的命令之⼀
KEY是⼀个可以⽤作数据库key的string(可选)
VALUE是数据库中的⼀个integer,list或string(可选)
list可以被表⽰为⼀个⽤逗号分隔的⼀串string,⽐如说,“red,green,blue”
VALUETYPE描述了VALUE应该被解释为什么类型
可能的类型值有:INT,STRING,LIST
Examples
"PUT;foo;1;INT"
"GET;foo;;"
"PUTLIST;bar;a,b,c;LIST"
"APPEND;bar;d;STRING"
"GETLIST;bar;;"
STATS;;;
INCREMENT;foo;;
DELETE;foo;;
ReponseMessages
⼀个响应消息(ReponseMessage)包含了两个部分,通过;进⾏分隔。第⼀个部分总是True|False,它取决于所执⾏的命令是否成功。第⼆个部分是命令消息(commandmessage),当出现错误时,便会显⽰错误信息。对于那些执⾏成功的命令,如果我们不想要默认的返回值(⽐如PUT),就会出现成功的信息。如果我们返回成功命令的值(⽐如GET),那么第⼆个部分就会是⾃⾝值。
Examples
True;Key[foo]setto[1]
True;1
True;Key[bar]setto[['a','b','c']]
True;Key[bar]hadvalue[d]appended
True;['a','b','c','d']
True;{'PUTLIST':{'success':1,'error':0},'STATS':{'success':0,'error':0},'INCREMENT':{'success':0,'error':0},'GET':
{'success':0,'error':0},'PUT':{'success':0,'error':0},'GETLIST':{'success':1,'error':0},'APPEND':{'success':1,'error':0},'DELETE':
{'success':0,'error':0}}
ShowMeTheCode!
我将会以块状摘要的形式来展⽰全部代码。整个代码不过180⾏,读起来也不会花费很长时间。
SetUp
下⾯是我们服务器所需的⼀些样板代码:
"""NoSQLdatabasewritteninPython"""
#Standardlibraryimports
importsocket
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},
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论