Skip to content

Commit 3e26f83

Browse files
committed
add function: make_sharding
1 parent b95a9c0 commit 3e26f83

File tree

4 files changed

+200
-0
lines changed

4 files changed

+200
-0
lines changed

mysqlutil/README.md

+60
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- [mysqlutil.make_insert_sql](#mysqlutilmake_insert_sql)
1818
- [mysqlutil.make_range_mysqldump_cmd](#mysqlutilmake_range_mysqldump_cmd)
1919
- [mysqlutil.make_select_sql](#mysqlutilmake_select_sql)
20+
- [mysqlutil.make_sharding](#mysqlutilmake_sharding)
2021
- [mysqlutil.make_sql_range_conditions](#mysqlutilmake_sql_range_conditions)
2122
- [mysqlutil.make_update_sql](#mysqlutilmake_update_sql)
2223
- [mysqlutil.scan_index](#mysqlutilscan_index)
@@ -423,6 +424,65 @@ make_select_sql('errlog', ['_id', 'key'], ('key', 'val'), ('a', 'b'),
423424
a string which is a sql select statement.
424425
425426
427+
## mysqlutil.make_sharding
428+
429+
**syntax**:
430+
`mysqlutil.make_sharding(conf)`
431+
432+
Scan a database table and generate sharding info according configurations in `conf`.
433+
Return sharding result as a dictionary like:
434+
```
435+
{
436+
"shard": [(), (), ...],
437+
"number": [number, number, ...],
438+
"total": number,
439+
}
440+
```
441+
442+
**argument**:
443+
444+
- `db`: which database to sharding. A string.
445+
- `table`: which table to sharding. A string.
446+
- `conn`: database connect info:
447+
448+
```
449+
{
450+
'host': '127.0.0.1',
451+
'port': 3306,
452+
'user': 'mysql',
453+
'passwd': 'password',
454+
}
455+
```
456+
457+
- `shard_fields`: are index fields to sharding by, a list or tuple of strings.
458+
- `start`: is the start condition to scan table, a list or tuple of strings.
459+
- `number_per_shard`: specifies the number of rows a shard contains, an integer.
460+
- `tolerance_of_shard`: the tolerance of one shard's capacity, an integer.
461+
- `shard_maker`: a function which accepts one list of strings argument and return a value as
462+
a "shard" in the result. For example:
463+
464+
```
465+
def shard_maker(shard):
466+
new_shard = shard + ['']*3
467+
return new_shard[:3]
468+
```
469+
By default, it is `list`.
470+
471+
**return**:
472+
a dictionary of sharding result, like:
473+
```
474+
{
475+
"shard": [['10000', 'a', '1'], ['11000', 'b', '3'], ['12000', 'd', '9']],
476+
"number": [100, 104, 80],
477+
"total": 284,
478+
}
479+
```
480+
481+
- `shard`: sharding info, a list of first row of every shard.
482+
- `number`: numbers of rows of every shard.
483+
- `total`: number of rows of all shards.
484+
485+
426486
## mysqlutil.make_sql_range_conditions
427487
428488
**syntax**:

mysqlutil/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
make_insert_sql,
1212
make_range_mysqldump_cmd,
1313
make_select_sql,
14+
make_sharding,
1415
make_sql_range_conditions,
1516
make_update_sql,
1617
scan_index,
@@ -35,6 +36,7 @@
3536
"make_insert_sql",
3637
"make_range_mysqldump_cmd",
3738
"make_select_sql",
39+
"make_sharding",
3840
"make_sql_range_conditions",
3941
"make_update_sql",
4042
"privileges",

mysqlutil/mysqlutil.py

+30
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from pykit import dictutil
1010
from pykit import mysqlconnpool
11+
from pykit import strutil
1112

1213

1314
class ConnectionTypeError(Exception):
@@ -375,3 +376,32 @@ def make_where_clause(index, index_values, operator='='):
375376
where_clause = ''
376377

377378
return where_clause
379+
380+
381+
def make_sharding(db, table, conn, shard_fields, start, number_per_shard, tolerance_of_shard,
382+
shard_maker=list):
383+
384+
scan_args = [(db, table), shard_fields, shard_fields, start]
385+
scan_kwargs = {"left_open": False, "use_dict": False, "retry": 3}
386+
387+
connpool = mysqlconnpool.make(conn)
388+
389+
record_iter = scan_index(connpool, *scan_args, **scan_kwargs)
390+
391+
shards = strutil.sharding(
392+
record_iter, number_per_shard, accuracy=tolerance_of_shard, joiner=list)
393+
394+
_, count = shards[0]
395+
result = {
396+
"shard": [shard_maker(start)],
397+
"number": [count],
398+
"total": count,
399+
}
400+
401+
for shard, count in shards[1:]:
402+
403+
result['shard'].append(shard_maker(shard))
404+
result['number'].append(count)
405+
result['total'] += count
406+
407+
return result

mysqlutil/test/test_mysqlutil.py

+108
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,114 @@ def test_scan_index(self):
119119
except error as e:
120120
self.assertEqual(type(e), error)
121121

122+
def test_make_sharding(self):
123+
124+
db = mysql_test_db
125+
table = mysql_test_table
126+
conn = {
127+
'host': base.mysql_test_ip,
128+
'port': base.mysql_test_port,
129+
'user': mysql_test_user,
130+
'passwd': base.mysql_test_password,
131+
}
132+
133+
def shard_maker(shard):
134+
135+
new_shard = [str(x) for x in shard]
136+
new_shard += ['', '', '']
137+
138+
return tuple(new_shard[:3])
139+
140+
cases = (
141+
(
142+
{
143+
"shard_fields": ('service', 'ip', '_id'),
144+
"start": ['common0', '', ''],
145+
"number_per_shard": 10,
146+
"tolerance_of_shard": 1,
147+
"shard_maker": tuple,
148+
},
149+
{
150+
'total': 32,
151+
'number': [10, 10, 10, 2],
152+
'shard': [('common0', '', ''), ('common0', '127.0.0.3', 27L),
153+
('common2', '127.0.0.1'), ('common4', '127.0.0.1', 7L)],
154+
},
155+
),
156+
157+
(
158+
{
159+
"shard_fields": ('service', 'ip', '_id'),
160+
"start": ['common0', '127.0.0.3', '27'],
161+
"number_per_shard": 10,
162+
"tolerance_of_shard": 1,
163+
},
164+
{
165+
'total': 22,
166+
'number': [10, 10, 2],
167+
'shard': [['common0', '127.0.0.3', '27'],
168+
['common2', '127.0.0.1'], ['common4', '127.0.0.1', 7L]],
169+
},
170+
),
171+
172+
(
173+
{
174+
"shard_fields": ('service', 'ip', '_id'),
175+
"start": ['common0', '', ''],
176+
"number_per_shard": 10,
177+
"tolerance_of_shard": 1,
178+
"shard_maker": shard_maker,
179+
},
180+
{
181+
'total': 32,
182+
'number': [10, 10, 10, 2],
183+
'shard': [('common0', '', ''), ('common0', '127.0.0.3', '27'),
184+
('common2', '127.0.0.1', ''), ('common4', '127.0.0.1', '7')],
185+
},
186+
),
187+
188+
(
189+
{
190+
"shard_fields": ('service', 'ip', '_id'),
191+
"start": ['common0', '', ''],
192+
"number_per_shard": 15,
193+
"tolerance_of_shard": 2,
194+
"shard_maker": shard_maker,
195+
},
196+
{
197+
'total': 32,
198+
'number': [15, 15, 2],
199+
'shard': [('common0', '', ''), ('common1', '127.0.0.1', '31'), ('common4', '', '')],
200+
},
201+
),
202+
203+
(
204+
{
205+
"shard_fields": ('time', '_id'),
206+
"start": ['201706060600', '1'],
207+
"number_per_shard": 10,
208+
"tolerance_of_shard": 1,
209+
},
210+
{
211+
'total': 32,
212+
'number': [10, 10, 10, 2],
213+
'shard': [['201706060600', '1'], [201706060610L,], [201706060620L,],
214+
[201706060630L,]],
215+
},
216+
),
217+
)
218+
219+
for kwargs, expected in cases:
220+
221+
kwargs['db'] = db
222+
kwargs['table'] = table
223+
kwargs['conn'] = conn
224+
225+
dd('expected: ', expected)
226+
result = mysqlutil.make_sharding(**kwargs)
227+
dd('result : ', result)
228+
self.assertEqual(result, expected)
229+
122230

123231
class TestMysqlutil(unittest.TestCase):
124232

0 commit comments

Comments
 (0)