上一篇文章跟大家介绍了一下diskcache的基础用法,本次推文带大家了解一下关于diskcache更深入的东西。
diskcache缓存对象管理是基于SQLite数据库,它是一个轻量级的基于磁盘的数据库,该数据库不需要单独的服务器进程,并允许使用SQL查询。大家如果注意到,上篇推文中的源码截图上有一些sql的语句。
diskcache可使用diskcache.FanoutCache 自动分片基础数据库。分片是对数据进行水平分区。可用于减少阻塞写入。尽管读和写不会互相阻碍,但写入会阻碍其他写入。分片默认值为8。
代码如下:
from diskcache import FanoutCache
cache=FanoutCache(r"D:\python\cachedb\diskcahce_2",shards=4,timeout=1)
cache.set("python","python知识学堂欢迎你!")
print(cache.get("python"))
diskcache_2文件件情况,如下:
上面的示例在一个diskcache_2文件夹中创建一个具有四个分片和一秒超时的缓存。如果操作花费的时间超过一秒,它们将尝试中止操作。
那么每一个分片的大小是多少呢?分片的大小都是平均分配的,占总空间的四分之一。diskcache的默认大小为1GB。
diakcache提供了一个collections.deque兼容的双端队列diskcache.Deque。双端队列是堆栈和队列的一般化,可以在前后都可以进行快速访问和编辑,可持久化,比较便捷。
如下示例:
from diskcache import Deque
deque=Deque(range(5,10))
print(str(deque.popleft())+"/"+str(deque.pop()))
deque.appendleft(4)
deque.append(10)
deque.extendleft([1,2,3])
deque.extend([11,12,13])
print(str(deque.popleft())+"/"+str(deque.pop()))
运行结果:
Popleft 获取队列最前面的一个元素,pop获取末尾的一个元素;
Appendleft 在队列最开始添加一个元素,append 在末尾添加一个元素;
Extendleft 在队列最开始添加一个数组,extend在末尾添加一个数组;
那么deque的存储的位置在哪里?可以使用下面的命令查看队列存储的位置:
deque.directory
包括cache也是一个可以使用directory属性查看默认存储的位置;
那么如何指定directory呢?Deque的第二个参数指定存储位置,第一个参数为队列的初始值。或者可以直接指定参数名称,如下:
deque=Deque([],r"D:\python\cachedb\diskcahce_3")
deque=Deque(directory=r"D:\python\cachedb\diskcahce_3")
Index 类似于dict(字典),可持久化,使用也比较便捷。如下示例:
from diskcache import Index
index=Index(r"D:\python\cachedb\diskcahce_5")
# index["python"]="python知识学堂欢迎你!"
# index["python1"]="python知识学堂非常欢迎你!"
# index["python2"]="python知识学堂非常非常欢迎你!"
print(index.popitem())
popitem表示获取最后一个键对值,并且删除,结果如下:
peekitem() 可传递last 参数是否获取最后一个,不会删除原始值;
setdefault(key,default) 可以给指定的key值设置默认值,在查找时可以预先设置一个默认值,防止key值不存在而抛出异常。
keys()与values()可以查找所有Index中的key值与value值,然而没有提供判断key值是否存在的方法,但是可以使用setdefault的方法自行封装。
还记得上篇文章中提到的Lock锁么?先看一下源码:
可以看到这里的锁用的就是cache的add方法的特性,源码中也给出了使用的方式:
from diskcache import Lock
cache=Cache(r"D:\python\cachedb\diskcahce_1")
lock=Lock(cache,"lock_key")
#锁开始
lock.acquire()
#业务处理 TODO
#锁释放
lock.release()
从源码上看有一个while 死循环,直到add成功时才会被释放,这里处理的方式不是很好,可能会造成死锁,所以一般情况下,都会添加一个过期的时间,如下:
lock=Lock(cache,"lock_key",expire=5)
还有一种RLock锁,与Lock锁的区别是RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。如下:
cache=Cache(r"D:\python\cachedb\diskcahce_1")
rLock=RLock(cache,"rlock_key",expire=5)
rLock.acquire()
rLock.acquire()
rLock.release()
rLock.release()
print("执行成功")
结果是执行成功。
看一下源码:
从源码中可以看出,判断锁的条件是os.getpid()(进程pid)与
threading.get_ident()(线程标识符),如果每次acquire时的pid与ident都相同的时,即可成功。那么就可以在相同的进程中无限次数的acquire,但是多少次acquire就得多少次的release,防止死锁。
那么是使用Lock还是RLock呢?这个要具体的看实际情况,并不是谁就一定好。
本次推文中介绍了diskcache中FanoutCache 缓存分片、双端队列dedue、Index、Lock以及RLock。
大家可以在实际中灵活运用,diskcache缓存的优势还是很大的,无需安装其他的模块,并且在文件管理器中能直接查看,还可以利用缓存的一些特性使用多线程的去实现业务等。
但是也是有缺点的,即受制于本地文件系统的限制。每个磁盘每个目录下的文件数量是有限制的,所以需要结合实际情况使用。