redisforpython
A. 如何高效地向Redis写入大量的数据
具体实现步骤如下:
1.
新建一个文本文件,包含redis命令
SET
Key0
Value0
SET
Key1
Value1
...
SET
KeyN
ValueN
如果有了原始数据,其实构造这个文件并不难,譬如shell,python都可以
2.
将这些命令转化成Redis
Protocol。
因为Redis管道功能支持的是Redis
Protocol,而不是直接的Redis命令。
如何转化,可参考后面的脚本。
3.
利用管道插入
cat
data.txt
|
redis-cli
--pipe
Shell
VS
Redis
pipe
下面通过测试来具体看看Shell批量导入和Redis
pipe之间的效率。
测试思路:分别通过shell脚本和Redis
pipe向数据库中插入10万相同数据,查看各自所花费的时间。
Shell
脚本如下:
#!/bin/bash
for
((i=0;i<100000;i++))
do
echo
-en
"helloworld"
|
redis-cli
-x
set
name$i
>>redis.log
done
每次插入的值都是helloworld,但键不同,name0,name1...name99999。
Redis
pipe
Redis
pipe会稍微麻烦一点
1>
首先构造redis命令的文本文件
在这里,我选用了python
#!/usr/bin/python
for
i
in
range(100000):
print
'set
name'+str(i),'helloworld'
#
python
1.py
>
redis_commands.txt
#
head
-2
redis_commands.txt
set
name0
helloworld
set
name1
helloworld
2>
将这些命令转化成Redis
Protocol
在这里,我利用了github上一个shell脚本,
#!/bin/bash
while
read
CMD;
do
#
each
command
begins
with
*{number
arguments
in
command}\r\n
XS=($CMD);
printf
"*${#XS[@]}\r\n"
#
for
each
argument,
we
append
${length}\r\n{argument}\r\n
for
X
in
$CMD;
do
printf
"\$${#X}\r\n$X\r\n";
done
done
<
redis_commands.txt
#
sh
20.sh
>
redis_data.txt
#
head
-7
redis_data.txt
*3
$3
set
$5
name0
$10
helloworld
至此,数据构造完毕。
测试结果
B. python爬虫需要安装哪些库
一、 请求库
1. requests
requests 类库是第三方库,比 Python 自带的 urllib 类库使用方便和
2. selenium
利用它执行浏览器动作,模拟操作。
3. chromedriver
安装chromedriver来驱动chrome。
4. aiohttp
aiohttp是异步请求库,抓取数据时可以提升效率。
二、 解析库
1. lxml
lxml是Python的一个解析库,支持解析HTML和XML,支持XPath的解析方式,而且解析效率非常高。
2. beautifulsoup4
Beautiful Soup可以使用它更方便的从 HTML 文档中提取数据。
3. pyquery
pyquery是一个网页解析库,采用类似jquery的语法来解析HTML文档。
三、 存储库
1. mysql
2. mongodb
3. redis
四、 爬虫框架scrapy
Scrapy 是一套异步处理框架,纯python实现的爬虫框架,用来抓取网页内容以及各种图片
需要先安装scrapy基本依赖库,比如lxml、pyOpenSSL、Twisted
C. python 用redis做什么功能
redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,
并使用官方的语法和命令,Redis是StrictRedis的子类,用于向后兼容旧版本的redis-py。
import redis 导入redis模块,通过python操作redis 也可以直接在redis主机的服务端操作缓存数据库
r = redis.Redis(host='192.168.19.130', port=6379) host是redis主机,需要redis服务端和客户端都起着 redis默认端口是6379
r.set('foo', 'Bar') key是"foo" value是"bar" 将键值对存入redis缓存
print r.get('foo') Bar 取出键foo对应的值!
D. 使用python同步mysql到redis由于数据较多,一条一条读出来写到redis太慢,有没有可以批量操作的。
MYSQL快速同步数据到Redis
举例场景:存储游戏玩家的任务数据,游戏服务器启动时将mysql中玩家的数据同步到redis中。
从MySQL中将数据导入到Redis的Hash结构中。当然,最直接的做法就是遍历MySQL数据,一条一条写入到Redis中。这样没什么错,但是速度会非常慢。如果能够想法使得MySQL的查询输出数据直接能够与Redis命令行的输入数据协议相吻合,可以节省很多消耗和缩短时间。
Mysql数据库名称为:GAME_DB, 表结构举例:
CREATE TABLE TABLE_MISSION (
playerId int(11) unsigned NOT NULL,
missionList varchar(255) NOT NULL,
PRIMARY KEY (playerId)
);
Redis中的数据结构使用哈希表:
键KEY为mission, 哈希域为mysql中对应的playerId, 哈希值为mysql中对应的missionList。 数据如下:
[root@iZ23zcsdouzZ ~]# redis-cli
127.0.0.1:6379> hget missions 36598
"{\"10001\":{\"status\":1,\"progress\":0},\"10002\":{\"status\":1,\"progress\":0},\"10003\":{\"status\":1,\"progress\":0},\"10004\":{\"status\":1,\"progress\":0}}"
快速同步方法:
新建一个后缀.sql文件:mysql2redis_mission.sql
内容如下:
SELECT CONCAT(
"*4\r\n",
'$', LENGTH(redis_cmd), '\r\n',
redis_cmd, '\r\n',
'$', LENGTH(redis_key), '\r\n',
redis_key, '\r\n',
'$', LENGTH(hkey), '\r\n',
hkey, '\r\n',
'$', LENGTH(hval), '\r\n',
hval, '\r'
)
FROM (
SELECT
'HSET' as redis_cmd,
'missions' AS redis_key,
playerId AS hkey,
missionList AS hval
FROM TABLE_MISSION
) AS t
创建shell脚本mysql2redis_mission.sh
内容:
mysql GAME_DB --skip-column-names --raw < mission.sql | redis-cli --pipe
Linux系统终端执行该shell脚本或者直接运行该系统命令,即可将mysql数据库GAME_DB的表TABLE_MISSION数据同步到redis中键missions中去。mysql2redis_mission.sql文件就是将mysql数据的输出数据格式和redis的输入数据格式协议相匹配,从而大大缩短了同步时间。
经过测试,同样一份数据通过单条取出修改数据格式同步写入到redis消耗的时间为5min, 使用上面的sql文件和shell命令,同步完数据仅耗时3s左右。
E. python redis和cache的区别
简单区别:
1. Redis中,并不是所有的数据都一直存储在内存中的,这是和Memcached相比一个最大的区别。
2. Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储。
3. Redis支持数据的备份,即master-slave模式的数据备份。
4. Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
Redis在很多方面具备数据库的特征,或者说就是一个数据库系统,而Memcached只是简单的K/V缓存
下面是来自redis作者的说法(stackoverflow上面)。
You should not care too much about performances. Redis is faster per core with small values, but memcached is able to use multiple cores with a single executable and TCP port without help from the client. Also memcached is faster with big values in the order of 100k. Redis recently improved a lot about big values (unstable branch) but still memcached is faster in this use case. The point here is: nor one or the other will likely going to be your bottleneck for the query-per-second they can deliver.
You should care about memory usage. For simple key-value pairs memcached is more memory efficient
F. python怎么安装redis
redis python-redis 安装详细步骤
安装redis
把redis安装到 /opt/redis-2.8目录中
tar -zxfx redis-2.8.1.tar.gz
cd redis-2.8.1
make && make PREFIX=/opt/redis-2.8 install
cp redis.conf /opt/redis-2.8/
只是把redis当做队列用,不需要存储,所以编辑 /opt/redis-2.8/redis.conf
设置 daemonize yes
把3条 save .. 都注释掉,这样就关闭了硬盘存储
启动redis 非常简单: /opt/redis-2.8/bin/redis-server /opt/redis-2.8/redis.conf
$REIDS_INSTALL_DIR/utils/redis_init_script 这个脚本稍做修改就可以放到/etc/init.d 作为redis启动脚本用
安装python
CentOS 自带的python2.4,太旧了,升级到2.7
tar -zvxf Python-2.7.6.tgz
cd Python-2.7.6
./configure
make && make install
替换系统默认的python: sudo ln -s /usr/local/bin/python2.7 /usr/bin/python
安装python的redis模块
wget --no-check-certificate 2.8.0.tar.gz
tar -zvxf redis-2.8.0.tar.gz
mv redis-2.8.0 python-redis-2.8.0
cd python-redis-2.8.0
python setup.py install
部署成功
G. Python 常用的标准库以及第三方库有哪些
我也来几个吧
standard libs:
itertools http://docs.python.org/2/library/itertools.html
functools http://docs.python.org/2/library/functools.html 学好python有必要掌握上面这两个库吧,
re 正则
subprocess http://docs.python.org/2/library/subprocess.html 调用shell命令的神器
pdb 调试
traceback 调试
pprint 漂亮的输出
logging 日志
threading和multiprocessing 多线程
urllib/urllib2/httplib http库,httplib底层一点,推荐第三方的库requests
os/sys 系统,环境相关
Queue 队列
pickle/cPickle 序列化工具
hashlib md5, sha等hash算法
cvs
json/simplejson python的json库,据so上的讨论和benchmark,simplejson的性能要高于json
timeit 计算代码运行的时间等等
cProfile python性能测量模块
glob 类似与listfile,可以用来查找文件
atexit 有一个注册函数,可用于正好在脚本退出运行前执行一些代码
dis python 反汇编,当对某条语句不理解原理时,可以用dis.dis 函数来查看代码对应的python 解释器指令等等。
3th libs:
paramiko https://github.com/paramiko/paramiko ssh python 库
selenium https://pypi.python.org/pypi/selenium 浏览器自动化测试工具selenium的python 接口
lxml http://lxml.de/ python 解析html,xml 的神器
mechanize https://pypi.python.org/pypi/mechanize/ Stateful programmatic web browsing
pycurl https://pypi.python.org/pypi/pycurl cURL library mole for Python
Fabric http://docs.fabfile.org/en/1.8/
Fabric is a Python (2.5 or higher) library and command-line tool for
streamlining the use of SSH for application deployment or systems
administration tasks.
xmltodict https://github.com/martinblech/xmltodict xml 转 dict,真心好用
urllib3 和 requests: 当然其实requests就够了 Requests: HTTP for Humans
flask http://flask.pocoo.org/python web 微框架
ipdb 调试神器,同时推荐ipython!结合ipython使用
redis redis python接口
pymongo mongodbpython接口
PIL http://www.pythonware.com/procts/pil/ python图像处理
mako http://www.makotemplates.org/ python模版引擎
numpy , scipy 科学计算
matplotlib 画图
scrapy 爬虫
django/tornado/web.py/web2py/uliweb/flask/twisted/bottle/cherrypy.等等 python web框架/服务器
sh 1.08 — sh v1.08 documentation 用来运行shell 模块的 极佳选择
暂时记得这么多吧,不过都是我自己常用的库 :) 。。欢迎补充
UPDATE:
A curated list of awesome Python frameworks, libraries and software.
vinta/awesome-python · GitHub
几乎所有很赞的 python 库,和框架都在这个列表里。
其他的 awesome list:
bayandin/awesome-awesomeness · GitHub
H. 如何高效地向Redis写入大量的数据
具体实现步骤如下:
1. 新建一个文本文件,包含redis命令
SET Key0 Value0
SET Key1 Value1
...
SET KeyN ValueN
如果有了原始数据,其实构造这个文件并不难,譬如shell,python都可以
2. 将这些命令转化成Redis Protocol。
因为Redis管道功能支持的是Redis Protocol,而不是直接的Redis命令。
如何转化,可参考后面的脚本。
3. 利用管道插入
cat data.txt | redis-cli --pipe
Shell VS Redis pipe
下面通过测试来具体看看Shell批量导入和Redis pipe之间的效率。
测试思路:分别通过shell脚本和Redis pipe向数据库中插入10万相同数据,查看各自所花费的时间。
Shell
脚本如下:
#!/bin/bash
for ((i=0;i<100000;i++))
do
echo -en "helloworld" | redis-cli -x set name$i >>redis.log
done
每次插入的值都是helloworld,但键不同,name0,name1...name99999。
Redis pipe
Redis pipe会稍微麻烦一点
1> 首先构造redis命令的文本文件
在这里,我选用了python
#!/usr/bin/python
for i in range(100000):
print 'set name'+str(i),'helloworld'
# python 1.py > redis_commands.txt
# head -2 redis_commands.txt
set name0 helloworld
set name1 helloworld
2> 将这些命令转化成Redis Protocol
在这里,我利用了github上一个shell脚本,
#!/bin/bash
while read CMD; do
# each command begins with *{number arguments in command}\r\n
XS=($CMD); printf "*${#XS[@]}\r\n"
# for each argument, we append ${length}\r\n{argument}\r\n
for X in $CMD; do printf "\$${#X}\r\n$X\r\n"; done
done < redis_commands.txt
# sh 20.sh > redis_data.txt
# head -7 redis_data.txt
*3
$3
set
$5
name0
$10
helloworld
至此,数据构造完毕。
测试结果
I. python redis连接 线程安全么
在ConnectionPool之前,如果需要连接redis,我都是用StrictRedis这个类,在源码中可以看到这个类的具体解释:
redis.StrictRedis Implementation of the Redis protocol.This abstract class provides a Python interface to all Redis commands and an
implementation of the Redis protocol.Connection and Pipeline derive from this, implementing how the commands are sent and received to the Redis server
使用的方法:
?
1
2
r=redis.StrictRedis(host=xxxx, port=xxxx, db=xxxx)
r.xxxx()
有了ConnectionPool这个类之后,可以使用如下方法
?
1
2
pool = redis.ConnectionPool(host=xxx, port=xxx, db=xxxx)
r = redis.Redis(connection_pool=pool)
这里Redis是StrictRedis的子类
简单分析如下:
在StrictRedis类的__init__方法中,可以初始化connection_pool这个参数,其对应的是一个ConnectionPool的对象:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class StrictRedis(object):
........
def __init__(self, host='localhost', port=6379,
db=0, password=None, socket_timeout=None,
socket_connect_timeout=None,
socket_keepalive=None, socket_keepalive_options=None,
connection_pool=None, unix_socket_path=None,
encoding='utf-8', encoding_errors='strict',
charset=None, errors=None,
decode_responses=False, retry_on_timeout=False,
ssl=False, ssl_keyfile=None, ssl_certfile=None,
ssl_cert_reqs=None, ssl_ca_certs=None):
if not connection_pool:
..........
connection_pool = ConnectionPool(**kwargs)
self.connection_pool = connection_pool
在StrictRedis的实例执行具体的命令时会调用execute_command方法,这里可以看到具体实现是从连接池中获取一个具体的连接,然后执行命令,完成后释放连接:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# COMMAND EXECUTION AND PROTOCOL PARSING
def execute_command(self, *args, **options):
"Execute a command and return a parsed response"
pool = self.connection_pool
command_name = args[0]
connection = pool.get_connection(command_name, **options) #调用ConnectionPool.get_connection方法获取一个连接
try:
connection.send_command(*args) #命令执行,这里为Connection.send_command
return self.parse_response(connection, command_name, **options)
except (ConnectionError, TimeoutError) as e:
connection.disconnect()
if not connection.retry_on_timeout and isinstance(e, TimeoutError):
raise
connection.send_command(*args)
return self.parse_response(connection, command_name, **options)
finally:
pool.release(connection) #调用ConnectionPool.release释放连接
在来看看ConnectionPool类:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class ConnectionPool(object):
...........
def __init__(self, connection_class=Connection, max_connections=None,
**connection_kwargs): #类初始化时调用构造函数
max_connections = max_connections or 2 ** 31
if not isinstance(max_connections, (int, long)) or max_connections < 0: #判断输入的max_connections是否合法
raise ValueError('"max_connections" must be a positive integer')
self.connection_class = connection_class #设置对应的参数
self.connection_kwargs = connection_kwargs
self.max_connections = max_connections
self.reset() #初始化ConnectionPool 时的reset操作
def reset(self):
self.pid = os.getpid()
self._created_connections = 0 #已经创建的连接的计数器
self._available_connections = [] #声明一个空的数组,用来存放可用的连接
self._in_use_connections = set() #声明一个空的集合,用来存放已经在用的连接
self._check_lock = threading.Lock()
.......
def get_connection(self, command_name, *keys, **options): #在连接池中获取连接的方法
"Get a connection from the pool"
self._checkpid()
try:
connection = self._available_connections.pop() #获取并删除代表连接的元素,在第一次获取connectiong时,因为_available_connections是一个空的数组,
会直接调用make_connection方法
except IndexError:
connection = self.make_connection()
self._in_use_connections.add(connection) #向代表正在使用的连接的集合中添加元素
return connection
def make_connection(self): #在_available_connections数组为空时获取连接调用的方法
"Create a new connection"
if self._created_connections >= self.max_connections: #判断创建的连接是否已经达到最大限制,max_connections可以通过参数初始化
raise ConnectionError("Too many connections")
self._created_connections += 1 #把代表已经创建的连接的数值+1
return self.connection_class(**self.connection_kwargs) #返回有效的连接,默认为Connection(**self.connection_kwargs)
def release(self, connection): #释放连接,链接并没有断开,只是存在链接池中
"Releases the connection back to the pool"
self._checkpid()
if connection.pid != self.pid:
return
self._in_use_connections.remove(connection) #从集合中删除元素
self._available_connections.append(connection) #并添加到_available_connections 的数组中
def disconnect(self): #断开所有连接池中的链接
"Disconnects all connections in the pool"
all_conns = chain(self._available_connections,
self._in_use_connections)
for connection in all_conns:
connection.disconnect()
execute_command最终调用的是Connection.send_command方法,关闭链接为 Connection.disconnect方法,而Connection类的实现:
?
1
2
3
4
5
6
7
class Connection(object):
"Manages TCP communication to and from a Redis server"
def __del__(self): #对象删除时的操作,调用disconnect释放连接
try:
self.disconnect()
except Exception:
pass
核心的链接建立方法是通过socket模块实现:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def _connect(self):
err = None
for res in socket.getaddrinfo(self.host, self.port, 0,
socket.SOCK_STREAM):
family, socktype, proto, canonname, socket_address = res
sock = None
try:
sock = socket.socket(family, socktype, proto)
# TCP_NODELAY
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
# TCP_KEEPALIVE
if self.socket_keepalive: #构造函数中默认 socket_keepalive=False,因此这里默认为短连接
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
for k, v in iteritems(self.socket_keepalive_options):
sock.setsockopt(socket.SOL_TCP, k, v)
# set the socket_connect_timeout before we connect
sock.settimeout(self.socket_connect_timeout) #构造函数中默认socket_connect_timeout=None,即连接为blocking的模式
# connect
sock.connect(socket_address)
# set the socket_timeout now that we're connected
sock.settimeout(self.socket_timeout) #构造函数中默认socket_timeout=None
return sock
except socket.error as _:
err = _
if sock is not None:
sock.close()
.....
关闭链接的方法:
?
1
2
3
4
5
6
7
8
9
10
11
def disconnect(self):
"Disconnects from the Redis server"
self._parser.on_disconnect()
if self._sock is None:
return
try:
self._sock.shutdown(socket.SHUT_RDWR) #先shutdown再close
self._sock.close()
except socket.error:
pass
self._sock = None
可以小结如下
1)默认情况下每创建一个Redis实例都会构造出一个ConnectionPool实例,每一次访问redis都会从这个连接池得到一个连接,操作完成后会把该连接放回连接池(连接并没有释放),可以构造一个统一的ConnectionPool,在创建Redis实例时,可以将该ConnectionPool传入,那么后续的操作会从给定的ConnectionPool获得连接,不会再重复创建ConnectionPool。
2)默认情况下没有设置keepalive和timeout,建立的连接是blocking模式的短连接。
3)不考虑底层tcp的情况下,连接池中的连接会在ConnectionPool.disconnect中统一销毁。