企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## NoSQL简单介绍    NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库,随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。 ## NoSQL数据库的四大分类 | 分类 | Examples举例 | 典型应用场景 | 数据模型 | 优点 | 缺点 | | --- | --- | --- | --- | --- | --- | | 键值(key-value)\[3\]  | Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB | 内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。\[3\]  | Key 指向 Value 的键值对,通常用hash table来实现\[3\]  | 查找速度快 | 数据无结构化,通常只被当作字符串或者二进制数据\[3\]  | | 列存储数据库\[3\]  | Cassandra, HBase, Riak | 分布式的文件系统 | 以列簇式存储,将同一列数据存在一起 | 查找速度快,可扩展性强,更容易进行分布式扩展 | 功能相对局限 | | 文档型数据库\[3\]  | CouchDB, MongoDb | Web应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容) | Key-Value对应的键值对,Value为结构化数据 | 数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构 | 查询性能不高,而且缺乏统一的查询语法。 | | 图形(Graph)数据库\[3\]  | Neo4J, InfoGrid, Infinite Graph | 社交网络,推荐系统等。专注于构建关系图谱 | 图结构 | 利用图结构相关算法。比如最短路径寻址,N度关系查找等 | 很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群方案。 | ## Redis简单介绍   redis是业界主流的key-value nosql 数据库之一。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(列表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。 ### Redis优点 * 异常快速 : Redis是非常快的,每秒可以执行大约110000设置操作,81000个/每秒的读取操作。 * 支持丰富的数据类型 : Redis支持最大多数开发人员已经知道如列表,集合,可排序集合,哈希等数据类型。 这使得在应用中很容易解决的各种问题,因为我们知道哪些问题处理使用哪种数据类型更好解决。 * 操作都是原子的 : 所有 Redis 的操作都是原子,从而确保当两个客户同时访问 Redis 服务器得到的是更新后的值(最新值)。 * MultiUtility工具:Redis是一个多功能实用工具,可以在很多如:缓存,消息传递队列中使用(Redis原生支持发布/订阅),在应用程序中,如:Web应用程序会话,网站页面点击数等任何短暂的数据。 ## Python操作Redis ### 1、操作模式   redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令,Redis是StrictRedis的子类,用于向后兼容旧版本的redis-py。 ~~~ import redis r = redis.Redis(host='localhost',port=6379) r.set('name','wusir') print(r.get('name')) ~~~ ### 2、连接池   redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池。 ~~~ import redis pool = redis.ConnectionPool(host='localhost',port=6379) r = redis.Redis(connection_pool=pool) r.set('name2','alex') print(r.get('name2')) ~~~ ## Redis操作 ### windows上面安装服务 ~~~ 安装:redis-server --service-install redis.windows.conf 卸载:redis-server --service-uninstall 启动:redis-server --service-start 停止:redis-server --service-stop ~~~ ### 1、String操作   redis中的String在在内存中按照一个name对应一个value来存储。 ![](https://img.kancloud.cn/f9/be/f9be4b90c0922170a45e736ec3894a88_448x369.png) set(name, value) ~~~ 在Redis中设置值,默认,不存在则创建,存在则修改 ~~~ expire key seconds ~~~ 给key设置过期时间 ~~~ setnx(name, value) ~~~ 设置值,只有name不存在时,执行设置操作(添加) ~~~ setex(name, time, value) ~~~ # 设置值 # 参数: # time,过期时间(数字秒 或 timedelta对象) ~~~ psetex(name, time\_ms, value) ~~~ # 设置值 # 参数: # time_ms,过期时间(数字毫秒 或 timedelta对象) ~~~ mset(\*args, \*\*kwargs) ~~~ 批量设置值 如: mset(k1='v1', k2='v2') 或 mget({'k1': 'v1', 'k2': 'v2'}) ~~~ get(name) ~~~ 获取值 ~~~ mget(keys, \*args) ~~~ 批量获取 ~~~ getset(name, value) ~~~ 设置新值并获取原来的值 ~~~ getrange(key, start, end) ~~~ # 获取子序列(根据字节获取,非字符) # 参数: # name,Redis 的 name # start,起始位置(字节) # end,结束位置(字节) # 如: "wusir" ,0-3表示 "wusi" ~~~ setrange(name, offset, value) ~~~ # 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加) # 参数: # offset,字符串的索引,字节(一个汉字三个字节) # value,要设置的值 ~~~ setbit(name, offset, value) ~~~ # 对name对应值的二进制表示的位进行操作 # 参数: # name,redis的name # offset,位的索引(将值变换成二进制后再进行索引) # value,值只能是 1 或 0 # 注:如果在Redis中有一个对应: n1 = "foo", 那么字符串foo的二进制表示为:01100110 01101111 01101111 所以,如果执行 setbit('n1', 7, 1),则就会将第7位设置为1, 那么最终二进制则变成 01100111 01101111 01101111,即:"goo" 特别的,如果source是汉字,对于utf-8,每一个汉字占 3 个字节,那么 "博客园" 则有 9个字节 对于汉字,for循环时候会按照 字节 迭代,那么在迭代时,将每一个字节转换 十进制数,然后再将十进制数转换成二进制 ~~~ getbit(name, offset) ~~~ # 获取name对应的值的二进制表示中的某位的值 (0或1) ~~~ bitcount(key, start=None, end=None) ~~~ # 获取name对应的值的二进制表示中 1 的个数 # 参数: # key,Redis的name # start,位起始位置 # end,位结束位置 ~~~ strlen(name) ~~~ # 返回name对应值的字节长度(一个汉字3个字节) ~~~ append(key, value) ~~~ # 在redis name对应的值后面追加内容 # 参数: key, redis的name value, 要追加的字符串 ~~~ ### 2、Hash操作 hash表现形式上有些像pyhton中的dict,可以存储一组关联性较强的数据 , redis中Hash在内存中的存储格式如下图:  ![](https://img.kancloud.cn/a3/d9/a3d9bc5af9c1fc8cb62bc5f5e5b7d94a_395x252.png) hset(name, key, value) ~~~ # name对应的hash中设置一个键值对(不存在,则创建;否则,修改) # 参数: # name,redis的name # key,name对应的hash中的key # value,name对应的hash中的value # 注: # hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加) ~~~ hmset(name, mapping) ~~~ # 在name对应的hash中批量设置键值对 # 参数: # name,redis的name # mapping,字典,如:{'k1':'v1', 'k2': 'v2'} # 如: # r.hmset('xx', {'k1':'v1', 'k2': 'v2'}) ~~~ hget(name,key) ~~~ # 在name对应的hash中获取根据key获取value ~~~ hmget(name, keys, \*args) ~~~ # 在name对应的hash中获取多个key的值 # 参数: # name,reids对应的name # keys,要获取key集合,如:['k1', 'k2', 'k3'] # *args,要获取的key,如:k1,k2,k3 # 如: # r.mget('xx', ['k1', 'k2']) # 或 # print r.hmget('xx', 'k1', 'k2') ~~~ hgetall(name) ~~~ 获取name对应hash的所有键值 ~~~ hlen(name) ~~~ # 获取name对应的hash中键值对的个数 ~~~ hkeys(name) ~~~ # 获取name对应的hash中所有的key的值 ~~~ hvals(name) ~~~ # 获取name对应的hash中所有的value的值 ~~~ hdel(name,\*keys) ~~~ # 将name对应的hash中指定key的键值对删除 ~~~ ### 3、list操作 List操作,redis中的List在在内存中按照一个name对应一个List来存储。如图: ![](https://img.kancloud.cn/26/23/26236518918cb750fbf79668b85bbaeb_443x262.png) lpush(name,values) ~~~ # 在name对应的list中添加元素,每个新的元素都添加到列表的最左边 # 如: # r.lpush('oo', 11,22,33) # 保存顺序为: 33,22,11 # 扩展: # rpush(name, values) 表示从右向左操作 ~~~ llen(name) ~~~ # name对应的list元素的个数 ~~~ linsert(name, where, refvalue, value)) ~~~ # 在name对应的列表的某一个值前或后插入一个新值 # 参数: # name,redis的name # where,BEFORE或AFTER # refvalue,标杆值,即:在它前后插入数据 # value,要插入的数据 ~~~ r.lset(name, index, value) ~~~ # 对name对应的list中的某一个索引位置重新赋值 # 参数: # name,redis的name # index,list的索引位置 # value,要设置的值 ~~~ r.lrem(name, value, num) ~~~ # 在name对应的list中删除指定的值 # 参数: # name,redis的name # value,要删除的值 # num, num=0,删除列表中所有的指定值; # num=2,从前到后,删除2个; # num=-2,从后向前,删除2个 ~~~ lpop(name) ~~~ # 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素 # 更多: # rpop(name) 表示从右向左操作 ~~~ lindex(name, index) ~~~ 在name对应的列表中根据索引获取列表元素 ~~~ lrange(name, start, end) ~~~ # 在name对应的列表分片获取数据 # 参数: # name,redis的name # start,索引的起始位置 # end,索引结束位置 ~~~ ltrim(name, start, end) ~~~ # 在name对应的列表中移除没有在start-end索引之间的值 # 参数: # name,redis的name # start,索引的起始位置 # end,索引结束位置 ~~~ ### 4、set集合操作 Set操作,Set集合就是不允许重复的列表 sadd(name,values) ~~~ # name对应的集合中添加元素 ~~~ scard(name) ~~~ 获取name对应的集合中元素个数 ~~~ sdiff(keys, \*args) ~~~ 在第一个name对应的集合中且不在其他name对应的集合的元素集合 ~~~ sinter(keys, \*args) ~~~ # 获取多一个name对应集合的交集 ~~~  sunion(keys, \*args) ~~~ # 获取多一个name对应的集合的并集 ~~~   有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。 zadd(name, \*args, \*\*kwargs) ~~~ # 在name对应的有序集合中添加元素 # 如: # zadd('zz', 'n1', 1, 'n2', 2) # 或 # zadd('zz', n1=11, n2=22) ~~~ zcard(name) ~~~ # 获取name对应的有序集合元素的数量 ~~~ zcount(name, min, max) ~~~ # 获取name对应的有序集合中分在 [min,max] 之间的个数 ~~~ r.zrange( name, start, end, desc=False, withscores=False, score\_cast\_func=float) ~~~ # 按照索引范围获取name对应的有序集合的元素 # 参数: # name,redis的name # start,有序集合索引起始位置(非分数) # end,有序集合索引结束位置(非分数) # desc,排序规则,默认按照分数从小到大排序 # withscores,是否获取元素的分数,默认只获取元素的值 # score_cast_func,对分数进行数据转换的函数 # 更多: # 从大到小排序 # zrevrange(name, start, end, withscores=False, score_cast_func=float) # 按照分数范围获取name对应的有序集合的元素 # zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float) # 从大到小排序 # zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float) ~~~ zrank(name, value) ~~~ # 获取某个值在 name对应的有序集合中的排行(从 0 开始) # 更多: # zrevrank(name, value),从大到小排序 ~~~ **其他常用操作** del (\*names) ~~~ # 根据删除redis中的任意数据类型 ~~~ exists(name) ~~~ # 检测redis的name是否存在 ~~~ keys(pattern='\*') ~~~ # 根据模型获取redis的name # 更多: # KEYS * 匹配数据库中所有 key 。 # KEYS h?llo 匹配 hello , hallo 和 hxllo 等。 # KEYS h*llo 匹配 hllo 和 heeeeello 等。 # KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo ~~~ rename(src, dst) ~~~ # 对redis的name重命名为 ~~~ type(name) ~~~ # 获取name对应值的类型 ~~~ keys \* ~~~ 查看当前db下所有的key ~~~ select num    注:num为0-15的数字 ~~~ 切换到指定的数据库 ~~~ help 指令 ~~~ 查看该指令的帮助 ~~~