存储方案
5.1 高并发服务器的存储方案
高并发服务器,分为两大类:第一大类是长连接类型的,也就是类似保持Socket Keep Alive类型的连接;第二大类是短连接类型;
5.1.1 网站高并发服务器的策略
- 数据库主从复制
- 数据库的读写分离
- 数据库反向代理
- 垂直分割
- 水平分割
5.1.2 数据库的锁
数据库锁有悲观锁和乐观锁。
悲观锁,指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁具有排他的性质,也就是锁住当前数据后,别人就看不到被锁住的数据了。
悲观锁的实现,依靠数据库提供的锁机制。Hibernate的悲观锁,也是基于数据库的锁机制实现。
Hibernate的加锁模式有:
-
LockMode.NONE:无锁机制。
-
LockMode.WRITE:Hibernate在Insert和Update记录的时候会自动获取。
-
LockMode.READ:Hibernate在读取记录的时候会自动获取。
在应用层较为常用的锁机制:
-
LockMode.UPGRADE:利用数据库的for update子句加锁。
-
LockMode.UPGRADE_NOWAIT:Oracle的特定实现,利用Oracle的forupdate nowait子句实现加锁。
加锁一般通过几种方法实现:
-
Criteria.setLockMode
-
Query.setLockMode
-
Session.lock
只有在查询开始之前设定加锁,才会真正通过数据库的锁机制进行加锁处理。
乐观锁机制采取了更加宽松的加锁机制,在一定程度上解决了并发的问题。乐观锁大多是基于数据版本Version记录机制实现的。
如果不考虑外部系统对数据库的更新操作,可以使用Hibernate提供的透明化乐观锁来实现。
optimistc-lock属性有如下可选取值:
-
none:无乐观锁。
-
version:通过版本机制实现乐观锁(推荐)。
-
dirty:通过检查发生变动过的属性实现乐观锁。
-
all:通过检查所有属性实现乐观锁。
5.2 高速缓存
5.2.1 Memcached
Memcached的核心用途,就是高速缓存数据库的内容,给上层应用服务。
Memcached中保存的数据都存储在其内置的内存存储空间中。
向Memcached保存数据的方法有:add
,replace
,set
add = memcached.add('键', '值', '期限'); # 仅当存储空间中不存在键相同的数据时才保存
replace = memcached.replace('键', '值', '期限'); # 仅当存储空间中存在键相同的数据时才保存
set = memcached.set('键', '值', '期限'); # 无论何时都保存
向Memcached保存数据时可以指定期限(秒)。
获取数据可以使用get和get_multi方法:
val = memcached.get('键');
val = memcached.get_multi('键1', '键2', '键3', '键4', '键5');
get_multi可以非同步地同时取得多个键值,其速度比循环调用get快数十倍。
删除数据使用delete方法:
memcached.delete('键', '阻塞时间(秒)');
删除第一个参数指定的键的数据。第二个参数指定一个时间值,可以禁止使用同样的键保存新数据,可以用于防止缓存数据的不完整(注:set函数会忽视该阻塞,照常保存数据)。
增一和减一操作,可以将Memcahced上特定的键值作为计数器使用:
ret = memcached.incr('键');
在Python下,有python-memcached库可供使用:
import memcache
mc = memcache.Client(['127.0.0.1:12000'], debug=0)
mc.set("foo", "bar")
value = mc.get("foo")
5.2.2 大文件缓存
目前暂时应对的是文本文件。
linecache模块的作用是将文件内容读取到内存中进行缓存,而不是每次都要从硬盘中读取,效率提高很多,又省去了对硬盘IO控制器的频繁操作。
linecache对于读取内容非常多的文件,效果甚好,而且它还可以读取指定的行内容。
-
linecache.getline(filename, lineno[, module_globals])
:从filename中读取内容,得到第lineno行(包含换行符)。 -
linecache.clearcache()
:清除现有的文件缓存。 -
linecache.checkcache([filename])
:检查缓存内容的有效性,若不提供参数,则检查缓存中所有的项。
linecache里面最常用到的就是getline方法,可以直接从内容中读到指定的行,日常编程中若涉及到读取大文件,一定要使用首选linecache模块,相比open方法要快许多倍。
import os
import linecache
def get_content(path):
if os.path.exists(path):
content = ''
cache_data = linecache.getlines(path)
for line in range(len(cache_data)):
content += cache_data[line]
return content
else:
print ('the path [{}] is not exist!'.format(path))
def main():
path = 'h:/test.txt'
content = get_content(path)
print content
if __name__ == '__main__':
main()
5.2.3 分布式和集群
分布式的核心功能是缩短某个任务的执行时间;集群的核心功能是通过提升单位时间里面执行多少任务数量来提高效率。
分布式是将不同的任务分布在不同的地方;集群是将电脑集中在一起同时执行一个任务。
我们也应该考虑CDN和反向代理。
5.3 二进制存储方案
5.3.1 磁盘IO和缓存
磁盘IO速度慢,可以利用缓存解决这个问题。
5.3.2 图片和影音文件
使用分布式+集群解决图片和影音文件的问题。
首先自动化分割大块影音文件,服务器在提交数据的时候,将分块的文件分别缓存在分布式服务器中,当请求传送过来的时候,负载均衡服务询问缓存服务器,如果缓存服务器存在文件缓存,则将缓存服务器的内容传输给客户端。
5.4 大规模计算
5.4.1 图片服务器的架构
5.4.2 读取和写入文本
5.4.3 文本搜索方案
小结: 在大规模计算,文本读取写入的时候,一定要考虑和分析其中的瓶颈在哪里,对症下药,做出最好的判断和策略,来提高服务器的效率。