CategoryResourceRepost/极客时间专栏/SQL必知必会/第三章:认识DBMS/44丨DBMS篇总结和答疑:用SQLite做词云.md
louzefeng d3828a7aee mod
2024-07-11 05:50:32 +00:00

208 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<audio id="audio" title="44丨DBMS篇总结和答疑用SQLite做词云" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/18/02/1860a27760780ddeceee9635fdc4d002.mp3"></audio>
在认识DBMS篇中我们讲解了Excel+SQL、WebSQL、SQLite以及Redis的使用这些DBMS有自己适用的领域我们可以根据需求选择适合的DBMS。我总结了一些大家常见的问题希望能对你有所帮助。
## 关于Excel+SQL
### 答疑1关于mysql-for-excel的安装
Excel是我们常用的办公软件使用SQL做数据分析的同学也可以使用Excel+SQL作为报表工具通过它们提取一些指定条件的数据形成数据透视表或者数据透视图。
但是有同学在安装mysql-for-excel-1.3.8.msi 时报错,这里感谢**同学莫弹弹**给出了解答。解决这个问题的办法是在安装时需要Visual Studio 2010 Tools for Office Runtime 才能运行。
它的下载链接在这里: [https://www.microsoft.com/zh-CN/download/confirmation.aspx?id=56961](https://www.microsoft.com/zh-CN/download/confirmation.aspx?id=56961)
## 关于WebSQL
我在讲解WebSQL操作本地存储时可以使用浏览器中的Clear Storage功能。有同学问到这里只能用户手动删除才可以吗
事实上除了在浏览器里手动删除以外我们完全可以通过程序来控制本地的SQLite。
使用executeSql函数即可在executeSql函数后面有两个function分别代表成功之后的调用以及执行失败的调用。比如想要删除本地SQLite的heros数据表可以写成下面这样
```
tx.executeSql(&quot;DROP TABLE heros&quot;,[],
function(tx, result) {alert('Drop 成功');},
function(tx, error) {alert('Drop 失败' + error.message);});
```
第二个问题是Session是什么概念呢HTTP请求不是无状态的吗
我在文章中讲到过SessionStorage这里的Session指的就是一个会话周期的数据当我们关闭浏览器窗口的时候SessionStorage存储的数据就会被清空。相比之下localStorage存储的时间没有限制一年之后数据依然可以存在。
HTTP本身是一个无状态的连接协议想要保持客户端与服务器之间的交互可以使用两种交互存储方式即Cookie和Session。
Cookie是通过客户端保存的数据也就是可以保存服务器发送给客户端的信息存储在浏览器中。一般来说在服务器上也存在一个Session这个是通过服务器来存储的状态信息这时会将浏览器与服务器之间的一系列交互称为一个Session。这种情况下Session会存储在服务器端。
不过我们讲解的sessionStorage是本地存储的解决方式它存放在浏览器里借用了session会话的概念它指的是在本地存储过程中的一种临时存储方案数据只有在同一个session会话中的页面才能访问而且当session结束后数据也会释放掉。
## 关于SQLite
第一个问题关于SQLite查找微信本地的聊天记录有同学说可以导出聊天记录做个词云。
这是个不错的idea我们既然有了SQLite完全可以动手做个数据分析做个词云展示。
我在《数据分析45讲》里讲到过词云的制作方法这里使用Python+SQLite查询将微信的聊天记录做个词云具体代码如下
```
import sqlite3
from wordcloud import WordCloud
import matplotlib.pyplot as plt
import jieba
import os
import re
# 去掉停用词
def remove_stop_words(f):
stop_words = ['你好', '已添加', '现在', '可以', '开始', '聊天', '当前', '群聊', '人数', '过多', '显示', '群成员', '昵称', '信息页', '关闭', '参与人', '还有', '嗯']
for stop_word in stop_words:
f = f.replace(stop_word, '')
return f
# 生成词云
def create_word_cloud(f):
print('根据微信聊天记录,生成词云!')
# 设置本地的simhei字体文件位置
FONT_PATH = os.environ.get(&quot;FONT_PATH&quot;, os.path.join(os.path.dirname(__file__), &quot;simhei.ttf&quot;))
f = remove_stop_words(f)
cut_text = &quot; &quot;.join(jieba.cut(f,cut_all=False, HMM=True))
wc = WordCloud(
font_path=FONT_PATH,
max_words=100,
width=2000,
height=1200,
)
wordcloud = wc.generate(cut_text)
# 写词云图片
wordcloud.to_file(&quot;wordcloud.jpg&quot;)
# 显示词云文件
plt.imshow(wordcloud)
plt.axis(&quot;off&quot;)
plt.show()
def get_content_from_weixin():
# 创建数据库连接
conn = sqlite3.connect(&quot;weixin.db&quot;)
# 获取游标
cur = conn.cursor()
# 创建数据表
# 查询当前数据库中的所有数据表
sql = &quot;SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE 'Chat\_%' escape '\\\'&quot;
cur.execute(sql)
tables = cur.fetchall()
content = ''
for table in tables:
sql = &quot;SELECT Message FROM &quot; + table[0]
print(sql)
cur.execute(sql)
temp_result = cur.fetchall()
for temp in temp_result:
content = content + str(temp)
# 提交事务
conn.commit()
# 关闭游标
cur.close()
# 关闭数据库连接
conn.close()
return content
content = get_content_from_weixin()
# 去掉HTML标签里的内容
pattern = re.compile(r'&lt;[^&gt;]+&gt;',re.S)
content = pattern.sub('', content)
# 将聊天记录生成词云
create_word_cloud(content)
```
运行结果:
<img src="https://static001.geekbang.org/resource/image/c0/6b/c01ef48d13e80b5742248b9cf58cfb6b.png" alt=""><br>
你在[Github](https://github.com/cystanford/SQLite)上也可以找到相应的代码,这个结果图是我运行自己的微信聊天记录得出的。
我来讲解下代码中相关模块的作用。
首先是`create_word_cloud`函数通过聊天内容f展示出词云。这里会用到WordCloud类通过它配置本地的simhei字体因为需要显示中文设置显示的最大词数`max_words=100`图片的尺寸width和height。
第二个是`remove_stop_words`函数,用来设置停用词,也就是不需要统计的单词,这里我设置了一些,不过从结果中,你能看到我们需要更多的停用词,要不会统计出一些没有意义的词汇,比如“撤回”“一条”等。
第三个是`get_content_from_weixin`函数。这里我们通过访问SQLite来访问微信聊天记录首先需要查询数据表都有哪些在微信的本地存储里每个数据表对应着一个聊天对象然后我们对这些数据表中的message字段进行提取。
最后因为统计出来的聊天记录会包括大量的HTML标签这里我们还需要采用正则表达式匹配的方式将content中的HTML标签去掉然后调用`create_word_cloud`函数生成词云,结果就是文稿中的图片所示啦。
第二个问题是Navicat如何导入`weixin.db`呢?
事实上使用Navicat导入`weixin.db`非常简单。首先我们需要创建SQLite连接然后从本地选择数据库文件这里选中`weixin.db`。
然后就导入到Navicat中了你在左侧可以看到weixin的连接然后打开main数据库就可以看到聊天记录的数据表了。
我制作了演示视频,可以看下。
<video preload="none" controls=""><source src="https://media001.geekbang.org/customerTrans/fe4a99b62946f2c31c2095c167b26f9c/5366112e-16d490a2068-0000-0000-01d-dbacd.mp4" type="video/mp4"><source src="https://media001.geekbang.org/0b5bd0228e9149cdb6965ef48f35b681/145dd2dd317949798ff8d62a9cafa6a6-a5a4cad5b88948f5722740a7b6a03596-sd.m3u8" type="application/x-mpegURL"><source src="https://media001.geekbang.org/0b5bd0228e9149cdb6965ef48f35b681/145dd2dd317949798ff8d62a9cafa6a6-560f782e2a1ba0c7731de809a5248c73-hd.m3u8" type="application/x-mpegURL"></video>
## 关于Redis
第一个问题MongoDB、Redis之间有什么区别实际使用时应该怎么选择呢
Redis是Key-Value数据库数据存放在内存中查询和写入都是在内存中进行操作。当然Redis也支持持久化但持久化只是Redis的功能之一并不是Redis的强项。通常你可以把Redis称之为缓存它支持的数据类型丰富包括字符串、哈希、列表、集合、有序集合同时还支持基数统计、地理空间以及索引半径查询、数据流等。
MongoDB面向文档数据库功能强大是非关系型数据库中最像RDBMS的处理增删改查也可以增加条件。
在存储方式上Redis将数据放在内存中通过RDB或者AOF方式进行持久化。而MongoDB实际上是将数据存放在磁盘上的只是通过mmap调用将数据映射到内存中你可以将mmap理解为加速的方式。mmap调用可以使得对普通文件的操作像是在内存中进行读写一样这是因为它将文件映射到调用进程的地址空间中实现了文件所在的磁盘物理地址与进程空间的虚拟地址一一映射的关系这样就可以直接在内存中进行操作然后写完之后同步一下就可以存放到文件中效率非常高。
不过在使用选择的时候,我们还是将 MongoDB 归为数据库而将Redis归为缓存。
总的来说Redis就像一架飞机查询以及写入性能极佳但是存储的数据规模有限。MongoDB就像高铁在处理货物数据的功能上强于Redis同时能承载的数据量远高于Redis但是查询及写入的效率不及Redis。
第三个问题是我们能否用Redis中的DECR实现多用户抢票问题
当然是可以的在专栏文章中我使用了WATCH+MULTI的乐观锁方式主要是讲解这种乐观锁的实现方式。我们也可以使用Redis中的DECR命令对相应的KEY值进行减1操作是原子性的然后我们判断下DECR之后的数值即可当减1之后大于等于0证明抢票成功否则小于0则说明抢票失败。
这里我给出了相应的代码,你也可以在[Github](https://github.com/cystanford/Redis)上下载。
```
# 抢票模拟使用DECR原子操作
import redis
import threading
# 创建连接池
pool = redis.ConnectionPool(host = '127.0.0.1', port=6379, db=0)
# 初始化 redis
r = redis.StrictRedis(connection_pool = pool)
# 设置KEY
KEY=&quot;ticket_count&quot;
# 模拟第i个用户进行抢购
def sell(i):
# 使用decr对KEY减1
temp = r.decr(KEY)
if temp &gt;= 0:
print('用户 {} 抢票成功,当前票数 {}'.format(i, temp))
else:
print('用户 {} 抢票失败,票卖完了'.format(i))
if __name__ == &quot;__main__&quot;:
# 初始化5张票
r.set(KEY, 5)
# 设置8个人抢票
for i in range(8):
t = threading.Thread(target=sell, args=(i,))
t.start()
```
最后有些同学感觉用Redis最终还是需要结合程序以及MySQL来处理因为排行榜展示在前端还是需要用户名的光给个用户id不知道是谁除非Redis有序集合的member包含了用户id和name。
这里排行榜中如果要显示用户名称需要放到有序集合中这样就不需要再通过MySQL查询一次。这种需要实时排名计算的通过Redis解决更适合。如果是排行榜生成之后用户想看某一个用户具体的信息比如地区、战绩、使用英雄情况等可以通过MySQL来进行查询。而对于热点数据使用Redis进行缓存可以解决高并发情况下的数据库读压力。
所以你能看到Redis通常可以作为MySQL的缓存它存储的数据量有限适合存储热点数据可以解决读写效率要求很高的请求。而MySQL则作为数据库提供持久化功能并通过主从架构提高数据库服务的高可用性。
最后留两个思考题。
我在文稿中使用SQLite对于微信聊天记录进行查询使用wordcloud词云工具对聊天记录进行词云展示。同时我将聊天记录文本保存下来一共4.82M不包括HTML标签内容你可以使用SQLite读取微信聊天记录然后看下纯文本大小有多少
第二个问题是我们使用Redis作为MySQL的缓存假设MySQL存储了1000万的数据Redis只保存有限的数据比如10万数据量如何保证Redis存储的数据都是热点数据呢
欢迎你在评论区写下你的思考,也欢迎把这篇文章分享给你的朋友或者同事,一起交流一下。